“X 不是内部或外部命令、可运行程序或批处理文件”是啥原因?

Posted

技术标签:

【中文标题】“X 不是内部或外部命令、可运行程序或批处理文件”是啥原因?【英文标题】:What is the reason for "X is not recognized as an internal or external command, operable program or batch file"?“X 不是内部或外部命令、可运行程序或批处理文件”是什么原因? 【发布时间】:2021-09-17 10:58:02 【问题描述】:

我有一个单行 sn-p 可以在命令行中完美运行,但是当我将它作为批处理脚本的一部分运行时会失败并引发错误。

以下命令按预期运行,删除文件夹中的所有空子文件夹。

for /f "delims=" %d in ('dir /s /b /ad ^| sort /r') do rd "%d"

但是,当像这样放入批处理文件时......

FOR /f "delims=" %%d in ('dir /s /b /ad ^| sort /r') do rd "%%d"

...它抛出标准错误:

排序不被识别为内部或外部命令

在过去的一个小时左右,我一直在试验是否有转义管道、更改选项的顺序、查找 dirsort 的文档等,但我仍然无法弄清楚这里发生了什么。批处理文件的其余部分,只有几行,工作正常,这是其中唯一失败的行。

谁能帮忙?

【问题讨论】:

我猜你的PATH变量设置不正确,或者你在脚本的其他地方覆盖了它,所以命令解释器不再知道在哪里搜索sort.exe;其他的命令都是cmd-internal 的,所以都找到了…… 该死的。我完全忘记了path 是一个环境变量。没错,我在脚本中定义了一个名为 path 的变量。请问你是怎么知道被覆盖的是PATH?我永远不会从sort 连接到PATH 环境变量。 这是错误消息以及 sort 是命令行中唯一的外部命令,这让我产生了这种怀疑...... "sort 未被识别为内部或外部命令..." - 我们知道它不是内部命令,但我们希望它被识别为外部命令。外部命令位于 PATH 上。如果找不到,那是因为它不在 PATH 上。所以也许你搞砸了 PATH。 太棒了,谢谢大家。经过测试,脚本现在运行良好。 【参考方案1】:

A) Windows 命令处理器如何搜索命令?

Windows 命令处理器搜索要执行的 COMMAND

    不是cmd.exe的内部命令 只指定了文件名,没有文件扩展名,没有路径

对于匹配模式command.*的文件并且具有在本地环境变量PATHEXT中列出的文件扩展名

    当前目录的第一个 在本地环境变量PATH的所有目录下。

SORTFINDFINDSTRROBOCOPYXCOPY 等等命令不是cmd.exe 的内部命令。它们是随 Windows 一起安装的控制台应用程序,位于目录 %SystemRoot%\System32 中,文件名为 sort.exefind.exefindstr.exerobocopy.exexcopy.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 将 systemuser PATH 连接到 local PATH 用于用作 Windows 桌面的 Windows 资源管理器实例,其中快捷方式打开桌面屏幕和 Windows 开始菜单以及 Windows 任务栏作为用户的可视界面,称为 Windows shell,用户通常从这些界面启动程序。

在启动新进程时,Windows 会为新进程复制当前正在运行的进程的整个当前活动环境变量列表。 Windows 内核库函数CreateProcess 将此环境变量列表从当前进程的内存复制到新进程的内存中,函数参数lpEnvironment(指向环境的长指针)是一个空指针。 CreateProcess 函数之一总是在 Windows 上用于从另一个可执行文件启动可执行文件。

父进程不能修改任何子进程的环境变量,子进程也不能修改其父进程的环境变量。

这意味着一旦像cmd.exe 这样的进程启动以执行批处理文件,该进程就有自己的一组环境变量,只有进程本身可以修改这些变量。没有其他进程可以修改已经运行的进程的环境变量。


C) 错误信息是什么意思?

错误信息

'...' 不是内部或外部命令、可运行的程序或批处理文件。

总是意味着

    文件名

    控制台应用程序 GUI 应用程序 脚本(批处理文件、PowerShell 脚本、Perl 脚本、VBScript、JScript、...)

    被指定执行很可能没有文件扩展名,也没有可执行文件/脚本文件的(完整)路径

    Windows 找不到与模式 FileName.* 匹配的文件,其文件扩展名列在当前目录中当前活动的环境变量 PATHEXT 或当前活动环境变量 PATH 中的任何其他目录中。


D) 出现此错误消息的可能原因是什么?

典型的原因是:

1.由于输入错误,要执行的文件的文件名指定错误。

逐字符检查命令/可执行文件的名称。

2。当前目录与包含要执行的文件的目录不同。

在命令行上运行echo Current directory is: %CD% 或将此行添加到无法查看当前目录是什么的命令行上方的批处理文件中。

