Search code examples
windowsfunctionbatch-fileexit

Exit batch script from inside a function


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

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:

: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 1 simply exits the function, but not the batch file.

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


Solution

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