这可能是一个简单的问题,但我找不到正确的答案。
如何在当前 Windows PowerShell 会话中更新环境变量而不关闭当前会话?
到目前为止,当我从Control Panel > SystemPATH
修改环境变量时,我必须关闭当前会话并打开一个新会话,以便刷新变量,或者发出一个很麻烦的问题。SetEnviromentVariable
我来自 Linux 世界,所以我正在寻找类似source
命令的东西。
这可能是一个简单的问题,但我找不到正确的答案。
如何在当前 Windows PowerShell 会话中更新环境变量而不关闭当前会话?
到目前为止,当我从Control Panel > SystemPATH
修改环境变量时,我必须关闭当前会话并打开一个新会话,以便刷新变量,或者发出一个很麻烦的问题。SetEnviromentVariable
我来自 Linux 世界,所以我正在寻找类似source
命令的东西。
没有必要在注册表中为这些东西去垃圾箱潜水:
foreach($level in "Machine","User") {
[Environment]::GetEnvironmentVariables($level)
}
如果你想让 PATH 变量正常工作,你必须特别对待它们(也就是说,将(可能)新的东西连接到你已经拥有的东西上,而不是丢失任何东西,因为你无法确定你的应该删除)。
foreach($level in "Machine","User") {
[Environment]::GetEnvironmentVariables($level).GetEnumerator() | % {
# For Path variables, append the new values, if they're not already in there
if($_.Name -match 'Path$') {
$_.Value = ($((Get-Content "Env:$($_.Name)") + ";$($_.Value)") -split ';' | Select -unique) -join ';'
}
$_
} | Set-Content -Path { "Env:$($_.Name)" }
}
请注意, Set-Content 实际上在您的流程环境中设置变量,与执行类似的操作相同$env:Temp = Convert-Path ~\AppData\Local\Temp
环境会在进程启动时填充,这就是您需要重新启动进程的原因。
您可以通过从注册表中读取环境来更新环境。也许使用如下脚本:
function Update-Environment {
$locations = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'HKCU:\Environment'
$locations | ForEach-Object {
$k = Get-Item $_
$k.GetValueNames() | ForEach-Object {
$name = $_
$value = $k.GetValue($_)
if ($userLocation -and $name -ieq 'PATH') {
Env:\Path += ";$value"
} else {
Set-Item -Path Env:\$name -Value $value
}
}
$userLocation = $true
}
}
(未经测试)
这是一个老问题,但如果你使用Chocolatey,它还会安装方便的refreshenv
命令,这对我来说很好用。
如果您只想更新当前 PowerShell 会话的路径,请执行以下操作:
$env:Path += ";<new path>"
如果您需要更新 PATH 环境变量,以便它在新的 PowerShell 会话中保持不变,请使用:
[Environment]::SetEnvironmentVariable("PATH", $env:Path + ";<new path>", 'User')
如果要更改所有用户的路径,请将“用户”更改为“机器”。
不幸的是,其他答案缺少一些重要的细节。PSModulePath 在 PowerShell 中有一些特殊处理,在更新环境变量时需要考虑这些处理。此外,可以添加、修改或删除变量。并且您需要考虑路径环境变量。
更糟糕的是,根据您在执行此操作时所处的产品/主机,该产品可能有一些自己的特殊环境变量处理应该考虑,除了希望 Path 环境变量(那些包含多个值, 分号分隔)实际上包含“路径”作为其名称的一部分,您可能会错过一些东西(当然,除非这是 Windows 在内部处理这些环境变量的方式,这可能是,我没有深入挖掘兔子洞)。
这是我想出的一个脚本,它与我认为您可以在当前 PowerShell 会话中正确更新环境变量而无需重新启动它一样接近:
# Get all environment variables for the current Process, as well as System and User environment variable values
$processValues = [Environment]::GetEnvironmentVariables('Process')
$machineValues = [Environment]::GetEnvironmentVariables('Machine')
$userValues = [Environment]::GetEnvironmentVariables('User')
# Identify the entire list of environment variable names first
$envVarNames = ($machineValues.Keys + $userValues.Keys + 'PSModulePath') | Sort-Object | Select-Object -Unique
# Now process all of those keys, updating what exists and adding what is new
foreach ($envVarName in $envVarNames) {
if ($envVarName -eq 'PSModulePath') {
$pieces = @()
if ($PSVersionTable.PSVersion -ge [System.Version]'4.0') {
$pieces += Join-Path -Path ${env:ProgramFiles} -ChildPath 'WindowsPowerShell\Modules'
}
if (-not $userValues.ContainsKey($envVarName)) {
$pieces += Join-Path -Path ([Environment]::GetFolderPath('Documents')) -ChildPath 'WindowsPowerShell\Modules'
} else {
$pieces += $userValues[$envVarName] -split ';'
}
if ($machineValues.ContainsKey($envVarName)) {
$pieces += $machineValues[$envVarName] -split ';'
}
[Environment]::SetEnvironmentVariable($envVarName,($pieces -join ';'),'Process')
} elseif ($envVarName -match 'path') {
$pieces = @()
if ($userValues.ContainsKey($envVarName)) {
$pieces += $userValues[$envVarName] -split ';'
}
if ($machineValues.ContainsKey($envVarName)) {
$pieces += $machineValues[$envVarName] -split ';'
}
[Environment]::SetEnvironmentVariable($envVarName,($pieces -join ';'),'Process')
} elseif ($userValues.ContainsKey($envVarName)) {
[Environment]::SetEnvironmentVariable($envVarName,$userValue[$envVarName],'Process')
} elseif ($machineValues.ContainsKey($envVarName)) {
[Environment]::SetEnvironmentVariable($envVarName,$machineValue[$envVarName],'Process')
}
}
# Lastly remove the environment variables that no longer exist
foreach ($envVarName in $processValues.Keys | Where-Object {$envVarNames -notcontains $_}) {
Remove-Item -LiteralPath "env:${envVarName}"
}
请注意,这在很大程度上未经测试,但原理是合理的,并且它基于我过去在该领域所做的工作。
谢谢@Joey 的回答(我标记为正确的:))请注意,鉴于我想在那一刻根据注册表刷新所有变量,我摆脱了 if-else 子句......
我仔细检查了注册表变量位置,根据Where are environment variables stored in registry?
此处使用的片段只是为了完整性....
function Update-Environment {
$locations = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'HKCU:\Environment'
$locations | ForEach-Object {
$k = Get-Item $_
$k.GetValueNames() | ForEach-Object {
$name = $_
$value = $k.GetValue($_)
Set-Item -Path Env:\$name -Value $value
}
}
}