windows 从函数内部退出批处理脚本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3227796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 14:48:48  来源:igfitidea点击:

Exit batch script from inside a function

windowsfunctionbatch-fileexit

提问by Gui13

I have a problem with my batch file. It builds several programs automatically by doing something like this:

我的批处理文件有问题。它通过执行以下操作自动构建多个程序:

  • set some compilation flags
  • run 'gmake all'
  • call the "check error level" function and if errorlevel 1, exit
  • 设置一些编译标志
  • 运行'gmake all'
  • 调用“检查错误级别”函数,如果错误级别为 1,则退出

So it looks like this:

所以它看起来像这样:

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

There's 6 or 7 of these (and it might grow). So I made a function to check errorlevel instead of copy/pasting it at every step. The problem is this: the error checking is made through a function:

其中有 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

Now, when running it, the exit /B 1simply exits the function, but not the batch file.

现在,当运行它时,它exit /B 1只是退出函数,而不是批处理文件

Do you know how to exit the complete batch file without having to copy/paste my "if errorlevel 1.." at every step?

你知道如何退出完整的批处理文件而不必在每一步复制/粘贴我的“if errorlevel 1..”?

回答by jeb

For a good solution see the part improved version

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

You can stop a batch at any point, also inside of nested function calls.

您可以随时停止批处理,也可以在嵌套函数调用内停止。

You only need to create a syntax error, for example with an empty block (), to suppress the error message, it can be executed in a call, and the stderr of the call is redirected to nul.

你只需要创建一个语法错误,例如带有一个空块(),来抑制错误信息,它可以在调用中执行,并且调用的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 Improved version: Exit only the current batch, but not the caller batch

2017-04-09 改进版本:仅退出当前批次,不退出调用方批次

As @dbenham mentioned, there is a new technic for exception handling that can also be used for exiting only the current batch.

正如@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

回答by dbenham

Using a fatal syntax error, as jeb demonstrates, does kill all batch processing, but it also has a nasty side effect - environment changes after SETLOCAL are preserved, even though they are supposed to be discarded via implicit ENDLOCAL when batch processing ends. See my DosTips post SETLOCAL continues after batch termination!for more information.

使用致命的语法错误,正如 jeb 所演示的,确实会杀死所有批处理,但它也有一个令人讨厌的副作用 - 保留 SETLOCAL 之后的环境更改,即使它们应该在批处理结束时通过隐式 ENDLOCAL 被丢弃。请参阅我的 DosTips 帖子SETLOCAL 在批处理终止继续!想要查询更多的信息。

Based on information at Why does a non-interactive batch script think I've pressed control-C?, I have discovered a clean way to exit all batch scripting from within a CALLed routine or script, and all changes after SETLOCAL are properly discarded.

基于为什么非交互式批处理脚本认为我按下了 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

Here is sample output of running the above test.bat. You can see that the script never returned from the :ExitBatch call, and the test variable definition has been properly discarded once batch processing terminates.

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

C:\test>test.bat
test=AFTER sub SETLOCAL

C:\test>set test
Environment variable test not defined

C:\test>

The :ExitBatch routine can be put into its own ExitBatch.bat script and placed somewhere within your PATH such that it can be conveniently used by any batch script.

: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

Important Update:

重要更新:

It is now possible to implement robust exception handling with pure batch. Not only can you throw an exception that can terminate all batch processing from any CALL level, but you can also catch the exception at a higher level, excecute special exception handling cleanup code, and resume processing, or continue to exit via another throw! See Does Windows batch support exception handling?for more info.

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

回答by GrahamS

When you use exit /b Xto exit from the function it sets ERRORLEVELto the value of X. You can then use the ||conditional processing symbolto execute another command if ERRORLEVELis non-zero after the call.

当您使用exit /b X退出函数时,它会设置ERRORLEVEL为 的值X。然后,您可以使用||条件处理符号,如果要执行其他命令ERRORLEVEL后非零call

@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

Output from this script is:

这个脚本的输出是:

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

回答by Maxim Suslov

I suggest following solution:

我建议以下解决方案:

@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

It saves error code from command which produce error. If you want to save error code to specific value, then modify the line:

它从产生错误的命令中保存错误代码。如果要将错误代码保存为特定值,请修改以下行:

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

I don't like solution with syntax error (see jeb's post), because it terminates also caller of current batch file also.

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

回答by HackingAddict1337

Exceeding stack limits will terminate all batch processing immediately:

超过堆栈限制将立即终止所有批处理:

@echo off

echo I am %0, calling :subroutine
call:subroutine

NEVER PARSED
exit /b


:subroutine
echo I am %0, calling :recurse
echo Exiting batch script IMMEDIATELY

cmd /c exit /b %=EXIT CODE HERE=%
2>nul call:recurse

NEVER PARSED
exit /b


:recurse - Aborts all batch processing, regardless of CALL depth
call:recurse

NEVER PARSED
exit /b