1

I have a batch file that I run from an msysgit bash shell script via cmd /c a.bat and then I test the exit code to determine whether or not to continue. When the batch file fails, from wherever it fails, exit /b 1 is called and the batch file exits with code 1.

I recently noticed that if the batch file fails at one point where exit /b 1 is called that the exit code is not returned, and is instead 0. It only happens in an inner block. Here's an example:

@echo off

if foo EQU foo (
  if bar EQU bar (
    echo Exiting with code 99.
    exit /b 99
  )
  echo this line is necessary to reproduce the issue
)

That should always exit 99, but:

X:\j\tmp>doesnotexist
'doesnotexist' is not recognized as an internal or external command,
operable program or batch file.

X:\j\tmp>echo %ERRORLEVEL%
9009

X:\j\tmp>cmd /c a.bat
Exiting with code 99.

X:\j\tmp>echo %ERRORLEVEL%
0

X:\j\tmp>cmd /c call a.bat
Exiting with code 99.

X:\j\tmp>echo %ERRORLEVEL%
99

If the last echo line is removed then cmd /c a.bat does return exit code 99. And as I mentioned in the actual batch file the exit /b <Code> does work most of the time.

I can reproduce in Windows 7 and 10. My question is why does it not return the exit code in the repro above? Is it a bug or something I did wrong? As you can see I tried CALL on a hunch and it seems to remedy this issue, but I'm not sure why. CALL is supposed to be for calling one batch from another without losing control.

Jay
  • 319
  • 3
  • 12
  • The `/b` parameter means to exit the batch script without exiting the shell. For this case the `cmd!eExit` function basically sets the error level (i.e. internally the global variable `cmd!LastReturnCode`) and does a `goto :EOF`. – Eryk Sun Feb 12 '18 at 09:40
  • 1
    When the shell is in single-command mode (i.e. `/c`), `cmd!eExit` also returns this exit value, which can *sometimes* bubble up to `cmd!Dispatch` and out as the exit code via `cmd!CMDExit`. However, in this case the implicit `&` command separator (i.e. `cmd!eComSep`) that's inserted when the `echo this` statement is added breaks this chain by returning 0, which becomes the process exit code. It hard codes this return value when it skips executing the right-hand node (e.g. the `echo this` statement) because it sees that `cmd!GotoFlag` is set. Recall that `exit /b` works like `goto :EOF`. – Eryk Sun Feb 12 '18 at 09:43
  • @eryksun Thanks, that appears to be the answer. My question appears to be a duplicate of a question asked by @ereOn that you already answered, [python - How to properly report an exit status in batch? - Stack Overflow](https://stackoverflow.com/q/30714985/3655142). The answers in that thread contain a lot of detail that was helpful. I will continue to use `CALL` which from what I understand from everyone's answers there should work fine so long as it's the only command (and @dbenham mentions a workaround in case it's not). – Jay Feb 12 '18 at 21:44

2 Answers2

0

You are starting a new cmd.exe instance which will produce the 99 error in the new window instead of current window.

The reason why you are getting the correct code when running it using call is purely because you are telling it to run the batch file in the current console, hence you get exit code 99

So you do not need to run cmd /c to start the file. Instead run the batch file simply as:

c:\path_to\file\a.bat

or if in the path or current directory:

a.bat
Gerhard
  • 22,678
  • 7
  • 27
  • 43
-1

When you call cmd /c, you're creating a new cmd.exe process, and that process is calling your script. So the errorcode variable within that new process is set to 99 but then the cmd process exits successfully, setting the level back to 0 in your starting command prompt.

If you just run a.bat, the error level gets set properly. If you try cmd /k a.bat, the new cmd will be still running, and you'll be able to see that the error level is 99. Then, depending on how you exit, the error level of the original cmd will be set appropriately.

Jon Thysell
  • 377
  • 1
  • 10