从函数内退出批处理脚本

我的batch file有问题。 它通过做这样的事情自动build立几个程序:

  • 设置一些编译标志
  • 运行“gmake all”
  • 调用“检查错误级别”function,如果错误级别1,退出

所以看起来像这样:

set FLAG=1 ... gmake all call :interactive_check set OTHERFLAG=1 ... gmake all call :interactive_check 

有6或7这些(它可能会增长)。 所以我做了一个函数来检查错误级别,而不是每一步复制/粘贴它。 问题是这样的:通过一个函数进行错误检查:

 :interactive_check if errorlevel 1 ( echo. echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ echo Error in compilation process... exiting echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ echo. cd %root_dir% exit /B 1 ) ELSE ( echo.Continuing to next step ) goto:eof 

现在,运行它时, exit /B 1只是退出函数, 而不是batch file

你知道如何退出完整的batch file,而不必复制/粘贴我的“如果errorlevel 1 ..”在每一步?

如jeb所示,使用一个致命的语法错误可以终止所有批处理,但是它也有一个令人讨厌的副作用 – 在SETLOCAL被保留之后环境发生变化,尽管在批处理结束时它们应该被隐式ENDLOCAL抛弃。 看到我的DosTips文章SETLOCAL批处理终止后继续! 了解更多信息。

根据为什么一个非交互式批处理脚本认为我已经按下control-C? ,我已经发现了一个干净的方式来退出CALLed例程或脚本中的所有批处理脚本,并且在SETLOCAL之后的所有更改都被正确丢弃。

 @echo off setlocal set test=AFTER main SETLOCAL call :sub echo returning from main NEVER REACHED exit /b :sub setlocal set test=AFTER sub SETLOCAL set test call :ExitBatch echo returning from sub2 NEVER REACHED exit /b :ExitBatch - Cleanly exit batch processing, regardless how many CALLs if not exist "%temp%\ExitBatchYes.txt" call :buildYes call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1 :CtrlC cmd /c exit -1073741510 :buildYes - Establish a Yes file for the language used by the OS pushd "%temp%" set "yes=" copy nul ExitBatchYes.txt >nul for /f "delims=(/ tokens=2" %%Y in ( '"copy /-y nul ExitBatchYes.txt <nul"' ) do if not defined yes set "yes=%%Y" echo %yes%>ExitBatchYes.txt popd exit /b 

这里是运行上面的test.bat的示例输出。 您可以看到脚本永远不会从:ExitBatch调用返回,并且一旦批处理终止,测试变量定义就会被正确丢弃。

 C:\test>test.bat test=AFTER sub SETLOCAL C:\test>set test Environment variable test not defined C:\test> 

ExitBatch例程可以放入自己的ExitBatch.bat脚本中,放置在PATH中的某个位置,以便任何批处理脚本都可以方便地使用它。

 @echo off :ExitBatch - Cleanly exit batch processing, regardless how many CALLs if not exist "%temp%\ExitBatchYes.txt" call :buildYes call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1 :CtrlC cmd /c exit -1073741510 :buildYes - Establish a Yes file for the language used by the OS pushd "%temp%" set "yes=" copy nul ExitBatchYes.txt >nul for /f "delims=(/ tokens=2" %%Y in ( '"copy /-y nul ExitBatchYes.txt <nul"' ) do if not defined yes set "yes=%%Y" echo %yes%>ExitBatchYes.txt popd exit /b 

重要更新:

现在可以使用纯批量实现强大的异常处理。 你不仅可以抛出一个异常,可以终止来自任何CALL级别的所有批处理,而且还可以在更高级别捕获异常,优先执行特殊的异常处理清除代码,并恢复处理,或者通过另一个抛出继续退出! 请参阅Windows批处理是否支持异常处理? 获取更多信息。

对于一个好的解决方案,请参阅部分改进版本

您可以在任何时候停止批处理,也可以在嵌套的函数调用中停止批处理。

你只需要创建一个语法错误,例如用一个空的block ()来抑制错误信息,它可以在一个调用中执行,并且调用的stderr被重定向到nul。

 @echo off setlocal enabledelayedexpansion rem Do something call :interactive_check rem Do something call :interactive_check goto :eof :::::::::::::::::::::::::: :interactive_check if errorlevel 1 ( echo. echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ echo Error in compilation process... exiting echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ call :halt 1 ) ELSE ( echo.Continuing to next step ) goto :eof :: Sets the errorlevel and stops the batch immediately :halt call :__SetErrorLevel %1 call :__ErrorExit 2> nul goto :eof :__ErrorExit rem Creates a syntax error, stops immediately () goto :eof :__SetErrorLevel exit /b %time:~-2% goto :eof 

2017-04-09改进版本:只退出当前批次,但不是调用者批次

正如@dbenham所说,异常处理有一个新的技术,也可以用于只退出当前批处理。

 @echo off echo Do something, detecting some fatal error call :ExitBatch 3 exit /b :ExitBatch [errorcode] - Exits only the current batch file, regardless how many CALLs set _errLevel=%1 REM *** Remove all calls from the current batch from the call stack :popStack ( (goto) 2>nul setlocal DisableDelayedExpansion call set "caller=%%~0" call set _caller=%%caller:~0,1%% call set _caller=%%_caller::=%% if not defined _caller ( REM callType = func rem set _errLevel=%_errLevel% goto :popStack ) (goto) 2>nul endlocal cmd /c "exit /b %_errLevel%" ) echo NEVER REACHED exit /b 

当使用exit /b X退出函数时,它将ERRORLEVEL设置为X的值。 然后你可以使用|| 如果ERRORLEVELcall后不为零,则执行另一个命令。

 @echo off setlocal call :myfunction PASS || goto :eof call :myfunction FAIL || goto :eof echo Execution never gets here goto :eof :myfunction if "%1"=="FAIL" ( echo myfunction: got a FAIL. Will exit. exit /b 1 ) echo myfunction: Everything is good. exit /b 0 

这个脚本的输出是:

 myfunction: Everything is good. myfunction: got a FAIL. Will exit. 

我建议以下解决方案:

 @echo off :: comment next line if you want to export any local variables in caller environment setlocal set FLAG=1 rem Do something call :interactive_check set FLAG2=2 rem Do something call :interactive_check goto :eof :interactive_check if errorlevel 1 ( echo. echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ echo Error in compilation process... exiting echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ (goto) 2>nul & endlocal & exit /b %ERRORLEVEL% ) else ( echo Continuing to next step ) goto :eof 

它保存错误代码从命令产生错误。 如果要将错误代码保存到特定值,请修改该行:

 (goto) 2>nul & endlocal & exit /b YOUR_EXITCODE_HERE 

我不喜欢语法错误的解决方案(见jeb的帖子 ),因为它也终止了当前批处理文件的调用者。