How to properly report an exit status in batch?

后端 未结 5 1454
[愿得一人]
[愿得一人] 2020-12-03 13:08

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

5条回答
  •  醉梦人生
    2020-12-03 13:31

    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.

提交回复
热议问题