I want to be able to set the %ERRORLEVEL%
environment variable (also referred to as a "system variable") to an arbitrary value in the beginning of a command prompt script (i.e. cmd /c
) running in PowerShell. Whenever people want to set %ERRORLEVEL%
(1), they do the following:
cmd /c "exit /b 3"
cmd /c "echo %ERRORLEVEL%"
However, although %ERRORLEVEL%
is set to 3
when the above is run in the normal Command Prompt, if these exact lines are executed in PowerShell, the exit code environment variable is not set to 3
and remains 0
. In addition, you cannot do the following:
cmd /v:on /c "exit /b 3 & echo !ERRORLEVEL!"
The exit
command breaks completely out of the command execution (i.e. cmd /c
) and no other command after the &
is run.
Therefore, I tried executing the following command in PowerShell:
cmd /v:on /c "SET %ERRORLEVEL% = 4 & echo !ERRORLEVEL!"
The expected output is 4
, but this always outputs 0
. I cannot figure out why I am unable to SET
the %ERRORLEVEL%
environment variable. I have used delayed command execution (example here), but no amount of fiddling seems to work here.
Does anyone have any idea of why the command SET %ERRORLEVEL% = 4
does not work?
If you are not able SET
environment variables, then how can set %ERRORLEVEL%
to an arbitrary value in a string of commands separated by an ampersand (&
) like above?
If you want to store the eventual exit code in a cmd.exe
variable for later use with exit
, do not try to set ERRORLEVEL
; instead, use a custom variable name; e.g., ec
(for exit code):
# Execute in PowerShell
PS> cmd /v /c 'set "ec=3" & echo ... & exit /b !ec!'; $LASTEXITCODE
...
3
On the cmd.exe
side:
set "ec=3"
sets variable ec
to value 3
Enclosing the name-value pair in "..."
isn't strictly necessary, but clearly delineates the end of the value, and allows use of values with embedded special characters such as &
.
The variable's value can later be referenced as %ec%
(up-front expansion) or !ec!
(delayed expansion, if enabled via setlocal enabledelayedexpansion
or via command-line switch /v
- see help setlocal
)
echo ...
is a sample command representing further commands
exit /b !ec!
exits with the value of variable ec
as the exit code; note how the variable is of necessity referenced as !ec!
rather than %ec%
, via delayed expansion, because the variable was set as part of the same statement.
/b
(exit the current batch file only) or not doesn't make a difference here; either way, the exit
statement in this case determines the process exit code of the cmd
instance.The commands are sequenced (unconditionally executed one after the other), with the &
operator, as part of a single statement.
On the PowerShell side:
PowerShell's analog to cmd.exe
's &
is ;
, the statement separator - it allows you to place multiple statements on a single line.
$LASTEXITCODE
is the automatic PowerShell variable that contains the process exit code of the most recently executed external program, which in this case is cmd.exe
's, with value 3
.
Does anyone have any idea why the command
SET %ERRORLEVEL% = 4
does not work?
To summarize the helpful information from the comments on the question:
Fundamentally, do no try to set the dynamic (pseudo) environment variable %ERRORLEVEL%
- it is automatically maintained by cmd.exe
to reflect the most recent command's exit code - see the bottom section.
Do not put spaces around =
in a cmd.exe
variable assignment:
=
become part of the variable name.Do not enclose the target variable name in %...%
(unless you want to indirectly set a variable, via another variable whose value contains the name of the variable to assign to).
set %ERRORLEVEL% = 4
With %ERRORLEVEL%
reflecting 0
at the start of your command sequence, the above assignment creates a variable literally named 0
(that is, the value of %ERRORLEVEL%
followed by a single space), whose value is 4
(that is, a single space followed by 4
).
cmd.exe
:Fundamentally, with the exceptions discussed below, variables in cmd.exe
are all environment variables.
Unlike in shells such as Bash and PowerShell, there is no separate namespace for shell-local variables that are not seen by child processes.
This has the following implications:
Predefined persistent environment variables such as %Path%
share the same namespace as custom variables you define with the SET
command, so you have to be mindful of name collisions.
Similarly, child processes you run from your cmd.exe
session / batch file inherit custom variables you've created in the session.
setlocal
does not change that; setlocal
is a cmd
-internal scoping mechanism that allows you to control the lifetime of custom environment variables, by localizing them to the scope (batch file) in which setlocal
was called, and removing them via a subsequent endlocal
call or, implicitly, at the end of the enclosing batch file.Custom variables are process-only environment variables that go out of scope with the cmd.exe
process; persistent environment variable definitions must be created and modified via the registry, such as with the setx.exe
utility.
In addition to the predefined (persistent) environment variables and custom (session-only) environment variables, cmd.exe
maintains dynamic pseudo environment variables such as %ERRORLEVEL%
and %RANDOM%
(see list below):
These pseudo environment variables have dynamic values maintained by cmd.exe
itself...
... and they are not visible to child processes.
Note: Strictly speaking, these dynamic variables are only enabled with the so-called command extensions turned on, but that is true by default (you can disable them with /E:OFF
, but that is ill-advised).
Because these dynamic variables are not strictly part of the process environment that child processes inherit a copy of, they are not environment variables, even though help SET
somewhat confusingly calls them dynamic environment variables.
You shouldn't (and cannot) modify these variables.
If you try, what really happens is that you shadow (override) these pseudo variables with real, custom environment variables, which by definition have static values.
Later code that relies on the variables by that name to have their usual, dynamic behavior can therefore malfunction.
The list of dynamic variables, as retrieved on Windows 10 via help SET
(emphasis added):
If Command Extensions are enabled, then there are several dynamic environment variables that can be expanded but which don't show up in the list of variables displayed by
SET
. These variable values are computed dynamically each time the value of the variable is expanded. If the user explicitly defines a variable with one of these names, then that definition will override the dynamic one described below:
%CD%
- expands to the current directory string.%DATE%
- expands to current date using same format as DATE command.%TIME%
- expands to current time using same format as TIME command.%RANDOM%
- expands to a random decimal number between 0 and 32767.%ERRORLEVEL%
- expands to the current ERRORLEVEL value%CMDEXTVERSION%
- expands to the current Command Processor Extensions version number.%CMDCMDLINE%
- expands to the original command line that invoked the Command Processor.%HIGHESTNUMANODENUMBER%
- expands to the highest NUMA node number on this machine.