How can I exit a batch file from inside a subroutine?
If I use the EXIT command, I simply return to the line where I called the subroutine, and execution continues.
Here's an example:
@echo off
ECHO Quitting...
CALL :QUIT
ECHO Still here!
GOTO END
:QUIT
EXIT /B 1
:END
EXIT /B 0Output:
Quitting...
Still here!Update:
This isn't a proper answer, but I ended up doing something along the lines of:
@echo off
CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL
ECHO You shouldn't see this!
GOTO END
:SUBROUTINE_WITH_ERROR
ECHO Simulating failure...
EXIT /B 1
:HANDLE_FAIL
ECHO FAILURE!
EXIT /B 1
:END
ECHO NORMAL EXIT!
EXIT /B 0The double-pipe statement of:
CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAILis shorthand for:
CALL :SUBROUTINE_WITH_ERROR
IF ERRORLEVEL 1 GOTO HANDLE_FAIL I would still love to know if there's a way to exit directly from a subroutine rather than having to make the CALLER handle the situation, but this at least gets the job done.
Update #2: When calling a subroutine from within another subroutine, called in the manner above, I call from within subroutines thusly:
CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1This way, the error propagates back up to the "main", so to speak. The main part of the batch can then handle the error with the error handler GOTO :FAILURE
5 Answers
Add this to the top of your batch file:
@ECHO OFF
SETLOCAL
IF "%selfWrapped%"=="" ( REM this is necessary so that we can use "exit" to terminate the batch file, REM and all subroutines, but not the original cmd.exe SET selfWrapped=true %ComSpec% /s /c ""%~0" %*" GOTO :EOF
)Then you can simply call:
EXIT [errorLevel]if you want to exit the entire fileEXIT /B [errorLevel]to exit the current subroutineGOTO :EOFto exit the current subroutine
How about this one minor adjustment?
@echo off
ECHO Quitting...
CALL :QUIT
:: The QUIT subroutine might have set the error code so let's take a look.
IF ERRORLEVEL 1 GOTO :EOF
ECHO Still here!
GOTO END
:QUIT
EXIT /B 1
:END
EXIT /B 0Output:
Quitting...Technically this doesn't exit from within the subroutine. Rather, it simply checks the result of the subroutine and takes action from there.
2This will exit current context and a parent context (i.e., when executed inside a one call deep subroutine script will exit):
(goto) 2>nul || exit /bOr, if you need errorlevel 0:
(goto) 2>nul || ( type nul>nul exit /b
)Basically, (goto) 2>nul sets errorlevel to 1 (without outputting an error), returns execution to the parent context and code after double pipe is executed in parent context. type nul>nul sets errorlevel to 0.
UPD:
To return execution more than twice in a row, chain several (goto) 2>nul || like this:
(goto) 2>nul || (goto) 2>nul || (goto) 2>nul || ( type nul>nul exit /b
)Here's a recursive subroutine to return context a variable number of times:
:Kill
(goto) 2>nul || ( set /a depth=%1-1 if %1 GEQ 1 ( call:Kill !depth! ) (goto) 2>nul || (type nul>nul)
)When called from a recursive function:
@echo off
setlocal EnableDelayedExpansion
call:Recurs 5
echo This won't be printed
exit /b
:Recurs
set /a ri+=1
echo %ri%
if %ri% LSS %1 ( call:Recurs %1
)
echo This will be printed only once
call:Kill %1
exit /bthe output will be:
1
2
3
4
5
This will be printed only once If you do not want to come back from the procedure, don't use call: instead use goto.
@echo off
ECHO Quitting...
GOTO :QUIT
ECHO Will never be there!
GOTO END
:QUIT
EXIT /B 1
:END
EXIT /B 0 1 I put error handling in my batch files. You can call error handlers like this:
CALL :WARNING "This is" "an important" "warning."And here is the end of the batch file:
::-------------------------------------------------------------------
:: Decisions
::-------------------------------------------------------------------
:INFO
IF "_DEBUG"=="true" ( ECHO INFO: %~1 IF NOT "%~2"=="" ECHO %~2 IF NOT "%~3"=="" ECHO %~3
)
EXIT /B 0
:WARNING
ECHO WARNING: %~1
IF NOT "%~2"=="" ECHO %~2
IF NOT "%~3"=="" ECHO %~3
EXIT /B 0
:FAILURE
ECHO FAILURE: %~1
IF NOT "%~2"=="" ECHO %~2
IF NOT "%~3"=="" ECHO %~3
pause>nul
:END
ECHO Closing Server.bat script
FOR /l %%a in (5,-1,1) do (TITLE %TITLETEXT% -- closing in %%as&PING.exe -n 2 -w 1 127.0.0.1>nul)