Difference between Dynamic variables and Environment variables in CMD
Asked Answered
U

1

6

I was reading an article on ss64 about environment variables in Windows command prompt.

Later in the article there exists a table which states environment variables found commonly in Command Prompt. Some of the variables listed there are termed as Volatile (Read Only). A sentence found in the article states:

Dynamic environment variables are read-only and are computed each time the variable is expanded. When all variables are listed with SET, these will not appear in the list. Do not attempt to directly SET a dynamic variable.

I understood the later two statements. But I can't understand the first one.

Doubts:

  • %userprofile% is an non-volatile variable, which resolves to %SystemDrive%\Users\{username}, and %homepath% is a volatile variable which resolves to Users\{Username}. Both commands are quite similar (except for the systemdrive). Then why is one volatile and the other one non-volatile?

  • What is the criteria for a variable to be dynamic? What makes %appdata% (just an ex.) a non volatile variable?

  • Dynamic variables are computed each time the variable is expanded, this makes sense for variables like %CD% %DATE% %TIME% %RANDOM% etc. as they would lose their functionality if they would be non-volatile. But how would it effect %homepath%?

  • Some non-volatile variables have sort-of dynamic components in them. Ex. %userprofile% has %SystemDrive% and {username} in it's path. Then how are those variables not dynamic?

Ultrasound answered 14/4, 2020 at 11:52 Comment(0)
R
9

There are three types of variables of which value is accessed using the syntax %variable% or the syntax !variable! on having enabled delayed variable expansion with using the cmd option /V:ON on starting cmd.exe or the option EnableDelayedExpansion of the cmd internal command setlocal in a batch file, i.e. on using %SystemRoot%\System32\cmd.exe.

1. Persistent stored variables

There are environment variables stored persistent in Windows registry.

  1. User variables stored in Windows registry under the key:

    HKEY_CURRENT_USER\Environment
    
  2. System variables stored in Windows registry under the key:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
    

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 %SystemRoot%\System32\config\SYSTEM).

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 TEMP and TMP since Windows XP. The newer versions of Windows 10 and all versions of Windows 11 define also a user variable Path with %USERPROFILE%\AppData\Local\Microsoft\WindowsApps as value by default.

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 PSModulePath.

None of the predefined system variables with exception of PATH and 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:

ATTENTION: It is an absolute NO GO - NEVER EVER to use command SETX with %PATH% in a batch file to modify user or system PATH variable.

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.

The system PATH variable should start always with:

%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\Wbem

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:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon

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\Shell Folders
  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\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 regedit.exe or reg.exe on the example of the shell folder Desktop.

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 in the WOW64 Implementation Details as follows:

64-bit process:

PROCESSOR_ARCHITECTURE=AMD64 or PROCESSOR_ARCHITECTURE=IA64 or PROCESSOR_ARCHITECTURE=ARM64
ProgramFiles=%ProgramFiles%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles%
CommonProgramW6432=%CommonProgramFiles%

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.

32-bit process:

PROCESSOR_ARCHITECTURE=x86
PROCESSOR_ARCHITEW6432=%PROCESSOR_ARCHITECTURE%
ProgramFiles=%ProgramFiles(x86)%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles(x86)%
CommonProgramW6432=%CommonProgramFiles%

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 lpEnvironment. Therefore 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 set.

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:

  1. CD
    The current directory path not ending with a backlash, except the current directory is the root directory of the drive.
  2. DATE
    The current local date in format as defined for the account in region and language settings of Windows.
  3. TIME
    The current local time in format as defined for the account in region and language settings of Windows.
  4. ERRORLEVEL
    Exit value of previously executed command or program.
  5. RANDOM
    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% or !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 ERRORLEVEL.

@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.

See also:

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.

See also:

if errorlevel number and if not errorlevel number work also within a command block!

The dynamic variables can be accessed with using %variable% or !variable! 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 with exception of ERRORLEVEL and using the recommend IF syntax:

@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.

Rosenbaum answered 31/1, 2021 at 13:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.