3.根本没有安装要运行的可执行文件或脚本。

验证是否存在要运行的可执行文件。某些安装包只有在之前安装了其他包(如 Java、NPM、php 等)时才有效。

4.要执行的文件的目录根本不在 PATH

在Windows控制面板中打开系统设置窗口,点击左侧的高级系统设置,点击环境按钮变量并在两个列表中查找Path 及其值。默认情况下Path 仅存在于系统变量列表中。

5.修改系统或用户后没有重新启动正在运行的进程/应用程序 PATH .

system PATHuser PATH 使用命令setx 或通过控制面板 - 系统和安全 - 系统 - 高级系统进行修改设置 - 环境变量 由用户或安装程序创建,但在 PATH 修改后,已运行的进程/应用程序(如打开的命令提示符或 PowerShell 窗口)未关闭/退出并打开/重新启动。这是必要的,详见下文F)一章。

6.在 64 位 Windows 上找不到 %SystemRoot%\System32 中的可执行文件。

在带有支持 x86 指令集的处理器的 64 位 Windows 上,目录 %SystemRoot%\System32 包含 64 位可执行文件,而 %SystemRoot%\SysWOW64 包含 32 位可执行文件。大多数可执行文件都存在于两个目录中。但是有一些可执行文件只存在于System32 中,少数只存在于SysWOW64 中。

系统 PATH 默认包含第一个文件夹路径%SystemRoot%\System32。但是在两个 Windows 系统文件夹中的哪一个文件夹中搜索指定的可执行文件(不带路径或路径 %SystemRoot%\System32)取决于执行环境。在 64 位环境中执行的应用程序或脚本实际上是在访问 %SystemRoot%\System32,而在 32 位环境中执行的应用程序或脚本被 Windows file system redirector 重定向到目录 %SystemRoot%\SysWOW64

在 32 位环境中运行的应用程序或脚本想要在 %SystemRoot%\System32 中运行 64 位可执行文件,必须使用文件路径为 %SystemRoot%\Sysnative 的可执行文件的完全限定文件名。

注意: %SystemRoot%\Sysnative 既不是目录也不是任何类型的链接。这是非常特殊的东西,仅存在于 x86 应用程序中。 amd64 应用程序不存在它。批处理文件中的条件if exist %SystemRoot%\Sysnative 在两种环境中始终为假,但if exist %SystemRoot%\Sysnative\cmd.exe 在 32 位执行环境中为真,在 64 位环境和 32 位 Windows 中为假。此条件可用于批处理脚本,以确定批处理文件是否在 64 位 Windows 上由 32 位 cmd.exe in %SystemRoot%\SysWOW64 处理,这可能很重要,具体取决于任务。

另请参阅 Microsoft 文档 WOW64 Implementation Details 和 Registry Keys Affected by WOW64。

7. PATH 包含对未(尚未)定义的环境变量的引用。

可以在PATH 中使用对另一个环境变量(如SystemRoot)的值的引用来指定文件夹路径。重要的是,此环境变量也定义在同一组环境变量或 Windows 首先处理的一组环境变量中。

例如,如果将%JAVA_HOME%\bin 添加到system PATH 环境变量,则还必须使用基本文件夹路径定义system 环境变量JAVA_HOME到 Java 程序文件。在批处理文件的本地环境中定义用户环境变量JAVA_HOME或稍后定义环境变量JAVA_HOME是不够的。

%JAVA_HOME%\bin 添加到 user PATH 被 Windows 扩展为一个完整的限定文件夹路径,如果环境变量 JAVA_HOME 被定义为 systemuser 环境变量,但不在稍后在 Windows 命令进程的 本地 环境中定义的 JAVA_HOME 上。

从 Windows 开始菜单中对 systemuser PATH 进行修改并运行 @ 后打开一个新的 command prompt 窗口,很容易看到此类错误987654398@。输出 PATH 不应再包含任何 %Variable% 环境变量值引用。

8. LOCAL 变量 PATH 之前已在命令行或批处理文件中修改过。

在命令行上运行set path 或将此命令添加到无法查看环境变量PATHPATHEXT 的当前值的命令行上方的批处理文件中。

最后一个原因是在执行包含set path=...以上某处的批处理文件时找不到外部命令SORT


E) 如何避免这个错误信息?

最好是编写一个独立于PATHPATHEXT 的批处理文件以及PATH 中的目录顺序,这意味着这里使用命令行:

FOR /f "delims=" %%d in ('dir /s /b /ad ^| %SystemRoot%\System32\sort.exe /r') do rd "%%d"

任何外部命令,其可执行文件存储在%SystemRoot%\System32 中,应在批处理文件中指定此路径和文件扩展名.exe。然后 Windows 命令解释器不需要使用 local PATHPATHEXT 搜索文件,并且批处理文件始终有效(只要环境变量 SystemRoot 未在批处理中也被修改我从未见过的文件)。


