遇到错误时如何使批处理文件终止?
Posted
技术标签:
【中文标题】遇到错误时如何使批处理文件终止?【英文标题】:How do I make a batch file terminate upon encountering an error? 【发布时间】:2010-10-18 14:07:38 【问题描述】:我有一个批处理文件,它使用不同的参数一遍又一遍地调用相同的可执行文件。如果其中一个调用返回任何级别的错误代码,如何使其立即终止?
基本上,我想要相当于 MSBuild 的 ContinueOnError=false
。
【问题讨论】:
什么命令外壳将运行您的脚本? DOS/Win9x 的 command.com 还是 Win2k+ 的 cmd.exe?既然这会带来很大的不同,您能否在编辑您的问题时澄清这一点? 【参考方案1】:检查if
语句中的errorlevel
,然后检查exit /b
(仅退出batch 文件,而不是整个cmd.exe 进程)以获取除0 以外的值。
same-executable-over-and-over.exe /with different "parameters"
if %errorlevel% neq 0 exit /b %errorlevel%
如果您希望错误级别的值传播到批处理文件之外
if %errorlevel% neq 0 exit /b %errorlevel%
但如果这是在 for
内,它会变得有点棘手。你需要更多类似的东西:
setlocal enabledelayedexpansion
for %%f in (C:\Windows\*) do (
same-executable-over-and-over.exe /with different "parameters"
if !errorlevel! neq 0 exit /b !errorlevel!
)
编辑:您必须在每个命令后检查错误。 cmd.exe/command.com 批处理中没有全局“on error goto”类型的构造。我还根据CodeMonkey 更新了我的代码,尽管我在 XP 或 Vista 上的任何批量黑客攻击中从未遇到过负错误级别。
【讨论】:
有没有办法为整个文件声明一次? “On error goto”或类似的东西? +1 用于负错误级别检查。由于否定结果,脚本静默失败。 小心:启用的elayedexpansion 是关键的,也是if/else 或任何其他块所必需的 @system-PAUSE 显示的前两个“if”有什么区别吗? 延迟扩展启用/禁用或命令扩展(neq
需要)启用/禁用对使用if not errorlevel 1 exit /B
无关紧要,正如微软在支持文章Using command redirection operators 和运行帮助输出中所解释的那样if /?
在 cmd 窗口中。当前错误级别(退出代码)在使用exit /B
退出批处理文件时保留。注意:exit
带参数/B
需要启用命令扩展,请参阅Where does GOTO :EOF return to?【参考方案2】:
在每一行添加|| goto :label
,然后定义一个:label
。
例如,创建这个 .cmd 文件:
@echo off
echo Starting very complicated batch file...
ping -invalid-arg || goto :error
echo OH noes, this shouldn't have succeeded.
goto :EOF
:error
echo Failed with error #%errorlevel%.
exit /b %errorlevel%
另见question about exiting batch file subroutine。
【讨论】:
这是大多数 shell 脚本语言中非常常见的习语,读起来很好:“Do this, or this if it failed..” 我使用 SET 手动跟踪行号:command || (SET ErrorLine=102 && goto :error)
@MarcelValdezOrozco 在我看来,这就是 ||
最初创建的目的。也许不是特别 goto,而是 Fowl 提到的“尝试,出错时执行此操作”。我的问题是这是否适用于所有非零退出代码?只有正面?
@jpmc26 是的,确实如此,证明给自己看 - cmd /k exit -1 && echo success || echo fail
- 打印失败。
你甚至可以避免使用command || exit /b %errorlevel%
之类的标签【参考方案3】:
最短的:
command || exit /b
如果需要,可以设置退出码:
command || exit /b 666
你也可以登录:
command || echo ERROR && exit /b
【讨论】:
exit /b 是否会返回原来的失败退出代码? @FrankSchwieterman,是的,%ERRORLEVEL%
在你调用exit /b
的时候没有被触动,所以错误码被转发了【参考方案4】:
这是一个用于 BASH 和 Windows CMD 的 polyglot program,它运行一系列命令并在其中任何一个失败时退出:
#!/bin/bash 2> nul
:; set -o errexit
:; function goto() return $?;
command 1 || goto :error
command 2 || goto :error
command 3 || goto :error
:; exit 0
exit /b 0
:error
exit /b %errorlevel%
我过去曾将这种类型的东西用于多平台continuous integration 脚本。
【讨论】:
【参考方案5】:一个小更新,您应该将“if errorlevel 1”的检查更改为以下...
IF %ERRORLEVEL% NEQ 0
这是因为在 XP 上您可能会得到负数作为错误。 0 = 没有问题,其他都是问题。
请记住 DOS 处理“IF ERRORLEVEL”测试的方式。如果您要检查的数字是该数字或更高,它将返回 true,因此,如果您要查找特定的错误数字,则需要从 255 开始并向下处理。
【讨论】:
Windows 标准内部和外部命令都不会以负值退出。 Microsoft 警告任何程序编写者以负值退出,例如在有关 Environment.ExitCode Property 的 MSDN 文章中。事实上,必须始终找出应用程序在成功时使用了哪些退出代码,以及在各种错误中使用了哪些退出代码,有关用户期望的负面 MS 示例,请参阅 html Help Workshop returns error after successfully compiled .chm file。【参考方案6】:我更喜欢 OR 形式的命令,因为我发现它们最易读(如
反对在每个命令后加上 if)。然而,天真的方式
这样做,command || exit /b %ERRORLEVEL%
是错误。
这是因为第一次读取一行时批处理会扩展变量,而不是
比使用它们时。这意味着如果command
在该行
以上失败,批处理文件正确退出,但返回代码为 0 退出,
因为这就是 %ERRORLEVEL%
在开头的值
线。显然,这在我们的脚本中是不可取的,所以我们必须启用
delayed expansion,像这样:
SETLOCAL EnableDelayedExpansion
command-1 || exit /b !ERRORLEVEL!
command-2 || exit /b !ERRORLEVEL!
command-3 || exit /b !ERRORLEVEL!
command-4 || exit /b !ERRORLEVEL!
这个 sn-p 将执行命令 1-4,如果其中任何一个失败,它将 使用与失败命令相同的退出代码退出。
【讨论】:
您的代码是正确的,但我相信对于简单任务来说不必要的冗长:exit /b
不带参数将保留错误级别。您的示例可以简化为 command-1 || exit /b
- 更短且不再需要 EnableDelayedExpansion。但是,您的代码确实演示了 EnableDelayedExpansion 的有效使用,有人可能会在此基础上进行构建,例如:command-1 || (echo Error=!errorlevel! && exit /b)
。【参考方案7】:
我们不能总是依赖 ERRORLEVEL,因为很多时候外部程序或批处理脚本不返回退出代码。
在这种情况下,我们可以对这样的故障使用通用检查:
IF EXIST %outfile% (DEL /F %outfile%)
CALL some_script.bat -o %outfile%
IF NOT EXIST %outfile% (ECHO ERROR & EXIT /b)
如果程序向控制台输出了一些东西,我们也可以检查它。
some_program.exe 2>&1 | FIND "error message here" && (ECHO ERROR & EXIT /b)
some_program.exe 2>&1 | FIND "Done processing." || (ECHO ERROR & EXIT /b)
【讨论】:
【参考方案8】:无论我如何尝试,即使 msbuild 失败,errorlevel 始终保持为 0。所以我建立了我的解决方法:
构建项目并将日志保存到 Build.log
SET Build_Opt=/flp:summary;logfile=Build.log;append=true
msbuild "myproj.csproj" /t:rebuild /p:Configuration=release /fl %Build_Opt%
在构建日志中搜索“0 Error”字符串,将结果设置为var
FOR /F "tokens=* USEBACKQ" %%F IN (`find /c /i "0 Error" Build.log`) DO (
SET var=%%F
)
echo %var%
获取最后一个字符,表示搜索字符串包含多少行
set result=%var:~-1%
echo "%result%"
如果未找到字符串,则错误 > 0,构建失败
if "%result%"=="0" ( echo "build failed" )
该解决方案的灵感来自 Mechaflash 在How to set commands output as a variable in a batch file 的帖子
和https://ss64.com/nt/syntax-substring.html
【讨论】:
仅适用于计数小于 10。更好的;for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F
。注意:find "0 Error"
也会找到 10 Errors
。【参考方案9】:
@echo off
set startbuild=%TIME%
C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe c:\link.xml /flp1:logfile=c:\link\errors.log;errorsonly /flp2:logfile=c:\link\warnings.log;warningsonly || goto :error
copy c:\app_offline.htm "\\lawpccnweb01\d$\websites\OperationsLinkWeb\app_offline.htm"
del \\lawpccnweb01\d$\websites\OperationsLinkWeb\bin\ /Q
echo Start Copy: %TIME%
set copystart=%TIME%
xcopy C:\link\_PublishedWebsites\OperationsLink \\lawpccnweb01\d$\websites\OperationsLinkWeb\ /s /y /d
del \\lawpccnweb01\d$\websites\OperationsLinkWeb\app_offline.htm
echo Started Build: %startbuild%
echo Started Copy: %copystart%
echo Finished Copy: %TIME%
c:\link\warnings.log
:error
c:\link\errors.log
【讨论】:
请在您的答案中添加更多信息。只是一段代码没有多大帮助。 除了没有添加任何 cmets 之外,您的 sn-p 看起来不太适合解释功能:它似乎包含许多与问题完全无关的内容。跨度>以上是关于遇到错误时如何使批处理文件终止?的主要内容,如果未能解决你的问题,请参考以下文章