Consider a command script named t.cmd that consists solely of these 2 lines:
@exit /b 123
@echo If you see this, THEN EXIT FAILED..
So, the script merely sets the exit code of the script's execution process to 123, but does not kill cmd.exe. The final echo confirms that exit actually causes an immediate return (its output should not appear).
Now execute this script, and then print out the %errorlevel%:
>t.cmd
>echo %errorlevel%
123
So far so good: everything behaves exactly as expected.
But now execute the above all on one line, using && for conditional execution:
>t.cmd && echo %errorlevel%
123
I do NOT expect this: if t.cmd really returns with a non-0 exit code, then it should stop everything after that && (i.e. the echo) from executing. The fact that we see it print means that it DID execute. What the heck is going on?
And if execute the above all on one line, using || for conditional execution:
>t.cmd || echo %errorlevel%
>
This behavior is also the opposite of what I expect (altho it is consistent with the && behavior above).
Note that this weird behavior is ONLY true for bat files, but not for "raw commands".
Proof: consider the following command line interactions where instead of calling t.cmd, I try to execute the bogus command abcdef:
>abcdef
'abcdef' is not recognized as an internal or external command,
operable program or batch file.
>echo %errorlevel%
9009
>abcdef && echo %errorlevel%
'abcdef' is not recognized as an internal or external command,
operable program or batch file.
>abcdef || echo %errorlevel%
'abcdef' is not recognized as an internal or external command,
operable program or batch file.
9009
Here, && and || immediately see the exit code of the failed bogus command.
So why do cmd files behave differently?
A possibly related bug in cmd.exe has been observed in File redirection in Windows and %errorlevel%
Also, I am aware that ERRORLEVEL is not %ERRORLEVEL%
By the way, the code above was all executed on a Win 7 Pro 64 bit box. I have no idea how other versions of Windows behave.
With t.bat
slightly modified as follows:
@exit /b 123%~1
@echo If you see this, THEN EXIT FAILED..
Think out next output:
==>t.bat 1
==>echo %errorlevel%
1231
==>t.bat 2&echo %errorlevel%
1231
==>echo %errorlevel%
1232
==>cmd /V /C t.bat 3^&echo !errorlevel!
1233
==>echo %errorlevel%
0
==>cmd /V /C t.bat 4^&echo !errorlevel!^&exit /B !errorlevel!
1234
==>echo %errorlevel%
1234
==>
Resources
%~1
etc. special page) Command Line arguments (Parameters)
Edit to enlighten EnableDelayedExpansion
:
==>cmd /v
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
==>t.bat 5&echo !errorlevel!
1235
==>echo %errorlevel%
1235
==>
Edit 2 to enlighten (or confuse?) &&
and ||
. Save next code snippet as errlevels.cmd
:
@ECHO ON >NUL
@SETLOCAL enableextensions enabledelayedexpansion
(call )
@echo ^(call ^) command clears errorlevel %errorlevel%
abcd /G>NUL 2>&1
@echo abcd /G: "'abcd' not recognized" errorlevel %errorlevel%
abcd /G>NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
@echo abcd /G: ^|^| changed errorlevel %errorlevel%
find /G >NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
@echo find /G: ^|^| unchanged errorlevel %errorlevel%
call t.cmd 333 && echo YES !errorlevel! || echo NO !errorlevel!
type t.cmd
t.cmd 222 && echo YES !errorlevel! || echo NO !errorlevel!
Output (from errlevels.cmd
):
==>errlevels.cmd
==>(call )
(call ) command clears errorlevel 0
==>abcd /G 1>NUL 2>&1
abcd /G: "'abcd' not recognized" errorlevel 9009
==>abcd /G 1>NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
NO 1
abcd /G: || changed errorlevel 1
==>find /G 1>NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
NO 2
find /G: || unchanged errorlevel 2
==>call t.cmd 333 && echo YES !errorlevel! || echo NO !errorlevel!
NO 333
==>type t.cmd
@exit /B %~1
==>t.cmd 222 && echo YES !errorlevel! || echo NO !errorlevel!
YES 222
==>
Note that
||
shows errorlevel 1 although 'abcd' not recognized
error should be 9009
whilst||
keeps errorlevel 2 unchanged for FIND: Invalid switch
error.||
branch evaluates in call t.cmd 333
whilst&&
branch evaluates in t.cmd 222
.On (call )
see DBenham's answer:
If you want to force the
errorlevel
to0
, then you can use this totally non-intuitive, but very effective syntax:(call )
. The space aftercall
is critical.If you want to set the
errorlevel
to1
, you can use(call)
. It is critical that there not be any space aftercall
.
%errorlevel% is expanded when line is read. So it is 123 at the time the line was read so comes from the previous command not t.exe. && is executed only if the current errorlevel (from t command) is 0.
See setlocal /?
and set /?
for more info.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With