cmd /c <batch-file> exit code is sometimes 0 even on error? - windows

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.

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

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.

Related

Can I call %COMSPEC% without terminating all Windows batch execution?

Came across this oddity of batch behaviour as part of the build process in our systems that I'm trying to automate in Jenkins pipelines.
To skip to the meat of the problem and why I'm encountering it, the batch file I'm calling is a build generated batch file that must be called prior to another executable within the same command window to set up paths and alike for our executable to build our system. When building as a user this is fine, you just open the cmd console, run the batch, then run the executable. However in Jenkins I have to pass all commands effectively as one batch file to make it call within the same window, unless I want to mess around with configuring all those paths by hand using withEnv or something.
Something along the lines of this in my Jenkins groovy script:
// Option 1 - && operator
bat "env_configuration.bat && env_dependent_exec.exe"
// Option 2 - multi line version
bat """env_configuration.bat
env_dependent_exec.exe
"""
// Option 3 - same using calls, yadda yadda
bat """call env_configuration.bat
call env_dependent_exec.exe
"""
// Option 4 - Place these calls in batch file and call that instead
bat "run_it_all.bat"
As part of the batch file however, for whatever reason, it has this command within it.
%COMSPEC%
exit /b 0
This will call "C:\WINDOWS\system32\cmd.exe" and output the Mircrosoft version and related blurb.e.g:
Microsoft Windows [Version 10.0.19045.2486]
(c) Microsoft Corporation. All rights reserved.
The catch is, calling this executable will immediately end all batch execution. The line "exit /b 0" isn't actually hit, something whoever created this process I assume never realised. After this batch file process completes, all subsequent commands/calls, (Option 1 above is the easiest to repro) are never hit as all batch processing just stops. This is visible directly in cmd itself, you don't need Jenkins to reproduce.
I will probably wind up just find and replacing this line to comment it out or something... but I refuse to believe I can't find some way of stopping %COMSPEC% from ending all execution of whatever batch file calls it. If I were to guess, I would guess that cmd.exe calls EXIT as it finishes and kills all....
For the sake of interest I have tried modifying the batch file in numerous ways to try and see if I can call %COMSPEC% and still get the rest of the batch script to run afterwards.
:: This fails the same way
call %COMSPEC%
exit /b 0
:: This succeeds to run, but spawns another cmd window that doens't print in the original calling batch file output, so doesn't achieve the original goal
start %COMSPEC%
exit /b 0
:: call %COMSPEC% via subroutine also immediately terminates, we never see "echo 2"
call :comspec_call
exit /b 0
:comspec_call
#echo %COMSPEC% echo 1
%COMSPEC%
#echo %COMSPEC% echo 2
I would guess cmd.exe calls the EXIT command flat on termination, hence the process death, but I've yet to find a means to prevent it doing so...
I've checked %COMSPEC% for flags that might just printout and terminate nicely, but any flags I've found that do provide this information, also terminate with EXIT (I think)
Does anyone has any idea how to call this line and continue execution as I assume the original dev intended?
Cheers in advance!
Batch processing doesn’t just stop — you have started another command interpreter, and the one that launched it is waiting for the new instance to terminate. As it doesn’t quit, everything appears to halt and you wind up killing both to get things back to normal.
Create a new batch script that does everything:
:: controller.bat
#echo off
call env_configuration.bat
call some_other.bat
env_dependent_exec.exe
...
Use of the call command causes the command shell to invoke a script using the current interpreter.
Now your Jenkins groovy script should be:
bat controller.bat
(Disclaimer: I don’t know Jenkins, so...)
Thanks to #Duthomhas and #Magoo
The problem here is that a call to %COMSPEC% launches a new command line process that must terminate before the rest of the script can continue. I didn't understand that cmd.exe was spawning a new script that the call script was waiting to terminate.
With a simplified recap:
:: env_configuration.bat
#echo env_configuration before COMSPEC
#%COMSPEC%
#echo env_configuration after COMSPEC
:: controller.bat
#echo controller before env_configuration.bat
#call env_configuration.bat
#echo controller after env_configuration.bat
controller.bat && #echo back in shell
When you run controller.bat the process will output the Microsoft blurb, but halt.
If you enter "exit" at this point it will kick out of the terminal launched with %COMSPEC% and then all following script steps continue.
It appears the original dev needed the user to be in a new subprocess to continue. The final solution to doing as was intended here is:
:: env_configuration.bat
#echo env_configuration before COMSPEC
#call %COMSPEC% /C <whatever_command_or_exec_next>
#echo env_configuration after COMSPEC
:: controller.bat
#echo controller before env_configuration.bat
#call env_configuration.bat
#echo controller after env_configuration.bat
controller.bat && #echo back in shell
Cheers!

Rest of script not running after calling a batch file to run?

I have the following script batch script:
call standalone.bat
"C:\Program Files (x86)\Notepad++\notepad++.exe" "C:\Program Files\jboss-eap-6.2\jboss-eap-6.2\standalone\log\server.log"
The first command runs as expected but the script never seems to call notepad to open the server.log file.
What is the issue here?
Edit: ending of standalone.bat is:
if ERRORLEVEL 10 goto RESTART
:END
if "x%NOPAUSE%" == "x" pause
:END_NO_PAUSE
Stephan likely diagnosed the problem in his question comment - standalone.bat probably terminates with an exit command. If that is correct, then you can work around the problem without modifying standalone.bat by changing
call standalone.bat
to
cmd /c standalone.bat.

