如何在PowerShell中更改环境变量并启动应用程序

5
在PowerShell中,我需要更改系统路径变量:
$oldPath = [System.Environment]::GetEnvironmentVariable('Path', 'Machine')
[System.Environment]::SetEnvironmentVariable('Path', (Transform-It $oldPath), 'Machine')

然后启动程序:

& $someExeName 'someargument'

我遇到的问题是可执行文件使用的是旧环境,无法识别新路径。如果我改变$env.path,只为此PowerShell会话更改路径,它也不会传播到新进程中。如果我关闭PowerShell并在新窗口中启动可执行文件,则一切正常。可能是从PowerShell进程继承了(未更新的)环境。
在PowerShell中更改环境变量并使新进程识别它的最佳实践是什么?
1个回答

11

通常情况下,子进程会继承父进程的环境变量*。如果你从一个已存在的PowerShell会话中创建一个新的PowerShell会话,新会话将继承该会话的环境变量(但不包括其他变量)。

但是,Path变量是一个特例,它经常引起混淆:虽然它是一个环境变量,但新的PowerShell会话会从注册表键HKLM:\System\CurrentControlSet\Control\Session Manager\Environment读取其值,覆盖从父会话继承的值。

  • 这种行为只适用于Path变量。其他环境变量不受影响,无论它们是在父会话中定义还是存储在前述注册表键中。

  • 此行为也仅适用于PowerShell。

    » 如果你在PowerShell会话中更改了Path,但没有在注册表中更改,并且创建了一个新的PowerShell会话(例如使用start powershell),则新会话将具有来自注册表的路径,但如果你创建了一个cmd会话,则新会话将具有从创建它的PowerShell会话继承的路径。

    » 同样地,如果你在cmd会话中更改了Path(使用set Path=New Path),并创建一个PowerShell会话,则新会话将具有来自注册表的路径,但如果你创建一个cmd会话,则它将具有父cmd会话中更改后的路径。

    » 默认行为是从父进程继承路径(以及环境变量的其余部分)(与cmd一样)。但是,很可能有其他程序的行为类似于PowerShell,用注册表值覆盖继承的值。这种行为不常见,但不能排除这种情况发生在你的可执行文件上。

以下命令将在当前会话中更改Path,而不在注册表中更改:

$env:Path = 'New path'

[System.Environment]::SetEnvironmentVariable('Path', 'New Path', 'Process')
以下命令将更改注册表中的 Path,而不是当前会话:
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Name 'Path' -Value 'New Path'

[System.Environment]::SetEnvironmentVariable('Path', 'New Path', 'Machine')
你所描述的对我来说没有意义,因为你说你尝试了一种方法改变注册表中的路径,另一种方法改变PowerShell会话中的路径,并且无论哪种方式,你产生的可执行文件都没有更改过的路径。据我所知,原始环境没有被缓存到任何地方,子进程必须从父进程的环境或注册表中获取路径。我的建议是,在启动可执行文件之前务必确保已经通过两种方式都更改了路径:
  1. 打开一个PowerShell会话
  2. 使用其中一种在会话中更改路径的方法更改路径
  3. 使用其中一种在注册表中更改路径的方法更改路径
  4. 启动可执行文件
如果出现某些莫名其妙的原因导致这不起作用,请尝试以下操作:
  1. 打开一个PowerShell会话
  2. 使用其中一种在注册表中更改路径的方法更改路径
  3. 而不是直接从该PowerShell会话启动可执行文件,执行此命令:

    powershell "& $someExeName 'someargument'"
    

    通过一个新的非交互式PowerShell会话启动可执行文件,该会话将从注册表中读取 Path 环境变量。


*请注意,在Windows中,环境的继承是父子进程之间唯一的关系。除此之外,它们完全独立(与Unix和Linux不同,没有层次结构)。


我认为'Machine'参数(EnvironmentVariableTarget)表示应该将新值保存在HKLM注册表中。 - Craig Celeste
因为它不会在当前会话中更改路径,所以当您将相同的注册表项设置为 $env:Path 时,它具有轻松地将其改回的额外好处。 - TheMadTechnician
@ParchedSquid 实际上,我刚刚尝试使用 SetEnvironmentVariableMachine 参数设置路径,然后生成了一个新的 PowerShell 会话,它确实有了新的路径(原始 PowerShell 会话中的路径没有更改)。我将更新答案以澄清关于 Path 变量和来自 PowerShell 的子进程的问题,但请澄清这一点:您是否尝试在注册表和会话中同时更改路径,然后从该会话启动可执行文件,还是分别尝试了这两种方法? - Adi Inbar
谢谢Adi。我先分别尝试了这两种方法。解决方案是使用SetEnvironmentVariable a)和Machine b)。可能还有其他解决方案,但我选择不使用注册表。当产品(操作系统、应用程序等)提供适当的API与之交互时,我通常不喜欢深入研究其内部机制。你的建议帮了我很多,我将其标记为答案。 - Craig Celeste
在PowerShell中是否可以像Linux中使用X=Y launch-process一样为单个命令设置环境变量? - CMCDragonkai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接