I\'m facing a weird situation where a batch file I wrote reports an incorrect exit status. Here is a minimal sample that reproduces the problem:
bug.cmd
I'm going to try to join the answers from dbenham (that checked the cases from batch code) and eryksum (that directly went to the code). Maybe doing it I could understand it.
Let's start with a bug.cmd
exit /b 1 & rem
From the eryksum answer and tests we know this code will set errorlevel
variable to 1, but the general result of the command is not a failure as the inner functions inside cmd
will process the concatenation operator as a function call that will return (meaning a C function returning a value) the result of the right command. This can be tested as
C:> bug.cmd
C:> exit /b 1 & rem
C:> echo %errorlevel%
1
C:> bug.cmd && echo NEL || echo EL
C:> exit /b 1 & rem
NEL
C:> echo %errorlevel%
1
Yes, errorlevel
is 1 but conditional execution will run the code after the &&
as the previous command (eComSep
) returned SUCESS
.
Now, executed in a separate cmd
instance
C:> cmd /c bug.cmd
C:> exit /b 1 & rem
C:> echo %errorlevel%
0
C:>
Here the same process that makes the conditional execution "fail" in the previous case propagates the errorlevel 0
out of the new cmd
instance.
But, why does the call
case work?
C:> cmd /c call bug.cmd
C:> exit /b 1 & rem
C:> echo %errorlevel%
1
C:>
It works because the cmd
is coded something like (rough assembler to C)
function CallWork(){
....
ret = BatProc( whatIsCalled )
return ret ? ret : LastRetCode
}
function eCall(){
....
return LastRetCode = CallWork( ... )
}
That is, the call
command is handled in function eCall
that calls CallWork
to delegate the context generation and execution to BatProc
. BatProc
returns the resulting value from execution of the code. We know from previous the tests that this value is 0 (but errorlevel / LastRetCode
is 1). This value is tested inside CallWork
(the ternary ?
operator): if the BatProc
return value is not 0, return the value else return LastRetCode
, that in this case is 1. Then this value is used inside eCall
as return value AND stored inside LastRetCode
(the =
in the return command is an asignation) so it is returned in errorlevel
.
If i didn't miss something, the rest of the cases are just variations over the same behaviour.