for-loop header 命令失败时如何退出脚本?

问题描述 投票:0回答:2

在 Windows 批处理文件中,我需要使用

处理命令的输出
for /f %%a in ('aCommand') do (
    ...
)

但是,如果

aCommand
失败,我想用
exit /b 1
退出脚本。我试过了

for /f %%a in ('aCommand') do (
    ...
) || exit /b 1

但这没有用;无论如何,脚本都会继续执行。有线索吗?

batch-file error-handling
2个回答
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 命令行上用脱字符号 ^ 转义,以便被解释为文字字符,该命令使用单独的命令进程在后台启动。


0
投票
主要问题是如何定义执行命令的成功或失败。 以下代码详细说明了三个不同的定义 -

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
    
© www.soinside.com 2019 - 2024. All rights reserved.