【问题标题】:Difference between Dynamic Environment Variables and Normal Environment Variables in CMDCMD中动态环境变量和普通环境变量的区别
【发布时间】:2020-04-14 11:52:32
【问题描述】:

我正在阅读ss64 上关于命令提示符中的环境变量的文章。

本文后面有一个,它说明了命令提示符中常见的环境变量。那里列出的一些变量被称为Volatile(只读)。文章中找到一句话:-

动态环境变量是只读的,每次扩展变量时都会计算。当所有变量都用 SET 列出时,这些变量不会出现在列表中。不要尝试直接设置动态变量。

我理解后面的两个陈述。但是第一个看不懂。

疑问:-

  • %userprofile% 是一个 non-volatile 变量,解析为 %SystemDrive%\Users\{username}%homepath% 是一个 volatile 变量,解析为 @987654325 @。这两个命令非常相似(systemdrive 除外)。那为什么一个是易失的,另一个是非易失的呢?

  • 动态变量的标准是什么?是什么让%appdata%(只是一个例子)成为非易失性变量?

  • 每次扩展变量时都会计算动态变量,这对于 %CD% %DATE% %TIME% %RANDOM% 等变量是有意义的,因为如果它们是非易失性,它们将失去其功能。但它会如何影响%homepath%

  • 一些非易失性变量在其中具有某种动态成分。前任。 %userprofile% 的路径中有 %SystemDrive%{username}。那这些变量怎么不是动态的呢?

