在 Windows 批处理文件中,我需要使用
处理命令的输出for /f %%a in ('aCommand') do (
...
)
但是,如果
aCommand
失败,我想用 exit /b 1
退出脚本。我试过了
for /f %%a in ('aCommand') do (
...
) || exit /b 1
但这没有用;无论如何,脚本都会继续执行。有线索吗?
至少有三种可能的解决方案。
第一个用于处理执行命令输出的多行并由 FOR 循环处理。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "OutputProcessed="
for /F %%I in ('aCommand 2^>nul') do (
set "OutputProcessed=1"
rem Other commands processing the string(s) assigned to the loop variable(s).
)
if not defined OutputProcessed exit /B 1
set "OutputProcessed="
rem Other commands to run by the batch file.
endlocal
首先确保环境变量
OutputProcessed
在运行for /F
之前不是偶然定义的,这会导致在后台启动cmd.exe
并使用选项/c
和作为附加参数附加的'
之间的命令行( s).
环境变量
OutputProcessed
是用一个字符串值定义的,如果在FOR循环内处理捕获的输出的任何行,这并不重要,这意味着由cmd.exe
执行的命令行在后台输出的内容是由 FOR 循环内的命令处理。
FOR循环下面的IF条件检查环境变量OutputProcessed
是否未定义,这意味着命令的执行没有处理任何有用的信息。在这种情况下,批处理文件处理将退出,并显示退出代码
1
,向父进程指示错误情况。如果将所执行命令的输出中的数据分配给至少一个环境变量,则此解决方案是最佳的,因为该环境变量可用于检查命令执行是否成功,而不是像示例中使用的那样使用单独的环境变量。
第二个解决方案与第一个解决方案非常相似,只是环境变量处理相反。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "NoOutputProcessed=1"
for /F %%I in ('aCommand 2^>nul') do (
set "NoOutputProcessed="
rem Other commands processing the string(s) assigned to the loop variable(s).
)
if defined NoOutputProcessed exit /B 1
rem Other commands to run by the batch file.
endlocal
此解决方案首先显式定义环境变量NoOutputProcessed
,其值无关紧要,并在从执行命令的输出处理的任何行上取消定义环境变量。如果在执行FOR
循环后仍然定义了环境变量
NoOutputProcessed
,则退出批处理文件处理,并且错误退出代码 1
返回到父进程。此解决方案适用于以下用例:使用
FOR 循环内的命令进一步处理已执行命令的多行输出,而不将数据分配给一个或多个环境变量。
第三种解决方案最好执行一个命令,其中只有一个输出行包含由批处理文件进一步处理的感兴趣的数据。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F %%I in ('aCommand 2^>nul') do (
rem Other commands processing the string(s) assigned to the loop variable(s).
goto HaveData
)
exit /B 1
:HaveData
rem Other commands to run by the batch file.
endlocal
在这种情况下,在从后台命令进程执行的命令的捕获输出中获取感兴趣的数据后,使用命令 GOTO 退出 FOR 循环的执行。使用标签下方的行继续批处理文件处理。否则,如果未从执行的命令中获取感兴趣的数据,则整个批处理文件处理将退出,并显示退出代码 1
。要了解所使用的命令及其工作原理,请打开
命令提示符窗口,执行以下命令,并完整、仔细地阅读每个命令显示的帮助页面。
echo /?
endlocal /?
exit /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
使用命令重定向运算符的 Microsoft 文档,了解 2>nul
的说明。当 Windows 命令解释器在执行命令FOR
之前处理此命令行时,重定向运算符
>
必须在FOR 命令行上用脱字符号
^
转义,以便被解释为文字字符,该命令使用单独的命令进程在后台启动。
STDOUT 处没有输出,STDERR 处有一些输出,退出代码或 ErrorLevel
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_CMDL=ver" & rem // (successful command line)
::set "_CMDL=set ^=" & rem // (alternative failing command line)
set "_TMPF=%TEMP%\%~n0_%RANDOM%.log" & rem // (temporary file)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
rem /* In case of an error, the command does not return anything at STDOUT:
rem hence `for /F` has got nothing to capture, in which case it sets the
rem exit code (but not the `ErrorLevel`!) to `1`; this can be detected
rem using the conditional execution operator `||`, given that the entire
rem `for /F` loop is put in between a pair of parentheses: */
(
rem // Remove `2^> nul` to also return a potential error message:
for /F delims^=^ eol^= %%K in ('%_CMDL% 2^> nul') do (
rem // Process an output line here...
echo(%%K
)
) || (
>&2 echo ERROR!& rem exit /B
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
rem /* In case of an error, the command returns something at STDERR:
rem so let us redirect STDOUT to a file for later usage and STDERR to STDOUT
rem to be captured by `for /F`; if the loop iterates STDERR was not empty: */
(
for /F delims^=^ eol^= %%K in ('%_CMDL% 2^>^&1 ^> "%_TMPF%"') do (
>&2 echo ERROR!& del "%_TMPF%" & rem exit /B 1
)
) || (
rem /* Now process the temporary file containing the original STDOUT data;
rem remember that `for /F` would anyway not iterate until the command has
rem finished its execution, hence there is no additional delay: */
for /F usebackq^ delims^=^ eol^= %%K in ("%_TMPF%") do (
rem // Process an output line here...
echo(%%K
)
del "%_TMPF%"
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
rem /* In case of an error, the command sets the exit code or `ErrorLevel`:
rem hence let us suppress a potential error message (unless `2^> nul` is
rem removed), redirect STDOUT to a file for later usage and just output `#`
rem to STDOUT in case the exit code is set (detected by the `||` operator);
rem `for /F` captures the `#` and iterates once in case it is present: */
(
for /F %%K in ('%_CMDL% 2^> nul ^> "%_TMPF%" ^|^| echo #') do (
>&2 echo ERROR!& del "%_TMPF%" & rem exit /B 1
)
) || (
rem // Now process the temporary file containing the original STDOUT data:
for /F usebackq^ delims^=^ eol^= %%K in ("%_TMPF%") do (
rem // Process an output line here...
echo(%%K
)
del "%_TMPF%"
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
endlocal
exit /B