How to exit 'git-cmd.exe' after running a command via it in a batch file?

I'm writing a batch file that runs a command via git-cmd.exe but it doesn't run the command(s) after it.
I've tried to use CALL, START /WAIT, and START /B /WAIT. All have the same behavior. Maybe there is a parameter should be sent to git-cmd.exe to execute the command and exit but I didn't find any guide explaining how to use git-cmd.exe.
This is a sample batch file:
#ECHO OFF
SET "PATH=C:\Ruby26-x64\bin;C:\Program Files\nodejs;%PATH%"
SET "CurrentDirectory=%CD%"
CD /D "%UserProfile%\AppData\Local\GitHub\PortableGit_*\"
SET "GitDirectory=%CD%"
CD /D "%CurrentDirectory%"
"%GitDirectory%/git-cmd.exe" CALL rake build
PAUSE
The command passed to git-cmd.exe is executed but the PAUSE command doesn't execute until I type EXIT command manually in the 'Command Prompt' window.
I've also tried a simple DIR command instead of rake build but the same issue still occurs:
"%GitDirectory%/git-cmd.exe" DIR
PAUSE
Thanks to the suggestions, the issue was resolved by passing EXIT command to git-cmd.exe as follows:
#ECHO OFF
SET "PATH=C:\Ruby26-x64\bin;C:\Program Files\nodejs;%PATH%"
SET "CurrentDirectory=%CD%"
CD /D "%UserProfile%\AppData\Local\GitHub\PortableGit_*\"
SET "GitDirectory=%CD%"
CD /D "%CurrentDirectory%"
"%GitDirectory%/git-cmd.exe" "CALL rake build & EXIT"
PAUSE
There was a useful conversation between me and someone else (I think his name was mony) but it was deleted, don't know why?!
He sent me this link with the difference between & and && between commands:
https://stackoverflow.com/a/25344009/9586127
The & before the EXIT command in order to execute both commands independent on result of the first one. If && is used, the EXIT command will be executed only if the first command succeeded (exit code 0).
In addition, he told me a way to replace the lines 3:6 by one line to be:
FOR /D %%I IN ("%LocalAppData%\GitHub\PortableGit_*") DO SET "GitDirectory=%%I"

How to get return/exit code of a batch file started with START command?

I am trying to run a batch file with start /high and still get the return/exit code, i.e. %ERRORLEVEL%. The problem seems to be that command START does not return the exit code that the batch file returns.
We have a simple batch file for testing named BatFileThatReturnsOne.bat.
The contents of BatFileThatReturnsOne.bat are
EXIT /B 1
We are calling this as such:
start /high /wait BatFileThatReturnsOne.bat
But no matter what the batch file returns, the execution of start never has a %ERRORLEVEL% of anything other than 0 (zero).
This is all actually called by cfn-init in CloudFormation, but that is probably not relevant because we can reproduce it from a command line window.
The actual call is:
cmd.exe /C start /high /wait BatFileThatReturnsOne.bat
How do I get start to set the %ERRORLEVEL% to something other than 0 (zero)?
directly from a cmd window or a batch file you can use
start /high /wait cmd /c BatFileThatReturnsOne.bat
but if you need to start the cmd instance to execute the start command that launchs the batch file then you can use
cmd /v /e /c" start /high /wait cmd /c launched.cmd & exit ^!errorlevel^!"
Just change EXIT /B 1 by EXIT 1.
As explained in the Table 4 given in this answer about START /WAIT bat command:
When the started Batch file ends, set ERRORLEVEL = value from 'EXIT number' commmand.

Error level not changing when file is ran with psexec

I have one piece of code that is behaving differently when I ran it on server and when I ran it with psexec. I'm actually trying to determent is computer offline, but that is not question here. Problem is that when I run this command directly on server errorlevel is changing to 1, because pc is online. But when I use psexec to run file with same commands on that server errorelevel is not changing it stays 0. I cant find any explanations on internet.
echo %errorlevel%
ping -n 1 machine | findstr "not" > nul
IF %errorlevel%==0 (
echo test
)
echo %errorlevel%
pause
What you show is not a command, but a batch file, say ping_machine.cmd. To call it from psexec the command line would be something like psexec \\server cmd /c ping_machine.cmd. If I guessed wrong you may stop reading the rest of this answer now (and next time post enough relevant context so that one doesn't have to guess).
Problem is that cmd /c returns the exit code of ping_machine, but the batch file does not explicitly set an exit code, so it returns 0 by default. This can be verified at the cmd prompt with the following 2 runs - note that inside the batch file you see the same/correct errorlevels, but cmd /c returns 0.
C:\etc>ping_machine
0
1
Press any key to continue . . .
C:\etc>echo %errorlevel%
1
C:\etc>cmd /c ping_machine
0
1
Press any key to continue . . .
C:\etc>echo %errorlevel%
0
To have cmd /c behave as you expect (and in turn psexec as well), add the following line at the end of the batch file to return the respective errorlevel (this works because neither echo nor pause modify the errorlevel, otherwise you'd have to save it in a temp variable for later use).
exit /b %errorlevel%
There it is. When I run this batch file locally errorlevel is changed to 9009. When I use psexec \\computername -f -c pathToFile , error code is staying 0.
#echo off
echo %errorlevel%
pin 234343
echo %errorlevel%
pause

Resources