如何过滤 cl.exe 的输出但保留 success/error 代码?

How to filter output from cl.exe but preserve success/error code?

相关:Prevent cl.exe from printing the compiled source file

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

cl -nologo source.cpp | findstr /V /X "source.cpp"

但这有两个问题:

  1. 如果编译器输出的唯一行是文件名,findstr returns 一个失败代码。
  2. cl.exe 调用的错误级别丢失。

我可以毫不费力地解决第一个问题,但我不确定如何在不使用临时文件的情况下解决第二个问题(我想避免!)。

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

然而,如果你真的需要避免使用临时文件,那么你可以在管道中获取 cl 的 errorlevel 但随后要捕获该值并在你的主代码中使用它,你需要使用 FOR /F 为了捕获 findstr 的输出和 cl 的 errorlevel.

获取管道内的 errorlevel 值本身就够棘手了,因为管道的子 cmd 在调用 cl 之前会扩展 %errorlevel%,并且当管道的子命令嵌套在 FOR /F 的子命令中。

@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_errorlevel: 是因为它足够独特,可以与 cl 的其他输出区分开来。

findstr 中的 errorlevel 应该无关紧要,您可以根据 cl_errorlevel

采取行动


更新

我从评论中采纳了 aschipfl 的优秀建议,不使用 ###cl_errorlevel 前缀,并以更简单的方式重新实现了代码,这完全依赖于 errorlevel 来自clFOR /F

返回的最后一行
@echo off
setlocal DisableDelayedExpansion

set "errorlevel="
set "_errorlevel=errorlevel"
set "__errorlevel=_errorlevel"
set "line="
for /F "delims=" %%A in ('
    ^( cl -nologo source.cpp ^& call set /p "=%%%%%%__errorlevel%%%%%%"^<nul ^) ^| findstr /V /X "source.cpp"
    ') do (
    setlocal EnableDelayedExpansion
    echo(!line!
    endlocal
    set "line=%%A"  
)
set "cl_errorlevel=%line%"

echo,
echo cl_errorlevel:%cl_errorlevel%
pause

cl 输出的开头会有一个额外的空行,我不认为这是不需要的东西,但如果真的需要删除它,那么可以用这样的 IF 语句完成:

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