Wednesday, August 22, 2007

Exit /b 1

So I spent an hour trying to debug why pre/post scripts weren't enforcing stopping workflow execution when the user's script exited with a non-zero return value. Discovered there was nothing wrong with my software - Windows batch files are just really, really stupid.

Let's look at BASH for a second. If you wanted to exit a script, returning "1" to the caller, you would put this in your script.

exit 1

Easy enough? What does this look like in a batch file? Well that depends on how you are being called. Let's look at the naive answer

exit 1

What does this do? Well if you happened to call this batch file from another batch file, you would be surprised to find that both the called batch file and the caller have stopped execution. Using exit actually terminates all execution of both the current script and all calling batch files.

To demonstrate the silliness, here is a handy dandy chart. We're going to assume that the parent batch file just does the one command so we can look at the exit status received by the parent's parent (since the %ERROLEVEL% of the last command executed is the return value of the script when there is not an explicit exit).













Parent Invocation methodInvocation methodExit methodcaller parent gets exit status?Grandparent gets exit status?Effects?
foo.batbar.batexit 1noyesBoth parent and child exit
foo.batcall bar.batexit 1noyesBoth parent and child exit
foo.batbar.batexit /b 1noyesOnly child exits
foo.batcall bar.batexit /b 1yesyesOnly child exits
cmd /c foo.batbar.batexit 1noyesBoth parent and child exit
cmd /c foo.batcall bar.batexit 1noyesBoth parent and child exit
cmd /c foo.batbar.batexit /b 1nonoOnly child exits
cmd /c foo.batcall bar.batexit /b 1yesnoOnly child exits


Ugh.

This gets really nasty when you're writing a script and don't know how you're going to get invoked. A good rule of thumb would be to always do "exit /b 1", but if you do that and they called you directly instead of saying "call foo.bat", then your return status gets dropped on the floor. But if you leave out the "/b", then calling "exit 1" will appear to work just fine when you test the script from the command line, but will explode when called by others since it will cause their scripts to seize execution as well.