【问题讨论】:

    标签: cmd environment-variables command-prompt


    【解决方案1】:

    在 Windows 命令提示符下使用命令 setlocal 的选项 EnableDelayedExpansion 启用延迟环境变量扩展时,可以使用语法 %variable%!variable! 访问三种类型的变量的值窗口或批处理文件,即使用%SystemRoot%\System32\cmd.exe

    1。持久存储变量

    环境变量永久存储在 Windows 注册表中。

    1. 用户变量存储在 Windows 注册表项下:

      HKEY_CURRENT_USER\Environment
      
    2. 系统变量存储在Windows注册表项下:

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

    user 变量仅针对存储它们的用户注册表配置单元的帐户定义(文件%UserProfile%\ntuser.dat)。 system 变量是为 Windows 机器上使用的所有帐户定义的(文件 %SystemRoot%\System32\config\SYSTEM)。

    可以通过打开Windows 控制面板,点击系统,点击上的下一步(左侧)来查看、编辑和删除永久存储的变量高级系统设置并点击按钮环境变量。上半部分用于当前用户帐户的 user 变量,下半部分用于从 Windows XP 开始的 system 变量。

    自 Windows XP 起,默认情况下定义为 user 变量只有 TEMPTMP

    预定义的系统变量列表是从 Windows XP 开始的:

    ComSpec
    NUMBER_OF_PROCESSORS
    OS
    PATH
    PATHEXT
    PROCESSOR_ARCHITECTURE
    PROCESSOR_IDENTIFIER
    PROCESSOR_LEVEL
    PROCESSOR_REVISION
    TEMP
    TMP
    windir
    

    在 Windows Vista 和较新的 Windows 版本上默认定义了 system 变量 PSModulePath

    除了PATHPATHEXT 之外,任何预定义的system 变量都不应被删除或修改,因为这可能会导致很多麻烦,甚至可能导致Windows 不再启动。我强烈建议使用虚拟机对预定义的系统变量进行试验,在开始试验之前,该变量存在整个虚拟机映像的备份。

    1.1 持久化存储变量的备份

    建议先备份 usersystem 变量,然后打开命令提示符窗口并运行以下命令:

    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 持久化存储变量的恢复

    恢复用户变量可以在之前备份的命令提示符窗口中完成:

    %SystemRoot%\System32\reg.exe DELETE "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /f
    %SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\UserVariables.reg"
    

    恢复系统变量可以在以管理员身份打开的命令提示符窗口中完成,该窗口之前已进行备份:

    %SystemRoot%\System32\reg.exe DELETE HKCU\Environment /f
    %SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\SystemVariables.reg"
    

    建议在从备份中恢复 usersystem 变量后重新启动 Windows,以确保所有进程都使用恢复的变量。

    1.3 用批处理文件修改PATH

    考虑使用批处理文件修改用户系统PATH的批处理文件程序员应该首先阅读:

    注意: 在批处理文件中使用命令 SETX%PATH% 来修改是绝对的NO GO - NEVER EVER strong>user 或 system PATH 变量。

    在安装程序(可执行文件或脚本)时使用批处理文件修改 usersystem PATH 的唯一原因是该程序设计用于主要由 Windows 命令行的用户使用。如果程序要求其目录或其子目录之一位于PATH 中才能正常工作,则该程序设计不佳。如果一个程序将 system PATH 的文件夹路径添加到 Windows 默认定义的文件夹路径中,则该程序设计得很糟糕。

    system PATH 变量应始终以:

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

    Windows 系统目录是包含大多数可执行文件和动态链接库的目录。因此,它应该始终是在当前目录之后搜索可执行文件和库的第一个目录。

    2。 Windows shell 变量

    还有很多预定义的 Windows 环境变量,如下所示:

    这些变量由 Windows shell 定义,默认情况下在 Windows 上启动 explorer.exe 根据注册表项下的注册表值 Shell 作为 Windows shell 启动:

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

    用户最关注的Window shell 元素是Windows 桌面、Windows 开始菜单和带有系统托盘的Windows 任务栏。

    Windows shell 在其内存中定义了许多环境变量,这些环境变量取决于 Windows 注册表中的各种值,用于当前用户帐户,而不是如上所述永久存储在 Windows 注册表中。每当创建新进程(例如从 Windows shell 启动可执行文件)时,都会复制当前的环境变量列表。

    由 Windows shell 定义的环境变量列表,包括持久存储的 usersystem 变量以及当前用户帐户的 shell 变量可以通过打开命令提示符窗口并运行命令 SET 来查看,而无需任何附加参数。

    大部分 shell 变量都是从下面的注册表字符串定义的

    • 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

    大多数注册表字符串值存在于REG_EXPAND_SZ 类型的User Shell FoldersShell Folders 类型REG_SZ 中。但是有一些注册表字符串值只存在于两个注册表项之一下。

    另请参阅:How to create a directory in the user's desktop directory?
    在这个答案中详细解释了 Windows 资源管理器如何评估这些注册表字符串值,并在用户使用 regedit.exereg.exe 以 shell 文件夹 Desktop 的示例进行手动修改时处理它们。

    explorer.exeGetUserNameExWGetComputerNameExW 使用函数CreateEnvironmentBlock 和私有shell32 函数RegenerateUserEnvironment 来创建Windows Explorer 在使用Windows 从Windows shell 启动可执行文件时复制的环境变量列表内核库函数CreateProcess.

    另见Eryk Sun就问题Where are the environment variables for cmd.exe stored?写的cmets

    64 位 Windows 上的一些环境变量依赖于启动 64 位或 32 位可执行文件。微软在WOW64 Implementation Details 上记录了它们,如下所示:

    64 位进程:

    PROCESSOR_ARCHITECTURE=AMD64 PROCESSOR_ARCHITECTURE=IA64 PROCESSOR_ARCHITECTURE=ARM64
    程序文件=%程序文件%
    ProgramW6432=%ProgramFiles%
    CommonProgramFiles=%CommonProgramFiles%
    CommonProgramW6432=%CommonProgramFiles%

    Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP:从 Windows 7 和 Windows Server 2008 R2 开始添加了 ProgramW6432 和 CommonProgramW6432 环境变量。

    32 位进程:

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

    环境变量PROCESSOR_ARCHITECTURE的值不能用于从批处理文件中找出安装的Windows是32位(x86)还是64位(AMD64)视窗。该值取决于 64 位 %SystemRoot%\Sysem32\cmd.exe 或 32 位 %SystemRoot%\SysWOW64\cmd.exe 在 64 位 Windows 上对批处理文件的处理。

    另请参阅 Microsoft 文档:

    在编写批处理文件时必须明智地使用由 Windows shell 定义的 环境 变量,该批处理文件应设计为由相同或不同 Windows 机器上的其他帐户执行。由于环境的差异,许多批处理文件在批处理文件作者的环境中运行良好,但在设置为使用系统帐户运行与计划任务相同的批处理文件或在不同机器上运行相同批处理文件的环境中无法正常工作变量列表。

    进程在使用 Windows 内核库函数CreateProcess 启动可执行文件时定义的环境变量决定了启动的可执行文件可以使用的环境变量。

    大多数应用程序使用CreateProcess,参数lpEnvironment 的值为null。因此CreateProcess 会复制当前进程的当前环境变量。因此,每个从 Windows 桌面、开始菜单或任务栏启动的可执行文件都会获取由作为 Windows shell 运行的explorer.exe 实例定义的环境变量。

    使用在 Windows 上默认定义的环境变量的非常好的编码可执行文件或脚本,明确验证每个使用的环境变量都已真正定义,否则在环境变量 SystemRoot 上使用合适的默认值,如 C:\Windows 不是通过检查是否确实存在目录 C:\Windows 并在可能造成损坏之前未定义的重要环境变量上以适当的错误消息退出。

    SystemRoot 是由explorer.exe 定义为环境变量的 Windows shell 变量的示例,该变量不是由 shell 文件夹的注册表字符串值确定的。某些环境变量值不应由用户在任何时候独立于其真实来源进行修改,任何脚本作者都不需要知道作为 Windows 实现细节。

    3。 Windows 命令处理器的动态变量

    在命令提示符窗口set /? 中运行时命令SET 输出的帮助中列出了一些变量,这些变量在运行时的环境变量列表中找不到set

    这些变量是:

    CD
    DATE
    TIME
    RANDOM
    ERRORLEVEL
    CMDEXTVERSION
    CMDCMDLINE
    HIGHESTNUMANODENUMBER
    

    动态变量是cmd.exe内部变量。因此,这些变量仅在 Windows 命令提示符窗口中可用,该窗口是正在运行的cmd.exe 进程或由cmd.exe 处理的批处理文件。 动态变量在其他可执行文件或脚本中不可用,因为这些变量不是环境变量。

    最常用的动态变量有:

    1. CD
      当前目录路径不以反斜杠结尾,但当前目录是驱动器的根目录。
    2. 日期
      当前本地日期,格式为在 Windows 的区域和语言设置中为帐户定义的格式。
    3. 时间
      当前本地时间,格式为在 Windows 的区域和语言设置中为帐户定义的格式。
    4. 错误级别
      先前执行的命令或程序的退出值。
    5. 随机
      0 到 32767 之间的随机十进制数。

    还有一些动态变量,但它们很少在批处理文件中使用。

    还有一个变量__AppDir__ 包含当前运行的cmd.exe 的路径,总是以反斜杠结尾,Microsoft 没有记录。我建议不要使用这个 undocumented 变量,因为不能保证 cmd.exe 的未来版本仍然有这个变量。 __AppDir__ 在 64 位 Windows 上,例如,%SystemRoot%\System32\ 在 64 位上 %SystemRoot%\System32\cmd.exe 当前正在运行,或 %SystemRoot%\SysWOW64\ 在 32 位上 %SystemRoot%\SysWOW64\cmd.exe 当前正在运行,或将 cmd.exe 复制到的任何其他路径任何其他文件夹并启动此 cmd.exe 副本。 SS64 页面How-to: Windows Environment Variables 上列出了更多未记录的动态变量,出于同样的原因,应特别小心使用。

    最常用的动态变量的值由Windows命令处理器本身动态改变,而环境变量的值只有在执行期间使用SET命令时才会改变的批处理文件以重新定义 环境 变量。这是环境动态变量之间的重要区别。

    4。访问动态变量的值

    每个环境变量都可以在已处理批处理文件的本地环境中删除或重新定义。没有只读的环境变量。

    cmd.exe 在其代码内部包含文件扩展名列表 .COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC,如果 环境 变量 PATHEXTlocalPATHEXT 的值/strong> 环境变量列表,以便能够找到在命令行或没有文件扩展名的批处理文件中指定的脚本和可执行文件。但如果 环境 变量 PATHlocal 环境中不存在,cmd.exe 不包含文件夹路径列表作为后备列表。因此,无论出于何种原因,批处理文件编写器都应该小心修改 本地环境 变量 PATH

    dynamic 变量的值与启用delayed expansion%variable%!variable!环境 变量的值一样被引用。但是,如果存在具有指定名称的变量,Windows 命令处理器总是首先在 环境 变量的当前列表中搜索。仅当不存在具有该名称的 环境 变量时,cmd 才会在其内部动态变量列表中搜索下一个(如果存在具有指定名称的变量)。

    dynamic 变量的当前值无法再在定义与 dynamic 变量同名的 environment 变量时访问。出于这个原因,批处理文件编写器不应使用 动态 变量名称之一作为 环境 变量的名称。

    下面的代码演示了如果批处理文件编写器确实不太好定义名称为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
    

    这个批处理文件的输出是:

    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.
    

    debugging the batch file 上可以看出,第一个 IF 条件if %ERRORLEVEL% EQU 12 导致将%ERRORLEVEL% 替换为字符串014,因为存在环境 变量ERRORLEVEL 使用此字符串值定义。命令 IF 使用函数 wcstol 因为运算符 EQU 将字符串 014 转换为八进制数,因为前导 0 和字符串 12 到 32 -bit 有符号整数值并比较它们是否相等。因此第一个条件为真。

    第二个 IF 条件 if %ERRORLEVEL% == 014 也导致将 %ERRORLEVEL% 替换为字符串 014,因为有 environment 变量 ERRORLEVEL 用这个定义字符串值。但是现在函数lstrcmpW被命令IF使用,因为操作符==。因此第二个条件也成立。

    第三个 IF 条件使用在命令提示符窗口中运行 if /? 时命令 IF 输出的帮助解释的推荐语法。可以看出,即使 environment 变量定义为 name ERRORLEVEL。评估先前执行的命令或程序的退出代码的推荐语法始终在批处理文件中的任何位置有效,如此处所示。

    另见:

    还必须考虑到,访问 动态 变量的当前值总是在由 Windows 命令处理器扩展的变量引用上,而不是真正执行命令或可执行文件。

    演示这种差异的代码:

    @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
    

    输出例如:

    %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% 导致打印到控制台窗口三倍于相同的日期/时间,因为在命令 FOR 之前,Windows 命令处理器在解析整个命令块时已经扩展了这两个变量引用完全执行。 %RANDOM% 会出于同样的原因打印三个相同的数字,而!RANDOM! 通常会打印三个不同的数字。

    另见:

    if errorlevel numberif not errorlevel number 也可以在命令块中工作!

    动态环境变量只能在默认启用命令扩展的情况下访问。否则,Windows 命令处理器会模拟 MS-DOS 和 Windows 95/98/ME 的 COMMAND.COM 行为(或多或少),根本不支持 dynamic 变量,如下代码所示:

    @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
    

    输出例如:

    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!"
    

    动态变量的值只能读取。无法使用命令 SET 修改 dynamic 变量的值,因为这会导致定义名为的 environment 变量dynamic 变量优先于 dynamic 变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-30
      • 1970-01-01
      • 2020-02-13
      • 2011-03-21
      • 1970-01-01
      • 2013-05-07
      相关资源
      最近更新 更多