F) 系统或用户 PATH 更改何时应用于进程?

当用户通过 Windows 开始菜单或从 Windows 资源管理器窗口中打开命令提示符窗口时,用户会使用隐式使用选项 /K 启动 cmd.exe 以在完成命令后保持控制台窗口打开,这对调试批处理文件。

在Windows资源管理器中双击批处理文件时,用户启动cmd.exe处理批处理文件,使用选项/C在完成批处理后关闭控制台窗口,这不利于调试批处理文件因为在这种情况下看不到错误消息。

在这两种情况下,Windows 都会从cmd.exe(通常是 Windows 资源管理器)开始创建应用程序环境变量的副本。因此,启动的命令进程有一个本地PATH,其值与父进程在启动cmd.exe时的值相同。

例子:

    打开命令提示符窗口,运行 title Process1 并运行 set path。 输出是 PATHPATHEXT,当前为控制台窗口中的当前用户帐户定义,现在窗口标题为 Process1

    运行set PATH=%SystemRoot%\System32,然后再次运行set path。 输出又是PATHPATHEXT,但PATH 现在只包含一个目录。

    运行start "Process2" 并在带有窗口标题Process2 的新控制台窗口中运行命令set path。 输出为 PATHPATHEXT,与 Process1 中的值相同。 这表明在启动一个新进程时,当前正在运行的进程的环境变量被复制,而不是 Windows 本身当前存储在 Windows 注册表中的环境变量。

    Process2 中运行命令 set PATH= 和下一个 set path。 输出仅为 PATHEXT,因为 Process2 不再存在 local PATH。 这表明每个进程都可以修改其环境变量,包括完全删除。

    切换到 Process1 窗口,运行命令 set PATH=%PATH%;%SystemRoot% 和下一个 set path。 输出是PATH,有两个目录和PATHEXT

    运行命令start "Process3",并在标题为Process3的打开窗口中运行命令set path。 输出为 PATH,其中还为 Process1PATHEXT 定义了两个目录。

    Process3中运行命令set PATH=%SystemRoot%\System32

%SystemRoot% 扩展为C:\Windows 时,local PATH 使用以下值运行 3 个命令进程:

Process1PATH=C:\Windows\System32;C:\WindowsProcess2PATH 根本不存在。Process3:@987654453 @

那么现在打开 控制面板 - 系统 - 高级系统设置 - 环境变量 并将 用户变量 添加到 列表会发生什么环境变量PATH 的值为C:\Temp,或者如果已经有一个用户 PATH 环境变量,编辑 PATH 并将;C:\Temp 添加到价值?

好吧,只要打开标题为Environment Variables 显示两个列表的对话窗口,修改变量不会发生任何事情,直到点击按钮OK对 Windows 注册表的所有更改并关闭窗口。

让我们回到三个正在运行的命令进程,分别在Process1Process2Process3中运行命令set path。可见:

Process1PATH=C:\Windows\System32;C:\WindowsProcess2PATH 根本不存在。Process3:@987654462 @

已经运行的进程没有任何改变。

任何进程都不能修改不同运行进程的环境变量!

从 Windows 开始菜单打开另一个命令提示符窗口并在第四个命令进程中运行命令set path。可以看到第四个命令进程的localPATH现在已经追加了目录C:\Temp

然后关闭所有四个命令进程并删除添加的user PATH 分别从user PATH 中删除;C:\Temp,如果之前附加了此目录路径。

如果没有进程可以修改已经运行的进程的环境变量,这怎么可能?

在使用OK按钮关闭Environment Variables窗口时,作为Windows桌面运行的Windows Explorer实例的环境变量列表是如何修改的?

eryksun 在他的评论中给出了这两个问题的答案。

点击环境变量窗口的OK按钮,将systemuser变量的修改写入注册表后, Windows 将WM_SETTINGCHANGE 消息发送到所有***窗口,以通知正在运行的应用程序有关更改的系统参数。

是否处理此事件消息以及如何处理取决于应用程序。作为 Windows 桌面运行的 Windows 资源管理器从注册表中读取环境变量并相应地更新其环境变量列表。 Total Commander 等其他应用程序也会处理此消息并更新其环境变量列表。但幸运的是cmd.exe 并没有这样做,因为这确实有问题。

是否有可能在命令提示符窗口或批处理文件中通过WM_SETTINGCHANGE 通知修改 systemuser 变量?

可以使用reg add 命令修改环境变量的注册表值。但这不会导致向所有***窗口发送WM_SETTINGCHANGE 消息。使用 reg addregedit 完成的此类更改需要重新启动 Windows(或至少注销并登录当前用户)才能完全考虑在内。

