【问题标题】:Missing environment variable (PATH) when calling batch from PowerShell?从 PowerShell 调用批处理时缺少环境变量 (PATH)?
【发布时间】:2017-09-28 08:00:17
【问题描述】:

我目前正在对无人参与的特殊软件安装进行故障排除。我将问题缩小到缺少的环境变量上,现在我很困惑,因为我不知道它为什么会丢失。

我可以像这样重现问题:

我有一个名为batch.cmd 的批处理文件。该文件包含以下代码:

SET
Pause

SET 命令列出环境变量。

我从 PowerShell 中调用此脚本,例如:

$Status = Start-Process -FilePath "C:\...\Batch.cmd" -PassThru -Wait

我也尝试过这样称呼它:

& C:\...\Batch.cmd

因此,与直接运行批处理脚本相比,我在 PATH 变量中缺少 2 个文件夹路径。看看截图:

如您所见,如果直接调用脚本,PATH 变量会包含更多条目。

条目本身不是我设置的。它们必须由安装程序在较早的安装步骤中创建。

我没有太多设置环境变量的经验,我不知道它是什么以及为什么会这样。

有人知道怎么回事吗?

更新 #1:

我仍然没有一个干净的解决方案。

更多细节:

我正在创建无人值守的软件安装。第一步调用设置。似乎安装例程向 PATH 环境变量添加了 2 个文件夹路径。

在第二步中,我调用了一个批处理脚本,该脚本需要 PATH 变量中的这两个文件夹路径,但由于它们不存在而失败。

在调用批处理之前,我尝试在 PowerShell 中重新加载变量:

$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

它似乎适用于 PS 环境,但批处理没有变化。 第二次调用脚本它工作。重启后它也能正常工作。

更新 #2:

【问题讨论】:

  • 重启后能否重现问题?
  • this?这样的东西呢
  • @PetSerAl:我只是试了一下。我可以确认,重启后就OK了。
  • @BenH:听起来很合适。我想我将不得不通过全新安装来测试它......
  • 为了便于比较,我建议您在 Powershell (gci env:path).value.split(";") 和 cmd 窗口中发出 cmd /D /K @for %A in ("%Path:;=";"%") do @Echo:%~A 后者将排除可能的 autoruns 可能会注入额外的路径条目。

标签: powershell batch-file


【解决方案1】:

每个进程都有自己的环境变量列表。每次创建进程时,Windows 都会将新进程的正在运行进程的当前环境变量列表复制到单独的内存区域中。

子进程不能修改其父进程的环境变量,父进程也不能修改其子进程之一的环境变量。如果那是可能的,那就意味着任何进程都可以操纵任何其他进程的工作记忆,那肯定是不好的。

在命令提示符窗口中执行以下操作可以轻松查看环境变量管理:

title First CMD
set PATH

第一行将命令进程窗口的窗口标题更改为First CMD

第二个命令列出所有以PATH 开头的环境变量及其值。这通常意味着环境变量PATHPATHEXT 的值与当前运行命令进程的环境变量列表中的值相同。

set "PATH=%PATH%;%USERPROFILE%\Desktop"
start "Second CMD"

第一行将您的桌面目录附加到 当前 命令进程的PATH。第二行再打开一个命令提示符窗口 带有窗口标题Second CMD。第二个命令进程获取当前命令进程的所有环境变量的副本。

第二命令提示符窗口中运行:

set PATH

输出为PATHEXTPATH,最后包含您的桌面目录。因此,在这里已经可以看出,新的命令进程是使用与在启动第二个命令进程时为第一个命令进程定义的相同的环境变量创建的,而不是存储在 Windows 注册表中并在查看高级系统设置中的环境变量时显示的内容窗户。

切换到第一个命令提示符窗口并执行:

set PATH=
sort /?

结果是错误信息:

排序未被识别为内部或外部命令,
可运行的程序或批处理文件。

sort.exe in directory %SystemRoot%\System32 在此命令进程中不再被 Windows 命令解释器找到,因为 local PATH 以及用于搜索可执行文件和脚本的所有目录路径不再存在在这个命令过程中除了当前目录是偶然%SystemRoot%\System32

第一个命令提示符窗口中运行:

start "Third CMD"

在打开的第三个命令提示符窗口中运行:

set PATH

列出的只有 PATHEXT 及其值。 PATH 在第一个命令提示符窗口中不再定义,因此在第三个命令提示符窗口中也不存在。

第三个命令提示符窗口中运行:

set PATH=%SystemRoot%\System32
sort /?

外部命令 SORT 的帮助显示为可执行文件可以被 Windows 命令解释器在第三个命令进程中找到,因为 local PATH 被再次定义包含sort.exe的目录的路径。

切换到第二个命令提示符窗口并运行:

sort /?

外部命令 SORT 的帮助也会显示在第二个命令提示符窗口中,因为 local PATH 仍然具有启动它时的所有目录路径,包括您的桌面文件夹最后。

切换到第一个命令提示符窗口并运行:

sort /?

之前删除后仍然显示第一个命令提示窗口PATH未定义的错误消息。

