There are three types of variables of which value is accessed using the syntax
!variable! on having enabled delayed environment variable expansion with using the option
EnableDelayedExpansion of the command
setlocal from within a Windows command prompt window or a batch file, i.e. using
1. Persistent stored variables
There are environment variables stored persistent in Windows registry.
User variables stored in Windows registry under the key:
System variables stored in Windows registry under the key:
The user variables are defined for just the account in which user registry hive they are stored (file
%UserProfile%\ntuser.dat). The system variables are defined for all accounts used on a Windows machine (file
The persistent stored variables can be viewed, edited and deleted by opening the Windows Control Panel, clicking on System, clicking next (on left side) on Advanced system settings and clicking on button Environment Variables. The upper half is for the user variables of the current user account and the lower half is for the system variables since Windows XP.
There are defined by default as user variables only
TMP since Windows XP.
The list of predefined system variables is since Windows XP:
ComSpec NUMBER_OF_PROCESSORS OS PATH PATHEXT PROCESSOR_ARCHITECTURE PROCESSOR_IDENTIFIER PROCESSOR_LEVEL PROCESSOR_REVISION TEMP TMP windir
There is on Windows Vista and newer Windows versions defined by default also the system variable
None of the predefined system variables with exception of
PATHEXT should be deleted or modified ever as this could cause lots of troubles which can even result in Windows not starting anymore. I would strongly recommend using a virtual machine on experimenting with the predefined system variables for which a backup of the entire virtual machine image exists before starting the experiments.
1.1 Backup of persistent stored variables
It is advisable to make a backup of the user and system variables before playing around with them by opening a command prompt window and running for example:
md C:\VariablesBackup 2>nul %SystemRoot%\System32\reg.exe EXPORT HKCU\Environment "C:\VariablesBackup\UserVariables.reg" %SystemRoot%\System32\reg.exe EXPORT "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "C:\VariablesBackup\SystemVariables.reg"
1.2 Restore of persistent stored variables
Restoring the user variables can be done from within a command prompt window on having made a backup before with:
%SystemRoot%\System32\reg.exe DELETE "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /f %SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\UserVariables.reg"
Restoring the system variables can be done from within a command prompt window opened as administrator on having made a backup before with:
%SystemRoot%\System32\reg.exe DELETE HKCU\Environment /f %SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\SystemVariables.reg"
It is advisable to restart Windows on having restored user and system variables from a backup to make sure that really all processes use the restored variables.
1.3 Modification of
PATH with a batch file
Batch file programmers thinking about modification of user or system
PATH with a batch file should read first:
- What is the reason for “X is not recognized as an internal or external command, operable program or batch file”?
- Why are other folder paths also added to system PATH with SetX and not only the specified folder path?
- How can I use a .bat file to remove specific tokens from the PATH environment variable?
- How to search and replace a string in environment variable PATH?
- Adding the current directory to Windows path permanently
ATTENTION: It is an absolute NO GO – NEVER EVER to use command SETX with
%PATH% in a batch file to modify user or system
The only reason to modify user or system
PATH with a batch file on installation of a program (executable or script) is that this program is designed for being mainly used by users from the Windows command line. A program is bad designed if it requires that its directory or one of its subdirectories is in
PATH to work at all. A program is very bad designed if it adds a folder path to system
PATH left to the folder paths defined by default by Windows.
PATH variable should start always with:
The Windows system directory is the directory containing most executables and dynamic linked libraries. For that reason it should be always the first directory searched for executables and libraries after the current directory.
2. Windows shell variables
There are a lot more Windows environment variables predefined as it can be seen on:
These variables are defined by Windows shell which is by default
explorer.exe being started on Windows start as Windows shell according to registry value
Shell under registry key:
The Window shell elements noticed most by users are the Windows desktop, the Windows Start menu and the Windows taskbar with the system tray.
The Windows shell defines in its memory lots of environment variables depending on various values in Windows registry for the current user account not stored persistent in Windows registry as described above. The current list of environment variables is copied whenever a new process is created like starting an executable from Windows shell.
The list of environment variables defined by Windows shell consisting of the persistent stored user and system variables and the shell variables for current user account can be seen by opening a command prompt window and running the command SET without any additional argument.
Most of the shell variables are defined from the registry strings under
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Most of the registry string values exist in
User Shell Folders of type
REG_EXPAND_SZ and in
Shell Folders of type
REG_SZ. But there are some registry string values existing only under one of the two registry keys.
See also: How to create a directory in the user’s desktop directory?
In this answer is explained in detail how Windows Explorer evaluates these registry string values and handles them on manual modification by a user using
reg.exe on the example of the shell folder
The function CreateEnvironmentBlock and the private shell32 function
RegenerateUserEnvironment are used by
explorer.exe with GetUserNameExW and GetComputerNameExW to create the environment variables list copied by Windows Explorer on starting an executable from Windows shell using the Windows kernel library function CreateProcess.
See also the comments written by Eryk Sun on question Where are the environment variables for cmd.exe stored?
There are some environment variables on 64-bit Windows which depend on starting a 64-bit or a 32-bit executable. Microsoft documented them on WOW64 Implementation Details as follows:
PROCESSOR_ARCHITECTURE=AMD64 or PROCESSOR_ARCHITECTURE=IA64 or PROCESSOR_ARCHITECTURE=ARM64
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The ProgramW6432 and CommonProgramW6432 environment variables were added starting with Windows 7 and Windows Server 2008 R2.
The value of the environment variable
PROCESSOR_ARCHITECTURE cannot be used to find out from within a batch file if the installed Windows is a 32-bit (x86) or a 64-bit (AMD64) Windows. The value depends on processing of the batch file by either 64-bit
%SystemRoot%\Sysem32\cmd.exe or by 32-bit
%SystemRoot%\SysWOW64\cmd.exe on 64-bit Windows.
See also the Microsoft documentations:
The usage of environment variables defined by the Windows shell must be done wisely on writing a batch file which should be designed for execution by other accounts on same or a different Windows machine. Many batch files working fine in environment of author of the batch file do not work in environment set up on running the same batch file as scheduled task with system account or on a different machine because of the differences in environment variables list.
The environment variables defined by a process on starting an executable with the Windows kernel library function
CreateProcess determine the environment variables which the started executable can use.
Most applications use
CreateProcess with value
null for the parameter
CreateProcess makes a copy of the current environment variables of the current process. For that reason every executable started from Windows desktop, start menu or taskbar gets the environment variables as defined by the
explorer.exe instance running as Windows shell.
A really very good coded executable or script using environment variables which are defined by default on Windows verify explicitly that every used environment variable is really defined and use otherwise a suitable default value like
C:\Windows on environment variable
SystemRoot is not defined with checking if there is really a directory
C:\Windows and exit with an appropriate error message on an important environment variable not defined before causing perhaps damage.
SystemRoot is an example for a Windows shell variable defined by
explorer.exe as environment variable which is not determined by the registry string values of the shell folders. Some environment variable values should not be modified by a user at any time independent on its real source which no script author ever needs to know as being a Windows implementation detail.
3. Dynamic variables of Windows command processor
There are some variables listed in help of command SET output on running in a command prompt window
set /? which cannot be found in the list of environment variables on running just
These variables are:
CD DATE TIME RANDOM ERRORLEVEL CMDEXTVERSION CMDCMDLINE HIGHESTNUMANODENUMBER
The dynamic variables are internal variables of
cmd.exe. So these variables are available only in a Windows command prompt window which is a running
cmd.exe process or a batch file processed by
cmd.exe. The dynamic variables are not available in other executables or scripts as these variables are not environment variables.
The most common used dynamic variables are:
The current directory path not ending with a backlash, except the current directory is the root directory of the drive.
The current local date in format as defined for the account in region and language settings of Windows.
The current local time in format as defined for the account in region and language settings of Windows.
Exit value of previously executed command or program.
A random decimal number between 0 and 32767.
There are some more dynamic variables, but they are rarely used in batch files.
There is additionally the variable
__AppDir__ containing the path of currently running
cmd.exe always ending with a backslash which is not documented by Microsoft. I recommend not using this undocumented variable as there is no guarantee that a future version of
cmd.exe still has this variable.
__AppDir__ is on 64-bit Windows, for example,
%SystemRoot%\System32\ on 64-bit
%SystemRoot%\System32\cmd.exe currently running, or
%SystemRoot%\SysWOW64\ on 32-bit
%SystemRoot%\SysWOW64\cmd.exe currently running, or any other path on copying
cmd.exe to any other folder and starting this copy of
cmd.exe. There are some more undocumented dynamic variables listed on SS64 page How-to: Windows Environment Variables which should be used only with special care for the same reason.
The values of the most used dynamic variables are changed dynamically by the Windows command processor itself while the values of the environment variables change only if command SET is used during execution of a batch file to redefine an environment variable. This is an important difference between environment and dynamic variables.
4. Accessing the values of dynamic variables
Every environment variable can be deleted or redefined in the local environment of a processed batch file. There is no environment variable read-only.
cmd.exe contains internally in its code the file extension list
.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC which is used as value for
PATHEXT if the environment variable
PATHEXT does not exist in local environment variables list to be able to find nevertheless scripts and executables specified on command line or in a batch file without file extension. But
cmd.exe does not contain a list of folder paths as fallback list if the environment variable
PATH does not exist in local environment. So a batch file writer should be careful on modification of local environment variable
PATH for whatever reason.
The values of the dynamic variables are referenced like the values of the environment variables with
!variable! on enabled delayed expansion. But the Windows command processor always searches first in the current list of environment variables if there is a variable with specified name. Only if there is no environment variable with that name,
cmd searches next in its internal list of dynamic variables if there is one with the specified name.
The current value of a dynamic variable cannot be accessed anymore on definition of an environment variable with same name as a dynamic variable. For that reason a batch file writer should never use one of the dynamic variable names as name for an environment variable.
Here is a code which demonstrates what happens if a batch file writer has the really not good idea to define an environment variable with name
@echo off setlocal EnableExtensions DisableDelayedExpansion echo/ echo Define environment variable ERRORLEVEL with string value "014". echo/ set ERRORLEVEL=014 if %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12. if %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14. if errorlevel 0 ( if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0. ) else echo IF: ERRORLEVEL is less than 0. echo/ echo Delete the environment variable ERRORLEVEL. echo/ set ERRORLEVEL= if %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12. if %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14. if errorlevel 0 ( if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0. ) else echo IF: ERRORLEVEL is less than 0. echo/ echo In all test cases the value of dynamic variable ERRORLEVEL was 0. echo/ for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch :EndBatch endlocal
The output of this batch file is:
Define environment variable ERRORLEVEL with string value "014". EQU: ERRORLEVEL is 12. ==: ERRORLEVEL is 14. IF: ERRORLEVEL is 0. Delete the environment variable ERRORLEVEL. EQU: ERRORLEVEL is not 12. ==: ERRORLEVEL is not 14. IF: ERRORLEVEL is 0. In all test cases the value of dynamic variable ERRORLEVEL was 0.
It can be seen on debugging the batch file that the first IF condition
if %ERRORLEVEL% EQU 12 results in replacing
%ERRORLEVEL% by the string
014 because of there is the environment variable
ERRORLEVEL defined with this string value. The function wcstol is used by command IF because of operator
EQU to convert the string
014 being interpreted as octal number because of the leading
0 and the string
12 to 32-bit signed integer values and compare them on equality. Therefore the first condition is true.
The second IF condition
if %ERRORLEVEL% == 014 results also in replacing
%ERRORLEVEL% by the string
014 because of there is the environment variable
ERRORLEVEL defined with this string value. But now the function lstrcmpW is used by command IF because of operator
==. Therefore the second condition is also true.
The third IF condition uses the recommended syntax explained by help of command IF output on running
if /? in a command prompt window. It can be seen that using the recommended syntax results in evaluation of the value of the internal dynamic variable
ERRORLEVEL of Windows command processor even if an environment variable is defined with name
ERRORLEVEL. The recommended syntax to evaluate the exit code of a previously executed command or program works always and anywhere within a batch file as demonstrated here.
- What are the ERRORLEVEL values set by internal cmd.exe commands?
- Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Further it must be taken into account that accessing current value of a dynamic variable is always on variable reference being expanded by the Windows command processor and not real execution of the command or executable.
A code to demonstrate that difference:
@echo off setlocal EnableExtensions EnableDelayedExpansion for /L %%I in (1,1,3) do ( echo %%DATE%% %%TIME%% is: %DATE% %TIME% echo ^^!DATA^^! ^^!TIME^^! is: !DATE! !TIME! echo %%RANDOM%%/^^!RANDOM^^!: %RANDOM%/!RANDOM! if exist %SystemRoot%\System32\timeout.exe ( %SystemRoot%\System32\timeout.exe /T 3 /NOBREAK >nul ) else ( %SystemRoot%\System32\ping.exe 127.0.0.1 -n 4 >nul ) ) for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch :EndBatch endlocal
The output is for example:
%DATE% %TIME% is: 31.01.2021 13:54:30,67 !DATA! !TIME! is: 31.01.2021 13:54:30,68 %RANDOM%/!RANDOM!: 18841/27537 %DATE% %TIME% is: 31.01.2021 13:54:30,67 !DATA! !TIME! is: 31.01.2021 13:54:33,12 %RANDOM%/!RANDOM!: 18841/16705 %DATE% %TIME% is: 31.01.2021 13:54:30,67 !DATA! !TIME! is: 31.01.2021 13:54:36,16 %RANDOM%/!RANDOM!: 18841/32668
%DATE% %TIME% results in printing to console window three times the same date/time because of these two variable references are expanded already by the Windows command processor on parsing the entire command block before command FOR is executed at all.
%RANDOM% results in printing three times the same number for the same reason while
!RANDOM! prints usually three different numbers.
- How does the Windows Command Interpreter (CMD.EXE) parse scripts?
- Variables are not behaving as expected
if errorlevel number and
if not errorlevel number work also within a command block!
The dynamic environment variables can be accessed only with command extensions enabled as by default. Otherwise Windows command processor emulates
COMMAND.COM behavior (more or less) of MS-DOS and Windows 95/98/ME not supporting dynamic variables at all as demonstrated by this code:
@echo off setlocal DisableExtensions DisableDelayedExpansion echo/ echo With command extensions disabled: echo/ echo Date/time is: %DATE% %TIME% echo Current dir: "%CD%" endlocal setlocal EnableExtensions DisableDelayedExpansion echo/ echo With command extensions enabled: echo/ echo Date/time is: %DATE% %TIME% echo Current dir: "%CD%" echo/ for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch :EndBatch endlocal
The output is for example:
With command extensions disabled: Date/time is: Current dir: "" With command extensions enabled: Date/time is: 31.01.2021 14:17:42,92 Current dir: "C:\Temp\Development & Test!"
The values of dynamic variables can be only read. It is not possible to modify the value of a dynamic variable with command SET as it results in the definition of an environment variable with the name of a dynamic variable which takes precedence over the dynamic variable.