We have a bunch of .bat
build scripts which are invoked by a PowerShell based GitLab runner that were recently refactored from:
program args
if !errorlevel! neq 0 exit /b !errorlevel!
to the more succinct:
program args || exit /b
Today I investigated a build job which obviously failed if you looked at the error log but which was reported as a success. After much experimentation I discovered that this pattern didn't always work as expected:
program args || exit /b
but this did appear to work when the former didn't:
program args || exit /b !errorlevel!
I've read the SO question Windows batch exit option b with or without errorlevel and the statement below from https://www.robvanderwoude.com/exit.php but still can't quite explain what I'm observing.
The DOS online help (HELP EXIT) doesn't make it clear that the /B parameter exits the current instance of script which is not necessarily the same as exiting the current script.
I.e. if the script is in a CALLed piece of code, the EXIT /B exits the CALL, not the script.
This is the minimal batch file I used to explore this:
@echo off
setlocal EnableDelayedExpansion
cmd /c "exit 99" || exit /b
:: cmd /c "exit 99" || exit /b !errorlevel!
And this is how I invoked the batch file (to simulate how it was invoked by the GitLab PowerShell based runner):
& .est.bat; $LastExitCode
Here is the output depending on which of the two lines in the batch file is executed:
PS> & .est.bat; $LastExitCode
0
PS> & .est.bat; $LastExitCode
99
There is another way to get the correct behaviour which is to invoke the batch file longhand from within PowerShell using CALL
as well:
PS> & cmd.exe /c "call .est.bat"; $LastExitCode
99
While I appreciate that this may be the correct way to invoke a batch file from PowerShell, that does not appear to be common knowledge based on the many examples I've seen. Also I wonder why PowerShell doesn't invoke a batch file this way if it's "the right way". Lastly I still don't understand why, when leaving off the call
, the behaviour changes depending on whether we add the !errorlevel!
to the exit /b
statement.
UPDATE: Thanks for all the discussion so far but I feel it's getting lost in the weeds which is probably my fault for being too vague in my original question. What I think I'm really after (if possible) is a definitive statement about when the errorlevel
is (supposedly) evaluated in the following statement:
program || exit /b
Is it really evaluated early (i.e. before program
is run) like this:
program || exit /b %errorlevel%
Or is it evaluated lazily (i.e. when exit
is being executed after program
has run and internally errorlevel
has been updated), more analogous to this:
program || exit /b !errorlevel!
Hence I'm not really after speculation unless, sadly, that is the best that we can do in which case knowing there is no definitive answer or that it's a bug is an acceptable answer to me :o).
See Question&Answers more detail:
os