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

时间:2010-07-12 10:50:52

标签: windows function batch-file exit

我的批处理文件有问题。 它通过这样的方式自动构建几个程序:

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

所以它看起来像这样:

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

其中有6或7个(它可能会增长)。所以我创建了一个函数来检查errorlevel而不是在每一步复制/粘贴它。 问题是:错误检查是通过函数进行的:

: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只退出该功能,但不退出批处理文件

您是否知道如何退出整个批处理文件而无需在每一步复制/粘贴“if errorlevel 1 ..”?

5 个答案:

答案 0 :(得分:22)

有关一个好的解决方案,请参阅部件改进版

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

您只需要创建一个语法错误,例如使用空块()来抑制错误消息,可以在调用中执行,并将调用的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

答案 1 :(得分:19)

使用致命语法错误,正如jeb演示的那样,确实会杀死所有批处理,但它也有一个令人讨厌的副作用 - 保留SETLOCAL后环境会发生变化,即使它们应该在批处理结束时通过隐式ENDLOCAL被丢弃。有关详细信息,请参阅我的DosTips帖子SETLOCAL continues after batch termination!

基于Why does a non-interactive batch script think I've pressed 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级别终止所有批处理的异常,而且还可以在更高级别捕获异常,执行特殊异常处理清理代码,恢复处理,或继续通过另一个抛出退出!有关详细信息,请参阅Does Windows batch support exception handling?

答案 2 :(得分:2)

当您使用exit /b X退出该功能时,它会将ERRORLEVEL设置为X的值。然后,如果||之后ERRORLEVEL为非零,您可以使用call conditional processing symbol执行其他命令。

@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.

答案 3 :(得分:1)

我建议遵循以下解决方案:

@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's post),因为它也会终止当前批处理文件的调用者。

答案 4 :(得分:0)

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

@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