use*_*882 10 windows powershell environment-variables
我遵循此过程,以便使用 powershell 永久添加 SumatraPDF 的路径。链接中的最后几个命令旨在检查路径是否确实已添加。
当我使用以下命令访问路径时,
(get-itemproperty -path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path.split(';')
Run Code Online (Sandbox Code Playgroud)
结果包括 SumatraPDF 的路径
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Windows\System32\OpenSSH\
C:\ProgramData\chocolatey\bin
C:\texlive\2021\bin\win32
C:\Users\921479\AppData\Local\SumatraPDF
Run Code Online (Sandbox Code Playgroud)
但是,当我使用以下命令访问它时,
($env:path).split(';')
Run Code Online (Sandbox Code Playgroud)
结果不包含 SumatraPDF 的路径:
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Windows\System32\OpenSSH\
C:\ProgramData\chocolatey\bin
C:\texlive\2021\bin\win32
C:\Users\921479\AppData\Local\Microsoft\WindowsApps
Run Code Online (Sandbox Code Playgroud)
最后,实际传递sumatrapdf
不起作用,这向我表明真正的路径是使用get-itemproperty
命令访问的路径。
为什么注册表中设置的路径与中设置的路径不对应$env:path
?我所遵循的链接中显示的过程是否有错误?我怎样才能纠正它?
我应该提到我已经尝试重新启动外壳,但它没有帮助。
mkl*_*nt0 23
前言:
请参阅中间部分的辅助函数Add-Path
请参阅底部部分,了解为什么setx.exe
应避免使用来更新Path
环境变量。
链接博客文章中的过程原则上是有效的,但缺少一条关键信息/附加步骤:
如果您直接通过注册表修改环境变量- 不幸的是,这是基于环境变量的正确方法,REG_EXPAND_SZ
例如Path
-您需要广播一条WM_SETTINGCHANGE
消息,以便 Windows (GUI) shell(及其组件、文件资源管理器) 、任务栏、桌面、开始菜单,所有这些都是通过explorer.exe
进程提供的)收到环境更改的通知,并从注册表重新加载其环境变量。随后启动的应用程序将继承更新后的环境。
不幸的是,没有直接的方法可以从 PowerShell 执行此操作,但有一些解决方法:
暴力解决方法 - 简单,但视觉上具有破坏性,并关闭所有打开的文件资源管理器窗口:
# Kills all explorer.exe processes, which restarts the Windows shell
# components, forcing a reload of the environment from the registry.
Stop-Process -Name explorer
Run Code Online (Sandbox Code Playgroud)
通过.NET API 的解决方法:
# Create a random name assumed to be unique
[string] $dummyName = New-Guid
# Set an environment variable by that name, which makes .NET
# send a WM_SETTINGCHANGE broadcast
[Environment]::SetEnvironmentVariable($dummyName, 'foo', 'User')
# Now that the dummy variable has served its purpose, remove it again.
# (This will trigger another broadcast, but its performance impact is negligible.)
[Environment]::SetEnvironmentVariable($dummyName, [NullString]::value, 'User')
Run Code Online (Sandbox Code Playgroud)
[Environment]::SetEnvironmentVariable()
直接更新变量。PATH
解决方法是通过C# 中临时编译的 P/Invoke 调用来调用 Windows API ,方法是:SendMessageTimeout()
Add-Type
虽然这是一个正确的解决方案,但由于第一次在会话中运行时进行临时编译,它总是会产生明显的性能损失。
有关详细信息,请参阅此博文。
博客文章中的方法还有另一个问题:
Get-ItemProperty
环境变量值,因为这就是和Get-ItemPropertyValue
总是做的事情。也就是说,如果值中的目录是根据其他环境变量(例如,%SystemRoot%
或%JAVADIR%
)定义的,则返回值不再包含这些变量,而是包含它们的当前值。请参阅底部部分了解为什么这可能会出现问题。下一节中讨论的辅助函数可以解决所有问题,同时还确保修改对当前会话也生效。
以下Add-Path
辅助函数:
默认情况下,将给定的单个目录路径添加(追加)到持久用户级环境变量; Path
用于-Scope Machine
定位机器级定义,这需要提升(以管理员身份运行)。
如果目标变量中已存在该目录,则不执行任何操作。
相关的注册表值会根据现有的未扩展值进行更新,从而保留其数据类型- 也就是说,对其他环境变量的引用将被保留(例如,),并且也可以在添加的新条目中使用。REG_EXPAND_SZ
%SystemRoot%
触发WM_SETTINGCHANGE
消息广播以通知 Windows shell 更改。
还更新当前会话的 $env:Path
变量值。
不幸的是,PowerShell 没有附带此功能;之前关于提供 cmdlet 进行稳健和选择性更新的讨论$env:PATH
毫无进展 - 请参阅废弃的GitHub RFC #92(其中介绍了提供用于管理一般持久环境变量的 cmdlet)。[1]
注意:根据定义(由于使用注册表),此功能仅适用于 Windows。
通过定义下面的函数,您Path
可以按如下方式执行所需的添加,修改当前用户的持久Path
定义:
Add-Path C:\Users\921479\AppData\Local\SumatraPDF
Run Code Online (Sandbox Code Playgroud)
如果您确实想更新计算机级别的定义(在注册表配置单元中,这对于用户特定的路径HKEY_LOCAL_MACHINE
没有意义),请添加,但不是必须以管理员身份运行。-Scope Machine
Add-Path
源代码:
function Add-Path {
param(
[Parameter(Mandatory, Position=0)]
[string] $LiteralPath,
[ValidateSet('User', 'CurrentUser', 'Machine', 'LocalMachine')]
[string] $Scope
)
Set-StrictMode -Version 1; $ErrorActionPreference = 'Stop'
$isMachineLevel = $Scope -in 'Machine', 'LocalMachine'
if ($isMachineLevel -and -not $($ErrorActionPreference = 'Continue'; net session 2>$null)) { throw "You must run AS ADMIN to update the machine-level Path environment variable." }
$regPath = 'registry::' + ('HKEY_CURRENT_USER\Environment', 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment')[$isMachineLevel]
# Note the use of the .GetValue() method to ensure that the *unexpanded* value is returned.
$currDirs = (Get-Item -LiteralPath $regPath).GetValue('Path', '', 'DoNotExpandEnvironmentNames') -split ';' -ne ''
if ($LiteralPath -in $currDirs) {
Write-Verbose "Already present in the persistent $(('user', 'machine')[$isMachineLevel])-level Path: $LiteralPath"
return
}
$newValue = ($currDirs + $LiteralPath) -join ';'
# Update the registry.
Set-ItemProperty -Type ExpandString -LiteralPath $regPath Path $newValue
# Broadcast WM_SETTINGCHANGE to get the Windows shell to reload the
# updated environment, via a dummy [Environment]::SetEnvironmentVariable() operation.
$dummyName = [guid]::NewGuid().ToString()
[Environment]::SetEnvironmentVariable($dummyName, 'foo', 'User')
[Environment]::SetEnvironmentVariable($dummyName, [NullString]::value, 'User')
# Finally, also update the current session's `$env:Path` definition.
# Note: For simplicity, we always append to the in-process *composite* value,
# even though for a -Scope Machine update this isn't strictly the same.
$env:Path = ($env:Path -replace ';$') + ';' + $LiteralPath
Write-Verbose "`"$LiteralPath`" successfully appended to the persistent $(('user', 'machine')[$isMachineLevel])-level Path and also the current-process value."
}
Run Code Online (Sandbox Code Playgroud)
setx.exe
以及为什么不应该使用它来更新Path
环境变量:setx.exe
具有使其成为问题的基本限制,特别是对于更新基于REG_EXPAND_SZ
- 类型的注册表值的环境变量,例如Path
:
值限制为 1024 个字符,其他字符会被截断,尽管会出现警告(至少从 Windows 10 开始)。
如果新REG_SZ
%SystemRoot%
值不包含环境变量引用(例如. [2]然而,Path
最初是 类型,REG_EXPAND_SZ
并且确实包含基于其他环境变量的目录路径,例如%SystemRoot%
和%JAVADIR%
。
%JAVADIR%
值,最初依赖的条目将停止工作。%JAVADIR%
同样,如果您稍后添加以其他环境变量表示的条目,则此类条目将不会扩展。此外,如果更新的值基于当前会话的$env:Path
值,则最终会出现重复条目,因为进程级别 $env:Path
值是计算机级别值和当前用户级别值的组合。
这会增加遇到 1024 个字符限制的风险,特别是在重复使用该技术的情况下。在原始条目从原始范围中删除后,它还存在重复值残留的风险。
虽然您可以通过直接从注册表或(总是以扩展形式)通过[Environment]::GetEnvironmentVariable('Path', 'User')
or检索特定于范围的值来避免此特定问题[Environment]::GetEnvironmentVariable('Path', 'Machine')
,但这仍然不能解决REG_EXPAND_SZ
上面讨论的问题。
[1]用于管理持久性环境变量的新 cmdlet 的原型实现可通过PowerShell Gallery获得,尽管它似乎已日渐衰落,而且最重要的是,在撰写本文时缺乏对可扩展环境变量 ( REG_EXPAND_SZ
) 的支持,这使得它不适合PATH
更新。
[2] 也就是说,setx.exe
决定是(重新)创建基础注册表值REG_SZ
(静态)还是REG_EXPAND_SZ
纯粹基于环境变量引用的存在(例如新%SystemRoot%
值中的环境变量引用) - 无论预先存在的注册表值的当前类型如何。
归档时间: |
|
查看次数: |
12171 次 |
最近记录: |