I think i have a basic understanding of ERRORLEVEL vs %ERRORLEVEL% but !ERRORLEVEL! confuses me.
I'm making a script that calls an executable, then tasklist to see if its running, then taskkill to kill it if it is and then trying to output the errorlevels and repeating for other exe's and i'm realising i really don't understand errorlevels in batch.
I set a variable equal to !errorlevel! then used that variable without quotation marks in an echo, and the variable changed from one uint16 to another uint16 when there was an error after the set, like its a reference to the real one instead of a copy. I want copy. Can someone explain the difference between these guys?
Update: Here is the snippet I'm working on.
for %%P in (%executableList%) do (
echo ----------------------------------------------------------------------------------
set exeErrorlevel=0
set running=false
start %%~fP
set exeErrorlevel=!ERRORLEVEL!
rem for debugging purposes
echo %%~nP%%~xP older errorlevel %ERRORLEVEL%
echo %%~nP%%~xP newer errorlevel !ERRORLEVEL!
echo before tasklist running var is : !running!
tasklist /FI "IMAGENAME eq %%~fP" | find /I /N /C "%%~fP" >nul && set running=true
echo after tasklist is running var is: !running!
if !running! equ true (
echo %%~nP%%~xP Program is running
taskkill /F /IM %%~nP%%~xP /T
echo %%~nP%%~xP Program was killed
if !exeErrorlevel! == 0 (
echo %passString% %%~nP%%~xP process was started and killed safely
echo %passString% %%~nP%%~xP process was started and killed safely >>%outputfile%
) else (
echo %failString% %%~nP%%~xP process was killed with errorcode !exeErrorlevel!
echo %failString% %%~nP%%~xP process was killed with errorcode !exeErrorlevel! >>%outputfile%
)
) else (
if !exeErrorlevel! == 0 (
echo %passString% %%~nP%%~xP process exited safely
echo %passString% %%~nP%%~xP process exited safely >>%outputfile%
) else (
taskkill /F /IM %%~nP%%~xP /T
echo %failString% %%~nP%%~xP process abruptly exited with errorcode !exeErrorlevel!
echo %failString% %%~nP%%~xP process abruptly exited with errorcode !exeErrorlevel! >>%outputfile%
)
)
echo. >>%outputfile%
)
I need to make sure exeErrorlevel has a copy of the errorlevel at a certain point in time - I only want to capture errors from the exe, not from the success/failure of tasklist/find/taskill. I'm concerned that exeerrorlevel, because of the delayed expansion, is accessing the delayed errorlevel upon execution. perhaps that should be set exeErrorlevel=%errorlevel% instead. In the line where i echo older and newer variables usually return different integers? In all my test runs %errorlevel% seems to typically return 0 whereas !errorlevel! is consistently non zero for executables with bad exit codes.
The errorlevel
errorlevel
is the name of a dynamic variable (it is not placed in the environment block but hold in memory) that stores the exit code of the previous executed process/command (if it sets that value, read here, here, here and here).The
if
command allows the usage of theif errorlevel n
syntax to check if the value of theerrorlevel
variable is greater than or equal ton
, without involving the batch parser into retrieving the value of the variable.But, if we put the batch parser to work with variable values,
%errorlevel%
is just a reference to the value stored in the variable, a read operation. Just the same as!errorlevel!
. The main difference between the two is when the value is retrieved depending on the rules on variable expansion.There is a great difference in using the
if errorlevel
or retrieving the value in the variable:if
constuct will not make this test.If you do something like
set errorlevel=10
, the dynamicerrorlevel
value will not be retrieved with%errorlevel%
or!errorlevel!
as the value set in the environment will hide the dynamic value. But asif errorlevel
does not read the environment block but directly reads the internal variable that holds the value, it will work without problems.The variables
The batch syntax does not include the option of having more than one variable pointing to the same value in memory in a way that if one of the variables changes its value, the other will reflect the change.
This behaviour can be simulated by proper use of the different phases in variable expansion, properly setting a variable to the name of another and forcing the batch parser to do two passes over the command so first variable is resolved to the name of the second and that to the real value.
Your problem
Simplified (non even working) code just for analysis
line 1: the code in the
do
clause, all the code, is parsed. Any%var%
variable read operation is removed from the code, replaced with the value inside the variable before starting the execution. This means that if the variable changes its value you will not be able to retrieve the changed value as the read operation does not exist, only the initial value in the variable.line 3: the executable is launched, in a separate process, without waiting for the process to end. Is it important? See next line
line 4: the current (delayed expansion used) value of the
errorlevel
variable is retrieved and stored inexeErrorlevel
variable. BUT the value stored is NOT theerrorlevel
returned by the executable (separate process, not waiting for it to end, how will we know what theexit code = errorlevel
is?), but the exit code of thestart
command.line 6: as the
%errorlevel%
read operation was removed, this line will echo the value that was stored in theerrorlevel
variable before thedo
clause started to execute.line 7: the current value of the
errorlevel
variable is retrieved. And here, we can have a problem. How the script being executed is named? There is a difference between.bat
and.cmd
. On sucess theset
command in line 4 will clear (set to 0) theerrorlevel
variable if this is a.cmd
file, but will not change theerrorlevel
if it is a.bat
file.lines 11, 14, 21: as seen the
exeErrorlevel
variable does not contain a valid value. And no, changing the lines to!errorlevel!
will not retrieve the exit code of the process, but the exit code of thetaskkill
.To be able to retrieve the exit code / errorlevel of a process we need to wait for it to end. If you need to start the process, if it keeps running kill it, and in both cases retrieve the exit code, directly call the executable or use
start "" /wait programName
, AND run the killing process in parallel (ex.start /b "" monitor.bat programName
or something similar before starting the program). The main process will wait and retrieve the exit code. The monitor process handles the killing.