Windows 如何过滤cl.exe的输出,但保留成功/错误代码?

Windows 如何过滤cl.exe的输出,但保留成功/错误代码?,windows,batch-file,command-line-tool,Windows,Batch File,Command Line Tool,有关: 我想从编译器输出中过滤掉“.cpp”,但不过滤掉任何编译器错误。我可以这样做: cl-nologo source.cpp | findstr/V/X“source.cpp” 但这有两个问题: 如果编译器的唯一输出行是文件名,findstr返回一个失败代码 cl.exe调用中的errorlevel丢失 我可以不费吹灰之力解决第一个问题,但我不知道如何在不使用临时文件的情况下解决第二个问题(我希望避免!)。在这种情况下使用临时文件可能是避免代码过于复杂的更好选择 但是,如果您确实需要避免使用

有关:

我想从编译器输出中过滤掉“.cpp”,但不过滤掉任何编译器错误。我可以这样做:

cl-nologo source.cpp | findstr/V/X“source.cpp”

但这有两个问题:

  • 如果编译器的唯一输出行是文件名,
    findstr
    返回一个失败代码
  • cl.exe
    调用中的errorlevel丢失
    我可以不费吹灰之力解决第一个问题,但我不知道如何在不使用临时文件的情况下解决第二个问题(我希望避免!)。

    在这种情况下使用临时文件可能是避免代码过于复杂的更好选择

    但是,如果您确实需要避免使用临时文件,那么您可以在管道中获取cl的
    errorlevel
    ,但为了捕获值并在主代码中使用它,您需要使用
    FOR/F
    ,以便捕获findstr的输出和cl的
    errorlevel

    在管道中获取
    errorlevel
    值本身就非常复杂,因为管道的子cmd将在调用
    cl
    之前展开
    %errorlevel%
    ,并且当管道的子cmd嵌套在
    FOR/F
    的子cmd中时会变得更加复杂

    @echo off
    setlocal DisableDelayedExpansion
    
    set "errorlevel="
    set "_errorlevel=errorlevel"
    set "__errorlevel=_errorlevel"
    for /F "delims=" %%A in ('
        ^( cl -nologo source.cpp ^& call echo ###cl_errorlevel:%%%%%%__errorlevel%%%%%% ^) ^| findstr /V /X "source.cpp"
        ') do (
    
        set "cl_out=%%A"
        setlocal EnableDelayedExpansion
        if "!cl_out:~0,17!"=="###cl_errorlevel:" (
            endlocal
    
            REM delims is set to <colon> and <space>
            REM <space> is used to remove trailing the space(s) in echo's output which is unavoidable
            REM Alternatively <nul set /p "=Output_String" can be used to eliminate the need to use <space> as delimeter
            for /F "tokens=2 delims=: " %%B in ("%%A") do set "cl_errorlevel=%%B"
        ) else (
            endlocal
            REM cl's output, filtered by findstr
            echo(%%A
        )
    )
    echo,
    echo cl_errorlevel:%cl_errorlevel%
    pause
    
    cl
    输出的开头会有一个额外的空行,我不认为这是不需要的,但若确实需要删除它,那个么可以使用
    if
    语句来完成,如下所示:

    if defined line (
        setlocal EnableDelayedExpansion
        echo(!line!
        endlocal
    )
    

    在这种情况下,使用临时文件可能是避免代码过于复杂的更好选择

    但是,如果您确实需要避免使用临时文件,那么您可以在管道中获取cl的
    errorlevel
    ,但为了捕获值并在主代码中使用它,您需要使用
    FOR/F
    ,以便捕获findstr的输出和cl的
    errorlevel

    在管道中获取
    errorlevel
    值本身就非常复杂,因为管道的子cmd将在调用
    cl
    之前展开
    %errorlevel%
    ,并且当管道的子cmd嵌套在
    FOR/F
    的子cmd中时会变得更加复杂

    @echo off
    setlocal DisableDelayedExpansion
    
    set "errorlevel="
    set "_errorlevel=errorlevel"
    set "__errorlevel=_errorlevel"
    for /F "delims=" %%A in ('
        ^( cl -nologo source.cpp ^& call echo ###cl_errorlevel:%%%%%%__errorlevel%%%%%% ^) ^| findstr /V /X "source.cpp"
        ') do (
    
        set "cl_out=%%A"
        setlocal EnableDelayedExpansion
        if "!cl_out:~0,17!"=="###cl_errorlevel:" (
            endlocal
    
            REM delims is set to <colon> and <space>
            REM <space> is used to remove trailing the space(s) in echo's output which is unavoidable
            REM Alternatively <nul set /p "=Output_String" can be used to eliminate the need to use <space> as delimeter
            for /F "tokens=2 delims=: " %%B in ("%%A") do set "cl_errorlevel=%%B"
        ) else (
            endlocal
            REM cl's output, filtered by findstr
            echo(%%A
        )
    )
    echo,
    echo cl_errorlevel:%cl_errorlevel%
    pause
    
    cl
    输出的开头会有一个额外的空行,我不认为这是不需要的,但若确实需要删除它,那个么可以使用
    if
    语句来完成,如下所示:

    if defined line (
        setlocal EnableDelayedExpansion
        echo(!line!
        endlocal
    )
    

    这令人印象深刻。实际上,为了避免编写临时文件,这有点过于复杂了,但是谢谢!美好的获取
    ErrorLevel
    可能会更简单一些:
    对于/F“delims=“%%a in(^(cl-nologo source.cpp^^^^和call echo##cl#u ErrorLevel:%%^^^^^ ErrorLevel%%^ findstr/V/X“source.cpp”do(…)
    。另外,您不需要
    ####cl#errorlevel
    前缀,因为您知道
    errorlevel
    是返回的最后一行…@aschipfl,非常感谢您花时间查看我的答案。如果可以保证
    ^^^ErrorLevel
    ^ErrorLevel
    都是未定义的(不管怎么说,大多数情况下都是如此),那么这确实更简单。(我认为没有必要对符号进行额外的转义(
    ^^^&
    ),它将不会被解析为带括号的管道块)。关于
    ####cl#errorlevel
    errorlevel
    在最后一行返回的事实,我重复了
    %cl#u errorlevel%
    只是为了演示结果,但我不知道如何确定我是否在没有在屏幕上打印的情况下阅读最后一行。@aschipfl,我已经根据您的评论更新了答案。谢谢大家!@马特·钱伯斯,你可能想检查一下最新的答案。这太令人印象深刻了。实际上,为了避免编写临时文件,这有点过于复杂了,但是谢谢!美好的获取
    ErrorLevel
    可能会更简单一些:
    对于/F“delims=“%%a in(^(cl-nologo source.cpp^^^^和call echo##cl#u ErrorLevel:%%^^^^^ ErrorLevel%%^ findstr/V/X“source.cpp”do(…)
    。另外,您不需要
    ####cl#errorlevel
    前缀,因为您知道
    errorlevel
    是返回的最后一行…@aschipfl,非常感谢您花时间查看我的答案。如果可以保证
    ^^^ErrorLevel
    ^ErrorLevel
    都是未定义的(不管怎么说,大多数情况下都是如此),那么这确实更简单。(我认为没有必要对符号进行额外的转义(
    ^^^&
    ),它将不会被解析为带括号的管道块)。关于
    ####cl#errorlevel
    errorlevel
    在最后一行返回的事实,我重复了
    %cl#u errorlevel%
    只是为了演示结果,但我不知道如何确定我是否在没有在屏幕上打印的情况下阅读最后一行。@aschipfl,我已经根据您的评论更新了答案。谢谢大家!@Matt Chambers,您可能需要检查更新的答案。