A) Windows命令处理器如何搜索命令?
Windows命令处理器搜索要执行的
命令,该命令满足以下条件:
1. 不是
cmd.exe
的内部命令
且
2. 只是指定了文件名,没有文件扩展名和路径
搜索匹配模式
command.*
的文件,且该文件的扩展名在本地环境变量
PATHEXT
中列出。
首先在当前目录中搜索
然后在本地环境变量
PATH
所列出的所有目录中搜索。
SORT、FIND、FINDSTR、ROBOCOPY、XCOPY等命令都不是cmd.exe
的内部命令。它们是随Windows安装的控制台应用程序,位于目录%SystemRoot%\System32
中,文件名分别为sort.exe
、find.exe
、findstr.exe
、robocopy.exe
、xcopy.exe
等。
这些默认在Windows上可用的控制台应用程序被称为外部命令,以便更好地区分它们与未随Windows操作系统安装的控制台应用程序。
B) 环境变量 PATH 是如何定义的?
有三种类型的 PATH 变量:
系统 PATH
是用于所有账户的,存储在Windows注册表的以下键值中:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
用户 PATH
仅用于当前账户,存储在Windows注册表的以下键值中:
HKEY_CURRENT_USER\Environment
本地 PATH
总是当前进程的父进程的本地 PATH
的副本。
Windows将
系统和
用户PATH
连接到本地
PATH
,用作Windows桌面上的快捷方式、开始菜单和任务栏的可见界面,这些界面被称为
Windows shell,用户通常从中启动程序。
在启动新进程时,Windows会通过复制当前正在运行进程的整个活动环境变量列表来为新进程创建一个副本。Windows内核库函数
CreateProcess会将这个环境变量列表从当前进程的内存复制到新进程的内存中,函数参数
lpEnvironment
(指向环境的长指针)为空指针时执行此操作。在Windows上,始终使用
CreateProcess
函数来从另一个可执行文件启动可执行文件。
父进程无法修改任何子进程的环境变量,子进程也无法修改其父进程的环境变量。
这意味着一旦像
cmd.exe
这样的进程开始执行批处理文件,该进程就有自己的一组环境变量,只有该进程本身才能修改。其他进程无法修改已经运行的进程的环境变量。
C) 这个错误信息是什么意思?
这个错误信息
'...' 不被识别为内部或外部命令,可执行程序或批处理文件。
通常意味着
一个
- 控制台应用程序
- 图形用户界面应用程序
- 脚本(批处理文件、PowerShell脚本、Perl脚本、VBScript、JScript等)
的文件名被指定为执行,很可能没有文件扩展名和(完整的)可执行文件/脚本文件路径 并且
Windows未能在当前目录或当前活动环境变量PATH
中的任何其他目录中找到与模式FileName.*
匹配的文件扩展名列表在当前活动环境变量PATHEXT
中列出的文件。
D) 这个错误信息可能的原因是什么?
典型的原因有:
1. 由于输入错误而导致执行文件名指定错误。
逐个字符检查命令/可执行文件的名称。
2. 当前目录与要执行的文件所在的目录不同。
在命令行上运行echo Current directory is: %CD%
,或者将此行添加到批处理文件中,在失败的命令行之前查看当前目录。
3. 要运行的可执行文件或脚本根本没有安装。
验证要运行的可执行文件是否存在。某些安装包只能在安装了其他软件包(如Java、NPM、PHP等)之后才能正常工作。
4. 要执行的文件所在的目录根本不在PATH
中。
在Windows的控制面板中打开“系统”设置窗口,点击左侧的“高级系统设置”,点击“环境变量”按钮,并查看两个列表中的“Path”及其值。默认情况下,“Path”仅存在于“系统变量”列表中。
5. 在修改系统或用户“PATH”后,某个正在运行的进程/应用程序未重新启动。
用户或安装程序使用命令“setx”或通过“控制面板-系统和安全-系统-高级系统设置-环境变量”对系统“PATH”或用户“PATH”进行了修改,但已经运行的进程/应用程序(如打开的命令提示符或PowerShell窗口)未关闭/退出并在“PATH”修改后重新打开/重新启动。详细说明请参见下面的章节F)。
6. 在64位Windows上找不到“%SystemRoot%\System32”中的可执行文件。
在64位Windows上,有一个目录
%SystemRoot%\System32
,其中包含64位可执行文件,还有一个目录
%SystemRoot%\SysWOW64
,其中包含32位可执行文件。这些目录中的大多数可执行文件都是存在于两个目录中的。但是有一些可执行文件只存在于
System32
目录中,还有一些只存在于
SysWOW64
目录中。
系统的
PATH
默认情况下将
%SystemRoot%\System32
作为第一个文件夹路径。但是,如果没有指定路径或者使用路径
%SystemRoot%\System32
来指定可执行文件,则取决于执行环境,是在哪个Windows系统目录中搜索。在64位环境中执行的应用程序或脚本实际上是访问
%SystemRoot%\System32
,而在32位环境中执行的应用程序或脚本则会被Windows的
文件系统重定向器重定向到
%SystemRoot%\SysWOW64
目录。
在32位环境中运行的应用程序或脚本想要在
%SystemRoot%\System32
中运行64位可执行文件,必须使用带有文件路径
%SystemRoot%\Sysnative
的完全限定文件名。
注意:
%SystemRoot%\Sysnative
既不是目录,也不是任何类型的链接。它是一种非常特殊的存在,仅适用于x86应用程序。对于amd64应用程序,它不存在。批处理文件中的条件
if exist %SystemRoot%\Sysnative
在两个环境中始终为假,但在32位执行环境中,
if exist %SystemRoot%\Sysnative\cmd.exe
为真,在64位环境和32位Windows上为假。这个条件可以在批处理脚本中使用,以确定批处理文件是否由64位Windows上的32位
cmd.exe
在
%SystemRoot%\SysWOW64
中处理,这可能是根据任务重要的信息。
请参阅Microsoft文档
WOW64实现细节和
受WOW64影响的注册表键。
7. PATH
包含对尚未定义的环境变量的引用。
可以在PATH
中使用对另一个环境变量值的引用来指定文件夹路径,例如SystemRoot
。重要的是,该环境变量也在Windows中的同一组环境变量或首先由Windows处理的一组环境变量中定义。
例如,如果将
%JAVA_HOME%\bin
添加到
系统的
PATH
环境变量中,则还必须定义一个
系统环境变量
JAVA_HOME
,其值为Java程序文件的基本文件夹路径。仅仅在用户环境变量中定义
JAVA_HOME
或者在批处理文件的
本地环境中稍后定义
JAVA_HOME
的环境变量是不够的。
如果将
%JAVA_HOME%\bin
添加到
用户的
PATH
中,Windows会根据环境变量
JAVA_HOME
的定义(无论是作为
系统环境变量还是
用户环境变量)将其展开为完整的文件夹路径,但是对于在Windows命令进程的
本地环境中稍后定义的
JAVA_HOME
则不会展开。
通过在Windows启动菜单上进行系统或用户PATH
的修改,并在新的命令提示符窗口中运行set path
命令后,可以轻松地看到这样的错误。输出的PATH
不应再包含任何%Variable%
环境变量值引用。
8. LOCAL变量PATH
在命令行或批处理文件中之前已被修改。
在命令行上运行set path
,或将此命令添加到批处理文件中,在无法查看环境变量PATH
和PATHEXT
当前值的命令行上方。
这个原因导致批处理文件中包含有set path=...
的位置无法找到外部命令SORT。
9. LOCAL变量PATH
太长。
本地变量PATH
的长度超过了cmd.exe
的限制。当字符串值超过8191
个字符时,Windows命令处理器无法在本地Path
的文件夹路径中找到任何可执行文件/脚本。
有关PATH
长度限制的更多详细信息,请参阅我的第二个答案。
感谢Albert Mosiałek向我提供了关于此问题导致程序或脚本无法识别的信息。
如何避免出现此错误信息?
最好编写一个批处理文件,使其不依赖于 PATH 和 PATHEXT 变量以及 PATH 中目录的顺序。这意味着在这里使用命令行。
FOR /f "delims=" %%d in ('dir /s /b /ad ^| %SystemRoot%\System32\sort.exe /r') do rd "%%d"
任何可执行文件存储在%SystemRoot%\System32中的外部命令,都应该在批处理文件中指定该路径和文件扩展名.exe。这样,Windows命令解释器就不需要使用本地PATH和PATHEXT来搜索文件,批处理文件将始终正常工作(只要批处理文件中未修改环境变量SystemRoot,我从未见过这种情况)。
F) 系统或用户的路径更改何时应用于进程?
当用户通过Windows开始菜单或在Windows资源管理器窗口中打开命令提示符窗口时,用户使用隐式选项/K
启动cmd.exe
,以便在完成命令后保持控制台窗口打开,这对于调试批处理文件很有用。
当在Windows资源管理器中双击批处理文件时,用户使用隐式选项/C
启动cmd.exe
来处理批处理文件,并在批处理处理完成后关闭控制台窗口,这对于调试批处理文件不利,因为在这种情况下无法看到错误消息。
在这两种情况下,Windows会创建一个应用程序(通常是Windows资源管理器)启动cmd.exe
的环境变量的副本。因此,启动的命令进程具有一个本地PATH
,其值与父进程在启动cmd.exe
时的值相同。
示例:
打开命令提示符窗口,运行title Process1
和set path
。
输出是当前用户帐户在控制台窗口中定义的PATH
和PATHEXT
,现在窗口标题为Process1。
运行set PATH=%SystemRoot%\System32
,然后再次运行set path
。
输出再次是PATH
和PATHEXT
,但PATH
现在只包含一个目录。
运行start "Process2"
,并在新的控制台窗口中以窗口标题Process2运行命令set path
。
输出是与Process1中相同的PATH
和PATHEXT
的值。
这表明在启动新进程时,正在运行的进程的当前环境变量被复制,而不是Windows当前存储在Windows注册表中的值。
在Process2中运行命令set PATH=
,然后运行set path
。
输出只有PATHEXT
,因为Process2中本地PATH
不再存在。
这表明每个进程都可以修改其环境变量,包括完全删除。
切换到Process1窗口,运行命令set PATH=%PATH%;%SystemRoot%
,然后运行set path
。
输出是包含两个目录和PATHEXT
的PATH
。
运行命令start "Process3"
,并在标题为Process3的打开窗口中运行命令set path
。
输出是与Process1中定义的两个目录以及PATHEXT
相同的PATH
。
在Process3中运行命令set PATH=%SystemRoot%\System32
。
有3个命令进程正在运行,当
%SystemRoot%
展开为
C:\Windows
时,它们的本地
PATH
值如下:
进程1:
PATH=C:\Windows\System32;C:\Windows
进程2:根本不存在
PATH
。
进程3:
PATH=C:\Windows\System32
那么现在,在打开
控制面板 - 系统 - 高级系统设置 - 环境变量并将
新的环境变量
PATH
的值设为
C:\Temp
,或者如果已经存在一个
用户PATH
环境变量,则编辑
PATH
并将
;C:\Temp
追加到其值后会发生什么?
只要打开带有标题“环境变量”的对话框窗口,显示两个列表,修改变量时不会发生任何事情,直到点击“确定”按钮将所有更改应用到Windows注册表并关闭窗口。
让我们回到三个正在运行的命令进程,并在Process1、Process2和Process3中运行命令“set path”。可以看到:
Process1:PATH=C:\Windows\System32;C:\Windows
Process2:根本不存在PATH。
Process3:PATH=C:\Windows\System32
已经运行的进程没有发生任何变化。
没有任何进程可以修改不同运行进程的环境变量!
从Windows开始菜单打开一个额外的命令提示符窗口,并在第四个命令进程中运行命令“set path”。可以看到,第四个命令进程的
本地PATH现在已经添加了目录C:\Temp。
然后关闭所有四个命令进程,并分别从用户PATH中删除添加的"user"和"PATH",如果之前已经附加了此目录路径,则从用户PATH中删除";C:\Temp"。
如果没有进程可以修改已经运行进程的环境变量,那么这怎么可能呢?
当关闭"环境变量"窗口并点击"确定"按钮时,Windows资源管理器实例作为Windows桌面运行的环境变量列表是如何被修改的?
这两个问题的答案由
eryksun在他的评论中给出。
在点击"环境变量"窗口的"确定"按钮后,将对系统和用户变量的修改写入注册表,Windows会向所有顶级窗口发送
WM_SETTINGCHANGE消息,以通知正在运行的应用程序有关已更改的系统参数。
这完全取决于应用程序是否处理此事件消息以及如何处理。作为Windows桌面运行的Windows资源管理器从注册表中读取环境变量,并相应地更新其环境变量列表。其他应用程序(如Total Commander)也会处理此消息,并更新它们的环境变量列表。但是,幸运的是,cmd.exe
并不这样做,因为这将带来很多问题。
是否有可能通过命令提示符窗口或批处理文件使用WM_SETTINGCHANGE
通知来修改系统或用户变量?
可以使用reg add
命令修改环境变量的注册表值。但是,这不会导致向所有顶级窗口发送WM_SETTINGCHANGE
消息。使用reg add
或regedit
进行的此类更改需要重新启动Windows(或至少注销并重新登录当前用户)才能生效。
但是还有一个命令
setx
,它专门用于修改
系统或
用户变量,并在根据指定参数更新注册表后向所有顶级窗口发送
WM_SETTINGCHANGE
消息。在命令提示符窗口中运行
setx /?
以获取详细信息。但请注意,
setx
不会修改正在运行的命令进程的
本地环境变量。这需要使用命令
set
与
setx
一起使用来完成。
G)Windows如何处理环境变量PATHEXT?
与环境变量PATH相比,Windows处理包含文件扩展名的环境变量PATHEXT的方式不同。
系统 PATHEXT和用户 PATHEXT不会连接到本地 PATHEXT。
对于在具有定义了用户 PATHEXT的账户的环境下运行的所有进程,用户 PATHEXT替代了系统 PATHEXT。
默认情况下,仅定义了一个系统 PATHEXT环境变量。
H)是否可以禁用当前目录中的文件搜索?
Windows命令处理器默认在当前目录中搜索脚本文件或可执行文件的文件名,如果在命令行或批处理文件中指定了文件名但没有路径,这意味着参数字符串中没有反斜杠\
(或正斜杠/
,感谢自动更正)。
但是,在Windows Vista及更高版本的Windows客户端和Windows Server 2003及更高版本的Windows服务器上,确实可以通过定义环境变量NoDefaultCurrentDirectoryInExePath
来禁用在当前目录中搜索未至少包含相对路径.\
的脚本/可执行文件。这个环境变量的值可以是任何值,如eryksun在他下面的评论中所写,并且由Microsoft的关于函数NeedCurrentDirectoryForExePathA的文档进行了解释。
有关使用此环境变量的详细信息,请参阅从路径中删除当前工作目录。
I) 如何修改系统或用户的PATH?
最好通过Windows GUI对话框窗口环境变量来修改系统和用户的PATH
环境变量。可以按照以下步骤打开此对话框窗口:
- 点击Windows的开始菜单按钮。
- 在键盘上输入环境变量。
- Windows会提供两个选项:
编辑系统环境变量
编辑您的帐户的环境变量
- 点击其中一个选项以打开环境变量窗口。
也可以打开Windows的控制面板。然后点击系统和安全,选择类别作为显示选项按视图查看。接下来点击系统。然后点击左侧的高级系统设置,再点击环境变量...按钮。
如果键盘上有 Pause 键,或者至少与 Fn 键组合使用,还可以通过按下 Windows 徽标键 + Pause 的组合键来打开 系统 窗口。另请参阅 Microsoft 文档页面 Windows 中的键盘快捷键。
进一步的用户操作对于编辑上方列表中的 用户 Path
或下方列表中的 系统 Path
是自解释的。
PATH
变量设置不正确或者在脚本的其他地方被覆盖了,这样命令解释器就不知道在哪里搜索sort.exe
。其他的命令都是CMD内置的,所以它们都能被找到。 - aschipflpath
是一个环境变量。你说得对,我在脚本中定义了一个名为path的变量。请问你是如何知道被覆盖的是PATH
?我从sort
到PATH
环境变量之间从未建立过联系。 - Hashim Azizsort
是您命令行中唯一的外部命令,这让我产生了怀疑... - aschipfl