但还有一个命令setx,它是为修改systemuser变量而设计的,它也将WM_SETTINGCHANGE消息发送到所有顶层根据指定的参数更新注册表后的窗口。在命令提示符窗口中运行 setx /? 以获取详细信息。但请注意setx 不会修改运行命令进程的本地环境变量。这必须使用除了setx 之外的命令set 来完成。


G) Windows 如何处理环境变量PATHEXT?

与环境变量 PATH 相比,Windows 处理带有文件扩展名列表的环境变量 PATHEXT 是不同的。

System PATHEXTuser PATHEXT 连接到 local PATHEXT

用户 PATHEXT 替换 系统 PATHEXT 用于在定义了的帐户环境下运行的所有进程用户 PATHEXT.

默认情况下只定义了一个系统 PATHEXT 环境变量。


H) 是否可以禁用当前目录中的文件搜索?

如果在命令行或批处理文件中指定了脚本文件或可执行文件的文件名,则默认在当前目录中搜索,而没有任何路径,这意味着没有反斜杠\(或正斜杠/感谢自动更正)在参数字符串中。

但在 Windows Vista 和更高版本的 Windows 客户端版本以及 Windows Server 2003 和更高版本的 Windows 服务器版本上,确实可以通过定义环境来禁用在指定的当前目录中搜索脚本/可执行文件,至少没有相对路径 .\变量NoDefaultCurrentDirectoryInExePath 具有eryksun 在下面的评论中所写的任何值,并由微软关于函数NeedCurrentDirectoryForExePathA 的文档解释。

有关此环境变量用法的更多详细信息,请参阅Removing the current working directory from the path。


I) 如何修改系统或用户路径?

systemuser PATH 环境变量最好由用户使用 Windows GUI 对话窗口 Environment Variables 进行修改。该对话窗口可以按如下方式打开:

    单击 Windows 开始菜单按钮。 在键盘上键入环境变量。 Windows 提供了两个项目:编辑系统环境变量为您的帐户编辑环境变量 点击这两项之一打开环境变量窗口。

也可以打开Windows 控制面板。接下来必须单击 System and Security,并为显示选项 View by 选择 Category。下一步必须点击System。必须点击左侧的Advanced system settings,然后点击按钮Environment Variables...

系统窗口也可以通过按下组合键Windows logo key + Pause来打开,如果键盘上有Pause键的话或至少与键 Fn 结合使用。另请参阅 Microsoft 文档页面 Keyboard shortcuts in Windows。

进一步的用户操作是不言自明的,用于编辑上面列表中的 user Pathsystem Path 在下面的列表中。

【讨论】:

存储在注册表中的环境变量是引用其他%variables%REG_SZREG_EXPAND_SZ类型。因为枚举注册表项没有设置顺序,Explorer 分 4 次重新加载环境:系统REG_SZ、系统REG_EXPAND_SZ、用户REG_SZ 和用户REG_EXPAND_SZPATH 值几乎始终是根据动态值和REG_SZ 值定义的REG_EXPAND_SZ 类型。此外,用户的 PATH 会附加到系统值。需要注意的是,天真地使用 setx.exe 来修改 PATH 会展平并扩展此结构。 CMD 的默认行为是首先在当前目录中搜索。我们可以通过定义环境变量NoDefaultCurrentDirectoryInExePath 让它跳过这一步。然后为了安全起见,我们可以添加“。”最后明确地PATH,或者至少在系统目录之后。如果我们不将其添加到PATH,那么在当前目录中运行文件必须使用明确的相对路径,例如.\program.exe 如果由于某种原因您有一个没有扩展名(.exe 或任何其他)的可执行文件,那么您可以通过附加“;”在 CMD 中运行它。到PATHEXT 环境变量。 非常感谢@eryksun 为像我这样感兴趣的人提供了所有这些额外信息。【参考方案2】:

很可能,您弄乱了PATH 变量。也许您正在脚本中的其他地方覆盖它。由于sort 是一个外部命令,与命令行中的所有其他命令(如fordirrd)相反,它们是cmd-内部命令,因此需要PATH 变量来查找命令。如果未定义PATH,则仅在当前工作目录中搜索外部命令。还有一个PATHEXT 变量需要定义可执行文件的标准文件扩展名,例如.com.exe。因此,当sort 出现在命令提示符或批处理文件中时,系统会在当前工作目录和PATH 变量指定的所有目录中搜索基本名称为sort 和@ 指定的扩展名之一的文件987654335@。命令sort实际上称为sort.exe,通常位于C:\Windows\System32

【讨论】:

以上是关于“X 不是内部或外部命令、可运行程序或批处理文件”是啥原因?的主要内容,如果未能解决你的问题,请参考以下文章