Vas*_*o.S 5 cmd environment-variables command-prompt
我正在阅读一篇关于ss64关于命令提示符中的环境变量的文章。
本文后面有一个表,其中列出了命令提示符中常见的环境变量。其中列出的一些变量被称为易失性(只读)。文章中的一句话指出:-
动态环境变量是只读的,并且每次扩展变量时都会进行计算。当所有变量都用 SET 列出时,这些变量将不会出现在列表中。不要尝试直接设置动态变量。
后两句话我明白了。但我无法理解第一个。
疑问:-
%userprofile%是非易失性变量,解析为%SystemDrive%\Users\{username},%homepath%是易失性变量,解析为Users\{Username}。这两个命令非常相似(除了systemdrive)。那为什么一个是易失性的,另一个是非易失性的呢?
变量动态的标准是什么?是什么使%appdata%(只是一个例子)成为非易失性变量?
每次扩展变量时都会计算动态变量,这对于诸如此类的变量是有意义的,%CD% %DATE% %TIME% %RANDOM%因为如果它们是非易失性的,它们就会失去其功能。但效果如何呢%homepath%?
一些非易失性变量中含有某种动态成分。前任。%userprofile%有%SystemDrive%并且{username}在它的路径中。那么这些变量怎么不是动态的呢?
存在三种类型的变量,其值可以使用语法%variable%或在 Windows 命令提示符窗口或批处理文件中!variable!使用EnableDelayedExpansion命令选项启用延迟环境变量扩展(即使用.setlocal%SystemRoot%\\System32\\cmd.exe
Windows 注册表中永久存储有环境变量。
\n用户变量存储在Windows注册表的键下:
\nHKEY_CURRENT_USER\\Environment\nRun Code Online (Sandbox Code Playgroud)\n系统变量存储在Windows注册表的键下:
\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\nRun Code Online (Sandbox Code Playgroud)\n用户变量仅为存储用户注册表配置单元的帐户(文件)%UserProfile%\\ntuser.dat定义。系统变量是为 Windows 计算机上使用的所有帐户定义的(文件%SystemRoot%\\System32\\config\\SYSTEM)。
通过打开 Windows控制面板,单击“系统” ,单击“高级系统设置”上的“下一步”(左侧),然后单击“环境变量”按钮,可以查看、编辑和删除持久存储的变量。上半部分是当前用户帐户的用户变量,下半部分是Windows XP以来的系统变量。
\n从 Windows XP开始,默认情况下仅定义为用户变量。TEMPTMP
从 Windows XP 开始,预定义的系统变量列表如下:
\nComSpec\nNUMBER_OF_PROCESSORS\nOS\nPATH\nPATHEXT\nPROCESSOR_ARCHITECTURE\nPROCESSOR_IDENTIFIER\nPROCESSOR_LEVEL\nPROCESSOR_REVISION\nTEMP\nTMP\nwindir\nRun Code Online (Sandbox Code Playgroud)\n在 Windows Vista 和较新的 Windows 版本上也默认定义了系统变量PSModulePath。
除了和之外,任何预定义的系统变量都不应被删除或修改,因为这可能会导致很多麻烦,甚至可能导致 Windows 不再启动。我强烈建议使用虚拟机来试验预定义的系统变量,在开始实验之前,存在整个虚拟机映像的备份。PATHPATHEXT
建议在使用用户和系统变量之前先对其进行备份,方法是打开命令提示符窗口并运行以下命令:
\nmd C:\\VariablesBackup 2>nul\n%SystemRoot%\\System32\\reg.exe EXPORT HKCU\\Environment "C:\\VariablesBackup\\UserVariables.reg"\n%SystemRoot%\\System32\\reg.exe EXPORT "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" "C:\\VariablesBackup\\SystemVariables.reg"\nRun Code Online (Sandbox Code Playgroud)\n可以在之前进行备份的命令提示符窗口中恢复用户变量:
\n%SystemRoot%\\System32\\reg.exe DELETE "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" /f\n%SystemRoot%\\System32\\reg.exe IMPORT "C:\\VariablesBackup\\UserVariables.reg"\nRun Code Online (Sandbox Code Playgroud)\n可以在之前进行过备份的情况下以管理员身份打开的命令提示符窗口中恢复系统变量:
\n%SystemRoot%\\System32\\reg.exe DELETE HKCU\\Environment /f\n%SystemRoot%\\System32\\reg.exe IMPORT "C:\\VariablesBackup\\SystemVariables.reg"\nRun Code Online (Sandbox Code Playgroud)\n建议在从备份恢复用户和系统变量后重新启动 Windows,以确保所有进程确实都使用恢复的变量。
\nPATH1.3用批处理文件修改考虑使用批处理文件修改用户或系统 PATH的批处理文件程序员应该首先阅读:
注意:绝对不能使用命令SETX在%PATH%批处理文件中命令来修改用户或系统 PATH变量。
修改用户或系统的唯一原因 PATH的唯一原因是该程序主要供用户从 Windows 命令行使用。如果一个程序需要其目录或其子目录之一才能PATH工作,那么它的设计就很糟糕。如果一个程序在Windows默认定义的文件夹路径中添加了系统 左侧的\xc2\xa0文件夹路径,那么该程序的设计就非常糟糕。PATH
系统变量应始终以以下 PATH形式开头:
%SystemRoot%\\System32;%SystemRoot%;%SystemRoot%\\System32\\Wbem\nRun Code Online (Sandbox Code Playgroud)\nWindows系统目录是包含大多数可执行文件和动态链接库的目录。因此,它应该始终是当前目录之后搜索可执行文件和库的第一个目录。
\n还有更多预定义的 Windows环境变量,可以在以下位置看到:
\n这些变量由 Windows shell 定义,默认情况下,explorer.exeWindows shell 根据注册表项下的注册表值作为 Windows shell 启动Shell:
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\nRun Code Online (Sandbox Code Playgroud)\n用户最注意到的Window shell元素是 Windows 桌面、Windows 开始菜单和带有系统托盘的 Windows 任务栏。
\nWindows shell 在其内存中定义了大量环境变量,具体取决于当前用户帐户的 Windows 注册表中的各种值,而不是如上所述永久存储在 Windows 注册表中。每当创建新进程(例如从 Windows shell 启动可执行文件)时,都会复制当前的环境变量列表。
\nWindows shell 定义的环境变量列表由持久存储的用户和系统变量以及当前用户帐户的shell变量组成,可以通过打开命令提示符窗口并运行不带任何其他参数的命令SET来查看。
\n大多数 shell 变量都是从下面的注册表字符串定义的
\nHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell FoldersHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell FoldersHKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell FoldersHKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders大多数注册表字符串值存在于 of User Shell FolderstypeREG_EXPAND_SZ和 in Shell Foldersof type 中REG_SZ。但有些注册表字符串值仅存在于两个注册表项之一下。
另请参阅:如何在用户的桌面目录中创建目录?
\n在此答案中详细解释了 Windows 资源管理器如何评估这些注册表字符串值,并通过用户使用regedit.exe或reg.exeshell 文件夹的示例进行手动修改来处理它们Desktop。
函数CreateEnvironmentBlock和私有 shell32 函数与GetUserNameExW和GetComputerNameExWRegenerateUserEnvironment一起使用,以创建 Windows 资源管理器在使用 Windows 内核库函数CreateProcess从 Windows shell 启动可执行文件时复制的环境变量列表。explorer.exe
另请参阅Eryk Sun对问题cmd.exe 的环境变量存储在哪里?
\n64 位 Windows 上有一些环境变量取决于启动 64 位或 32 位可执行文件。Microsoft 在WOW64 实现详细信息中记录了它们,如下所示:
\n\n\n64位进程:
\nPROCESSOR_ARCHITECTURE=AMD64或PROCESSOR_ARCHITECTURE=IA64或PROCESSOR_ARCHITECTURE=ARM64
\n
\nProgramFiles=%ProgramFiles%
\nProgramW6432=%ProgramFiles%
\nCommonProgramFiles=%CommonProgramFiles%
\nCommonProgramW6432=%CommonProgramFiles%Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP:从 Windows 7 和 Windows Server 2008 R2 开始添加了 ProgramW6432 和 CommonProgramW6432 环境变量。
\n32位进程:
\nPROCESSOR_ARCHITECTURE=x86
\n
\nPROCESSOR_ARCHITEW6432=%PROCESSOR_ARCHITECTURE%
\nProgramFiles=%ProgramFiles(x86)%
\nProgramW6432=%ProgramFiles%
\nCommonProgramFiles=%CommonProgramFiles(x86)%
\nCommonProgramW6432=%CommonProgramFiles%
环境变量的值PROCESSOR_ARCHITECTURE不能用于从批处理文件中查找所安装的 Windows 是 32 位 (x86) 还是 64 位 (AMD64) Windows。该值取决于 64 位或64 位 Windows 上的%SystemRoot%\\Sysem32\\cmd.exe32 位批处理文件的处理。%SystemRoot%\\SysWOW64\\cmd.exe
另请参阅 Microsoft 文档:
\n\n在编写批处理文件时必须明智地使用Windows shell 定义的环境变量,该批处理文件应设计为由相同或不同 Windows 计算机上的其他帐户执行。由于环境变量列表的差异,许多批处理文件在批处理文件作者的环境中工作正常,但在使用系统帐户或在不同计算机上运行与计划任务相同的批处理文件所设置的环境中却无法工作。
\n进程在使用 Windows 内核库函数启动可执行文件时定义的CreateProcess环境变量决定了启动的可执行文件可以使用的环境变量。
大多数应用程序使用参数的CreateProcess值。因此,复制当前进程的当前环境变量。因此,从 Windows 桌面、开始菜单或任务栏启动的每个可执行文件都会获取由作为 Windows shell 运行的实例定义的环境变量。nulllpEnvironmentCreateProcessexplorer.exe
使用在 Windows 上默认定义的环境变量的真正非常好的编码可执行文件或脚本,明确验证每个使用的环境变量是否确实定义并使用,否则未定义合适的默认值,例如C:\\Windows环境变量,SystemRoot并检查是否确实存在目录C:\\Windows并退出,并在造成可能损坏之前未定义的重要环境变量上显示适当的错误消息。
SystemRoot是由环境变量定义的 Windows shell 变量的示例,explorer.exe该变量不是由 shell 文件夹的注册表字符串值确定的。某些环境变量值不应由用户在任何时候独立于其真实源进行修改,脚本作者不需要知道这些值是 Windows 实现细节。
在命令提示符窗口中运行时,命令SET输出的帮助中列出了一些变量,set /?这些变量在运行时的环境变量列表中找不到set。
这些变量是:
\nCD\nDATE\nTIME\nRANDOM\nERRORLEVEL\nCMDEXTVERSION\nCMDCMDLINE\nHIGHESTNUMANODENUMBER\nRun Code Online (Sandbox Code Playgroud)\n动态变量是的内部变量cmd.exe。因此,这些变量仅在 Windows 命令提示符窗口中可用,该窗口是正在运行的cmd.exe进程或由cmd.exe. 动态变量在其他可执行文件或脚本中不可用,因为这些变量不是环境变量。
最常用的动态变量是:
\n还有一些更多的动态变量,但它们很少在批处理文件中使用。
\n另外还有一个变量__AppDir__包含当前运行的路径,cmd.exe总是以反斜杠结尾,微软没有对此进行记录。我建议不要使用这个未记录的变量,因为不能保证未来版本cmd.exe仍然具有这个变量。__AppDir__在 64 位 Windows 上,例如,在当前运行的%SystemRoot%\\System32\\64 位上%SystemRoot%\\System32\\cmd.exe,或在当前运行的%SystemRoot%\\SysWOW64\\32 位上%SystemRoot%\\SysWOW64\\cmd.exe,或复制cmd.exe到任何其他文件夹并启动此cmd.exe. SS64 页面操作方法:Windows 环境变量中列出了更多未记录的动态变量,出于同样的原因,应特别小心地使用这些变量。
最常用的动态变量的值由 Windows 命令处理器本身动态更改,而只有在执行批处理文件期间使用命令SET来重新定义环境变量时,环境变量的值才会更改。这是环境变量和动态变量之间的重要区别。
\n每个环境变量都可以在已处理的批处理文件的本地环境中删除或重新定义。没有只读环境变量。
\ncmd.exe在其代码内部包含文件扩展名列表,如果本地环境变量列表中不存在环境变量,则该文件扩展名列表.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC将用作值,以便能够找到在命令行或批处理文件中指定的没有文件扩展名的脚本和可执行文件。但如果本地环境中不存在环境变量,则不包含文件夹路径列表作为后备列表。因此,无论出于何种原因,批处理文件编写者都应该小心修改本地环境变量。PATHEXTPATHEXTcmd.exePATHPATH
动态变量的值的引用类似于启用延迟扩展的环境变量的值。但是,如果存在具有指定名称的变量, Windows 命令处理器始终首先在当前环境变量列表中搜索。仅当不存在具有该名称的环境变量时,才会在其内部动态变量列表中搜索下一个(如果存在具有指定名称的环境变量)。%variable%!variable!cmd
在定义与动态变量同名的环境变量时,无法再访问动态变量的当前值。因此,批处理文件编写器不应使用动态变量名称之一作为环境变量的名称。
\n下面的代码演示了如果批处理文件编写器确实没有好主意定义一个名为 name 的环境ERRORLEVEL变量,会发生什么情况。
@echo off\nsetlocal EnableExtensions DisableDelayedExpansion\necho/\necho Define environment variable ERRORLEVEL with string value "014".\necho/\nset ERRORLEVEL=014\nif %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12.\nif %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14.\nif errorlevel 0 (\n if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0.\n) else echo IF: ERRORLEVEL is less than 0.\necho/\necho Delete the environment variable ERRORLEVEL.\necho/\nset ERRORLEVEL=\nif %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12.\nif %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14.\nif errorlevel 0 (\n if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0.\n) else echo IF: ERRORLEVEL is less than 0.\necho/\necho In all test cases the value of dynamic variable ERRORLEVEL was 0.\necho/\nfor %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch\n:EndBatch\nendlocal\nRun Code Online (Sandbox Code Playgroud)\n该批处理文件的输出是:
\nDefine environment variable ERRORLEVEL with string value "014".\n\nEQU: ERRORLEVEL is 12.\n ==: ERRORLEVEL is 14.\n IF: ERRORLEVEL is 0.\n\nDelete the environment variable ERRORLEVEL.\n\nEQU: ERRORLEVEL is not 12.\n ==: ERRORLEVEL is not 14.\n IF: ERRORLEVEL is 0.\n\nIn all test cases the value of dynamic variable ERRORLEVEL was 0.\nRun Code Online (Sandbox Code Playgroud)\n在调试批处理文件时可以看到,第一个IF条件if %ERRORLEVEL% EQU 12导致替换%ERRORLEVEL%为字符串014,因为存在使用该字符串值定义的环境变量ERRORLEVEL。命令IF使用函数wcstol来将由于前导而被解释为八进制数的字符串转换为 32 位带符号整数值,并比较它们是否相等。因此第一个条件为真。EQU014012
第二个IF条件if %ERRORLEVEL% == 014也会导致替换%ERRORLEVEL%为字符串014,因为存在使用该字符串值定义的环境变量ERRORLEVEL。但现在由于运算符的原因,函数lstrcmpW被命令IF使用==。因此第二个条件也成立。
第三个IF条件使用在命令提示符窗口中运行时通过命令IF输出的帮助解释的推荐语法。if /?可以看出,即使使用name 定义了环境变量,使用推荐的语法也会导致对 Windows 命令处理器的内部动态变量的值进行评估。用于评估先前执行的命令或程序的退出代码的推荐语法始终适用于批处理文件中的任何位置,如此处所示。ERRORLEVELERRORLEVEL
也可以看看:
\n\n此外,必须考虑到访问动态变量的当前值始终是由 Windows 命令处理器扩展的变量引用,而不是命令或可执行文件的实际执行。
\n演示这种差异的代码:
\n@echo off\nsetlocal EnableExtensions EnableDelayedExpansion\nfor /L %%I in (1,1,3) do (\n echo %%DATE%% %%TIME%% is: %DATE% %TIME%\n echo ^^!DATA^^! ^^!TIME^^! is: !DATE! !TIME!\n echo %%RANDOM%%/^^!RANDOM^^!: %RANDOM%/!RANDOM!\n if exist %SystemRoot%\\System32\\timeout.exe (\n %SystemRoot%\\System32\\timeout.exe /T 3 /NOBREAK >nul\n ) else (\n %SystemRoot%\\System32\\ping.exe 127.0.0.1 -n 4 >nul\n )\n)\nfor %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch\n:EndBatch\nendlocal\nRun Code Online (Sandbox Code Playgroud)\n输出例如:
\n%DATE% %TIME% is: 31.01.2021 13:54:30,67\n!DATA! !TIME! is: 31.01.2021 13:54:30,68\n%RANDOM%/!RANDOM!: 18841/27537\n%DATE% %TIME% is: 31.01.2021 13:54:30,67\n!DATA! !TIME! is: 31.01.2021 13:54:33,12\n%RANDOM%/!RANDOM!: 18841/16705\n%DATE% %TIME% is: 31.01.2021 13:54:30,67\n!DATA! !TIME! is: 31.01.2021 13:54:36,16\n%RANDOM%/!RANDOM!: 18841/32668\nRun Code Online (Sandbox Code Playgroud)\n%DATE% %TIME%结果会打印到控制台窗口三倍相同的日期/时间,因为在执行命令FOR之前,Windows 命令处理器在解析整个命令块时已经扩展了这两个变量引用。%RANDOM%由于相同的原因,结果会打印相同数字的三倍,而!RANDOM!通常会打印三个不同的数字。
也可以看看:
\n\nif errorlevel number并且if not errorlevel number也可以在命令块内工作!
默认情况下,只有在启用命令扩展的情况下才能访问动态环境变量。否则,Windows 命令处理器会COMMAND.COM(或多或少)模拟 MS-DOS 和 Windows 95/98/ME 的行为,根本不支持动态变量,如以下代码所示:
@echo off\nsetlocal DisableExtensions DisableDelayedExpansion\necho/\necho With command extensions disabled:\necho/\necho Date/time is: %DATE% %TIME%\necho Current dir: "%CD%"\nendlocal\nsetlocal EnableExtensions DisableDelayedExpansion\necho/\necho With command extensions enabled:\necho/\necho Date/time is: %DATE% %TIME%\necho Current dir: "%CD%"\necho/\nfor %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch\n:EndBatch\nendlocal\nRun Code Online (Sandbox Code Playgroud)\n输出例如:
\nWith command extensions disabled:\n\nDate/time is:\nCurrent dir: ""\n\nWith command extensions enabled:\n\nDate/time is: 31.01.2021 14:17:42,92\nCurrent dir: "C:\\Temp\\Development & Test!"\nRun Code Online (Sandbox Code Playgroud)\n动态变量的值只能读取。无法使用命令SET修改动态变量的值,因为它会导致使用\xc2\xa0 定义环境变量,其名称为\xc2\xa0a\xc2\xa0动态变量,该变量优先于动态变量。
\n| 归档时间: |
|
| 查看次数: |
4431 次 |
| 最近记录: |