21

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 0

Output:

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 0

The double-pipe statement of:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

is 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 1

This 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

23

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 file
  • EXIT /B [errorLevel] to exit the current subroutine
  • GOTO :EOF to exit the current subroutine
  • +1 for actually mentioning GOTO :EOF – afrazier Dec 7 '11 at 22:06
  • 1
    Very nice. I've made a small modification, assign %~0 to the variable instead of true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). That way you can use the same trick in multiple batch scripts that call each other. – GolezTrol Jul 27 '15 at 20:41
  • This is a great solution. Do you think it's worth an edit to explain how it works? It took me a minute to unpack all that and realize it is actually just calling the batch file (%~0) with all the arguments (%*) from a nested cmd.exe, and the /s is used to control the way the %ComSpec% argument handles the double quotes around the call. – Sean Sep 19 '17 at 14:07
  • @Sean I find brevity is more useful for most people. More docs hadn't been asked for in the 7 years since I wrote it, so it doesn't seem to be in high demand. I also think there is some value to people looking things up themselves and not duping/fragmenting docs. But maybe if a few more people ask I could add something. It's also a CW so you could propose an edit too – Merlyn Morgan-Graham Sep 19 '17 at 17:39
3

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 0

Output:

Quitting...

Technically this doesn't exit from within the subroutine. Rather, it simply checks the result of the subroutine and takes action from there.

| improve this answer | |
  • 2
    Thanks, that would certainly get the job done, and if I can't find a better answer that's what I'll have to do. However, I'd rather not have to paste that line after every CALL in my long and complex batch file. – Brown Dec 8 '09 at 18:47
1

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
| improve this answer | |
  • The point of the question is how to do it from subroutines (i.e. using call) so this doesn't answer it. – Steve Crane Nov 19 '13 at 10:15
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)
| improve this answer | |
1

This 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 /b

Or, 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 /b

the output will be:

1
2
3
4
5
This will be printed only once

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.