【问题标题】: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?既然这会带来很大的不同,您能否在编辑您的问题时澄清这一点?

标签: batch-file


【解决方案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 开始并向下处理。

    【讨论】:

    【解决方案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 看起来不太适合解释功能:它似乎包含许多与问题完全无关的内容。跨度>
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-04
      • 1970-01-01
      • 2014-08-01
      • 2019-01-03
      • 1970-01-01
      • 2016-01-04
      相关资源
      最近更新 更多