Exit batch script from inside a function

前端 未结 5 663
没有蜡笔的小新
没有蜡笔的小新 2020-12-02 23:57

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
相关标签:
5条回答
  • 2020-12-03 00:17

    When you use exit /b X to exit from the function it sets ERRORLEVEL to the value of X. You can then use the || conditional processing symbol to execute another command if ERRORLEVEL is non-zero after the 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.
    
    0 讨论(0)
  • 2020-12-03 00:21

    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
    
    0 讨论(0)
  • 2020-12-03 00:23

    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.

    @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

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

    @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
    
    0 讨论(0)
  • 2020-12-03 00:29

    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.

    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.

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

    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.

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

    0 讨论(0)
  • 2020-12-03 00:37

    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.

    0 讨论(0)
提交回复
热议问题