所以现在有 3 个命令进程正在运行

  1. 根本没有定义PATH
  2. PATH 带有未存储在 Windows 注册表中的其他桌面文件夹,
  3. PATH 很可能只有 C:\Windows\System32 作为唯一的文件夹路径。

您现在还可以在 Windows 高级系统设置中的 usersystem 环境变量列表中修改 PATH,但这不会改变 3 上的任何内容本地 PATH 3 个已经运行的命令进程。

现在关闭所有 3 个不再需要的命令进程窗口。

查看What is the reason for 'sort' is not recognized as an internal or external command, operable program or batch file? 上的答案,它解释了 systemuser PATH 在 Windows 注册表中的存储位置。

我假设 setup.exe 运行首先将目录路径直接添加到 Windows 注册表中的 systemuser PATH 这仅对从 Windows 桌面启动的新进程有效或开始菜单,但不适用于任何已经运行的进程。

因此您必须在批处理文件中执行您已经在 PowerShell 脚本中执行的操作,直接从 Windows 注册表中查询 PATH 的值并为命令进程设置 local PATH当前正在执行批处理文件。

@echo off
rem Get directly from Windows registry the system PATH variable value.
set "SystemPath="
for /F "skip=2 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe query "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" /v "Path" 2^>nul') do (
    if /I "%%N" == "Path" (
        set "SystemPath=%%P"
        goto GetUserPath
    )
)

rem Get directly from Windows registry the user PATH variable value.
:GetUserPath
set "UserPath="
for /F "skip=2 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe query "HKCU\Environment" /v "Path" 2^>nul') do (
    if /I "%%N" == "Path" (
        set "UserPath=%%P"
        goto SetPath
    )
)

rem Concatenate the two PATH values to a single value and expand variables.
rem Delete the two environment variables not further needed.
rem Next replace all two consecutive semicolons by a single semicolon.
rem Last remove semicolon from end of directory list if there is one.

:SetPath
call set "PATH=%SystemPath%;%UserPath%"
set "SystemPath="
set "UserPath="
set "PATH=%PATH:;;=;%"
if "%PATH:~-1%" == ";" set "PATH=%PATH:~0,-1%"

如果目录路径列表中有;; 或目录路径列表以; 结尾,则最后两个命令实际上不是必需的。

以上批处理代码减至最少:

for /F "skip=2 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe query "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" /v "Path" 2^>nul') do if /I "%%N" == "Path" call set "PATH=%%P" & goto GetUserPath
:GetUserPath
for /F "skip=2 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe query "HKCU\Environment" /v "Path" 2^>nul') do if /I "%%N" == "Path" call set "PATH=%PATH%;%%P" & goto ProcessSetup
:ProcessSetup

要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

  • echo /?
  • for /?
  • goto /?
  • reg /?
  • reg query /?
  • rem /?
  • set /?

顺便说一句:

现在,一个应用程序必须添加到 Windows 注册表中的PATH 才能工作,现在的编码很差。

仅当应用程序设计为主要从命令提示符中手动执行时,才应在 Windows 注册表中向 systemuser PATH 添加目录路径应用程序用户的窗口。在这种情况下,将应用程序的目录添加到 Windows 注册表中的 systemuser PATH 对应用程序的用户很有帮助。

但主要通过图形用户界面使用的应用程序应将其目录路径添加到 Windows 注册表中的其他位置。常见的是将可执行文件的名称与扩展名添加到

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths

或应用程序(套件)的特定注册表项,可由应用程序(套件)的所有组件查询。

另请参阅Where is "START" searching for executables? 上的回答

【讨论】:

  • 嗨 Mofi,谢谢 - 我尝试了您更新的脚本。 PATH-variables 现在已重新加载。它并不完美(-> 在我的请求中查看“更新 #2”),但第二个安装脚本现在确实开始了。安装本身仍然失败,我目前不知道为什么。我第二次调用它时,它正在运行:-(。也许我将不得不在第一个任务之后创建一个任务序列并重新启动。Fu****g 软件.... grrrr 因为你的脚本正在运行,我会将其标记为答案!感谢您的努力!
  • 我在回答中写道,PATH 中的;; 对于 Windows 命令解释器并不重要,但在进一步处理之前还发布了清理 PATH 的代码。只需将完全注释的批处理代码中的最后两行复制到减少的批处理代码中的:ProcessSetup 行下方,以将所有出现的;; 替换为;,以防system PATH 以a 结尾分号虽然不应该是这样,但许多安装程序更新 系统 PATH 不是 100% 正确的。
  • 您的安装问题可能是由其他环境变量引起的,这些环境变量也是由第一个setup.exe添加到Windows注册表的,并且在本地环境中执行此应用程序时需要这些变量。或者要运行的应用程序需要在 Windows 启动时自动启动的运行服务,但在安装后不会立即启动。或者安装的应用程序要求当前工作目录是应用程序的目录才能正常工作。如果您将文本发布为文本而不是文本编辑器显示的屏幕截图,那也会很好。
猜你喜欢
  • 2017-02-21
  • 1970-01-01
  • 2017-02-12
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-05
相关资源
最近更新 更多