首页 > 解决方案 > Windows $env:path ="$($env:path);." 它是在哪里添加的?

问题描述

$env:path ="$($env:path);."我通过从 PowerShell运行“修复”了一个问题。显然它将当前目录添加到我的路径中。请问它添加了哪个路径变量?在我的环境变量对话中,我会在哪里看到它添加?用户变量?系统变量?

我很困惑,因为我已经将文件夹添加到系统变量path中,但是在运行 ``$env:path ="$($env:path);." 之前无法运行包含的脚本。

标签: powershellpathenvironment

解决方案


$env:EnvVarName仅影响当前进程的更新- 不会通过注册表进行持久更改:

$env:EnvVarName = 'foo'

相当于调用 .NET 方法System.Environment.SetEnvironmentVariable,如下所示:

[Environment]::SetEnvironmentVariable('EnvVarName', 'foo', 'Process')

即更新的范围是当前进程

仅当您在上述调用中替换'User''Machine'替换(仅在 Windows [1]上受支持)时,您才会持续更新注册表中的环境变量'Process'(分别针对当前用户或本地计算机(所有用户)),以用于将来的会话(进程) [2]

从 PowerShell [Core] 7.2 开始,没有PowerShell 原生方式来持续更新环境变量,但在 GitHub 上正在讨论引入一种方式。

换句话说:如果您不仅要更新基于注册表的定义,还要更新当前进程中的值,则需要进行两次调用;例如,对于当前用户:

# Windows only: Update / create a persistent definition for the current user,
#               stored in the registry.
[Environment]::SetEnvironmentVariable('EnvVarName', 'foo', 'User')

# Update value for current process too.
$env:EnvVarName = 'foo'

或者,更符合DRY的精神:

'User', 'Process' | foreach {
  [Environment]::SetEnvironmentVariable('EnvVarName', 'foo', $_)
}

如果新值基于给定注册表范围中的现有值,请通过System.Environment.GetEnvironmentVariable;检索特定于范围的值 例如:

# Get the registry-based *user* value 
[Environment]::GetEnvironmentVariable('Path', 'User')

警告:不支持基于 REG_EXPAND_SZ注册表值的 Windows 环境变量:

在 Windows 上,可以基于其他环境变量定义持久定义的环境变量,即定义变量的底层注册表值是否为REG_EXPAND_SZ.

从 .NET 6 开始,该System.Environment类型的方法(直接)支持此类环境变量:

  • 获得这样一个变量的值时,它的扩展形式总是被返回;也就是说,对其他环境变量的引用%SystemRoot%被它们的值替换。

  • 设置环境变量时,总是会REG_SZ创建注册表值,即静态的逐字记录值——即使在更新现有值时也是如此。REG_EXPAND_SZ

虽然悄悄地将REG_EXPAND_SZ环境变量转换为静态变量REG_SZ通常不会产生不良影响(只要新值只包含文字值),它当然可以:例如,假设一个变量是根据%JAVADIR%; 如果该变量基于 的当前值转换为静态值,则如果稍后更改%JAVADIR%的值,它将停止工作。%JAVADIR%

不幸的是,目前检索原始REG_EXPAND_SZ环境变量和正确更新它们的值需要直接访问注册表,这非常麻烦(甚至 Windows API 似乎都不支持它) - 请参阅此答案


Windows 上Path环境变量 ( )的重要注意事项:$env:PATH

  • 环境变量的Path特殊之处在于它是一个复合值:当一个进程启动时,进程内值是(本地机器,对于所有用户)值和(当前用户)值的串联。MachineUser

    • 请注意,由于机器级值首先出现,因此其条目优先于用户级值的条目。
  • 因此,如果您想修改(附加到)现有Path的 ,最好不要简单地通过附加到现有的进程中值 ( $env:Path) 来定义新值,因为您将复制MachineorUser值,具体取决于您的范围目标。

    • 相反,有选择地从目标范围检索现有值,修改该值(通常通过附加目录,然后将修改后的值写回同一范围。

    • 考虑到进程中的副本可能已被修改,要使相同的修改在当前进程中稳健地生效也很重要;$env:Path但是,在将新目录附加到用户路径的简单情况下,您可以简单地执行$env:Path += ';' + $newDir; 在其他情况下,您也可以使用这种简单的方法,但请注意,考虑到列出目录的顺序$env:Path很重要,行为可能会有所不同。

重要提示:默认情况下, PathWindows 上的环境变量是REG_EXPAND_SZ基于 - 的,因此需要注意REG_SZ以下代码执行的静默转换为基于静态的值 - 再次,请参阅此答案以获得正确但更复杂的解决方案。

例子:

# New dir. to add to the *user's* path
$newDir = 'c:\foo\bin'

# Get current value *from the registry*
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')

# Append the new dir and save back to the registry.
[Environment]::SetEnvironmentVariable('Path', ($userPath + ';' + $newDir), 'User')

# To also update the current process, append $newDir to the in-process
# variable, $env:Path
$env:Path += ';' + $newDir

顺便说一句:在类 Unix 平台上,分隔符是:,而不是;( 反映在 中[System.IO.Path]::PathSeparator区分大小写的变量名是Path。如上所述,.NET 从根本上不提供类 Unix 平台上的持久环境变量定义(如.NET Core 3.1),因为各种平台没有统一的本地机制来执行此操作。


[1] 在类 Unix 平台上,从 .NET Core 3.1 开始,定位或被User悄悄忽略Machine

[2]警告:由当前 PowerShell 会话(直接调用、、)直接创建的新进程Start-Process尚未Start-Job看到注册表更改,因为它继承了当前会话的环境。


推荐阅读