遇到错误时如何使批处理文件终止?

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 看起来不太适合解释功能:它似乎包含许多与问题完全无关的内容。跨度>

以上是关于遇到错误时如何使批处理文件终止?的主要内容,如果未能解决你的问题,请参考以下文章

如何使批处理文件自行删除?

双击时如何使Windows批处理文件暂停?

如果批处理脚本无法写入输出文件,如何忽略错误

SQL server 2008还原文件时遇到的问题?

如何等待进程终止以在批处理文件中执行另一个进程

批处理文件终止时终止任务