powershellcmdexit-codeerrorleveldelayedvariableexpansion

How to set %ERRORLEVEL% in cmd /c?


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?


Solution

  • 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:

    The commands are sequenced (unconditionally executed one after the other), with the & operator, as part of a single statement.

    On the PowerShell side:


    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:

    set %ERRORLEVEL% = 4

    With %ERRORLEVEL% reflecting 0 at the start of your command sequence, the above assignment creates a variable literally named (that is, the value of %ERRORLEVEL% followed by a single space), whose value is  4 (that is, a single space followed by 4).


    Variables in 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:

    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):

    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.