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 the if errorlevel n
syntax to check if the value of the errorlevel
variable is greater than or equal to n
, 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:
- The variable read operation will check if the environment block contains a variable with the indicated name.
- The
if
constuct will not make this test.
If you do something like set errorlevel=10
, the dynamic errorlevel
value will not be retrieved with %errorlevel%
or !errorlevel!
as the value set in the environment will hide the dynamic value. But as if 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
1 for %%P in (%executableList%) do (
2
3 start %%~fP
4 set exeErrorlevel=!ERRORLEVEL!
5
6 echo %%~nP%%~xP older errorlevel %ERRORLEVEL%
7 echo %%~nP%%~xP newer errorlevel !ERRORLEVEL!
8 ....
9 if !running! equ true (
10 taskkill /F /IM %%~nP%%~xP /T
11 if !exeErrorlevel! == 0 (
12 ....
13 ) else (
14 echo process killed with errorcode !exeErrorlevel!
15 )
16 ) else (
17 if !exeErrorlevel! == 0 (
18 ....
19 ) else (
20 taskkill /F /IM %%~nP%%~xP /T
21 echo process abruptly exited with errorcode !exeErrorlevel!
22 )
23 )
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 in exeErrorlevel
variable. BUT the value stored is NOT the errorlevel
returned by the executable (separate process, not waiting for it to end, how will we know what the exit code = errorlevel
is?), but the exit code of the start
command.
line 6: as the %errorlevel%
read operation was removed, this line will echo the value that was stored in the errorlevel
variable before the do
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 the set
command in line 4 will clear (set to 0) the errorlevel
variable if this is a .cmd
file, but will not change the errorlevel
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 the taskkill
.
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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…