Snow covered driveway

Batch File Exit Codes

While working on a Windows batch script earlier today, I ran across an interesting side effect of the call and exit commands. Let’s take this simple example, which we’ll name script_a.bat:

@echo off
SETLOCAL

call :function
cd %SOME_PATH%

goto :functionEnd
:function
    set foobar=1
    if "%foobar%" == "1" exit /b 1
    goto :EOF
:functionEnd

Unlike Bash, Windows batch files have no function capabilities. Clever hacks like the above can be used to fake out functions, but these hacks hide some subtle quirks. You see that exit call within the ‘function’? It only gets called if the %foobar% variable is equal to 1 (which is always the case, in our example). Also note that we exit with an error code of 1. So, in short, this script should always return an exit code of 1. Now, let’s create another batch script which we’ll name script_b.bat:

@echo off

call script_a.bat
echo Exit Code = %ERRORLEVEL%

This second script is very simple. All we do is call script_a.bat, and then print its resulting return code. What do you expect the return code to be? One would expect it to be 1, but it’s not! Our second script will actually print out Exit Code = 0. Why is this?

The answer lies in the call command. Again, unlike Bash scripts, stand-alone batch files do not create their own context when executed. But if you use the call command, the thing you call does get its own context. How weird is that? So, let’s trace the first script we wrote to figure out where the error code gets changed.

After some initial setup, we call our function (call :function). Inside our function, we create a variable, initialize it to 1, then test to see if the value is 1. Since the value is indeed 1, the if test succeeds, and the exit command is called. But we don’t exit the script; instead, we exit the context that was created when we called our function. Note that immediately after we call our function, we perform a cd operation. This line of code gets executed, succeeds, and sets the %ERRORLEVEL% global to 0.

In order to exit properly, we have to exit our initial script twice, like this:

@echo off
SETLOCAL

call :function
if "%ERRORLEVEL%" == "1" exit /b 1

cd %SOME_PATH%

goto :functionEnd
:function
    set foobar=1
    if "%foobar%" == "1" exit /b 1
    goto :EOF
:functionEnd

See the new exit call after our initial function call? Then, and only then, will our second script print out what we expected. This subtle bug stymied me for several hours today; hopefully this short post will help someone else avoid this frustration.

8 Responses

  1. Batch files are so crippled because they have to backwards compatible all the way back to when they were introduced in DOS 1.0, which was like 1980. That makes it very hard to introduce new features.

    I found this workaround recently for getting behavior like UNIX backquotes:

    REM similar to: set HOSTNAME=`hostname`
    for /f %%i in ('hostname') do set HOSTNAME=%%i

  2. Doing exit /B x from a ‘function’ is fine. Doing this will set the error level.

    There is a new full featured debugger for batch files. It runs in a Visual-Studio like environment. The product name is ‘Running Steps’, and it can be found in http://www.steppingsoftware.com. I find it very useful.

  3. Hi Jonah,

    I’ve the same problem you had. I tried your example above but unfortunately it doesn’t work for me. After calling script_a.bat I allways get ERRORLEVEL=0.

    I’m using WinXP SP2. Do you have any idea?

    greetz
    modbom

  4. If you are using the first instance of script_a (the first code block), the errorlevel will always be 0 (because the final cd call always succeeds, assuming %SOME_PATH% exists).

    The second form of script_a (the third code block) will produce an errorlevel of 1. I just tried it on my Windows XP SP-2 box here, and it worked just fine.

  5. Mmh…

    I tried the second one. I tried also a batch script foo.bat only containing:

    @echo off
    exit /b 1
    

    After modifying script_b.bat to:

    @echo off
    
    call foo.bat
    echo Exit Code = %ERRORLEVEL%
    

    and running them, the output is Exit Code = 0.

    It’s very curious. Nevertheless thanks a lot for your quick response.

  6. I’m not sure why the above examples don’t work for you. You might take a look at the documentation in your command prompt for the exit command: exit /?

    The set command might also be useful: set /?

    Also, remove the echo @off line (so that all the lines of the script are echoed). That would help you figure out what the last issued command is. Other than that, I can’t think of why it wouldn’t work for you. The above works just fine on my Windows XP SP2 box.

  7. modbom- not sure but maybe this is related? Try checking the registry settings mentioned there.

  8. Thx for our replies.

    No it works, but i’m not unsure why. I thinks it was caused by a side effect of another script called long before the above mentioned. Something like set %ERRORLEVEL%=0

    But now it works. Thx!

Leave a Reply

The following XHTML tags are allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>