
[英]Setting environment variables as dependent on other env vars on windows via powershell
[英]Setting Windows PowerShell environment variables
我发现设置 PATH 环境变量只会影响旧的命令提示符。 PowerShell 似乎有不同的环境设置。 如何更改 PowerShell (v1) 的环境变量?
笔记:
我想让我的更改永久化,这样我就不必每次运行 PowerShell 时都进行设置。PowerShell 是否有配置文件? 类似于 Unix 上的 Bash 个人资料?
如果在 PowerShell 会话期间的某个时间,您需要查看或临时修改 PATH 环境变量,您可以键入以下命令之一:
$env:Path # shows the actual content
$env:Path = 'C:\foo;' + $env:Path # attach to the beginning
$env:Path += ';C:\foo' # attach to the end
可以使用env: namespace / drive
信息来更改实际的环境变量。 例如,此代码将更新路径环境变量:
$env:Path = "SomeRandomPath"; (replaces existing path)
$env:Path += ";SomeRandomPath" (appends to existing path)
有一些方法可以使环境设置永久化,但如果您只在 PowerShell 中使用它们,那么使用Powershell配置文件脚本可能要好得多。
每次启动一个新的 Powershell 实例时,它都会查找特定的脚本文件(命名配置文件)并在它们存在时执行它们。 您可以编辑其中一个配置文件来自定义您的环境。
要了解这些配置文件脚本在您的计算机类型中的位置:
$profile
$profile.AllUsersAllHosts
$profile.AllUsersCurrentHost
$profile.CurrentUserAllHosts
$profile.CurrentUserCurrentHost
您可以编辑其中之一,例如,通过键入:
notepad $profile
您还可以使用以下内容永久修改用户/系统环境变量(即在 shell 重新启动时将保持不变):
修改系统环境变量
[Environment]::SetEnvironmentVariable
("Path", $env:Path, [System.EnvironmentVariableTarget]::Machine)
修改用户环境变量
[Environment]::SetEnvironmentVariable
("INCLUDE", $env:INCLUDE, [System.EnvironmentVariableTarget]::User)
注释中的用法 - 添加到系统环境变量
[Environment]::SetEnvironmentVariable(
"Path",
[Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::Machine) + ";C:\bin",
[EnvironmentVariableTarget]::Machine)
如果您不想编写类型,也可以使用基于字符串的解决方案
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\bin", "Machine")
警告:通过在 PowerShell 提示符下执行$env:path >> a.out
保存现有路径的副本,以防出现问题。
从 PowerShell 提示符:
setx PATH "$env:path;\the\directory\to\add" -m
然后你应该看到文本:
SUCCESS: Specified value was saved.
重新启动会话,变量将可用。 setx
也可用于设置任意变量。 setx /?
在文档提示时。
就像JeanT 的回答一样,我想要一个关于添加路径的抽象。 与 JeanT 的回答不同,我需要它在没有用户交互的情况下运行。 我正在寻找的其他行为:
$env:Path
以便更改在当前会话中生效如果它有用,这里是:
function Add-EnvPath {
param(
[Parameter(Mandatory=$true)]
[string] $Path,
[ValidateSet('Machine', 'User', 'Session')]
[string] $Container = 'Session'
)
if ($Container -ne 'Session') {
$containerMapping = @{
Machine = [EnvironmentVariableTarget]::Machine
User = [EnvironmentVariableTarget]::User
}
$containerType = $containerMapping[$Container]
$persistedPaths = [Environment]::GetEnvironmentVariable('Path', $containerType) -split ';'
if ($persistedPaths -notcontains $Path) {
$persistedPaths = $persistedPaths + $Path | where { $_ }
[Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
}
}
$envPaths = $env:Path -split ';'
if ($envPaths -notcontains $Path) {
$envPaths = $envPaths + $Path | where { $_ }
$env:Path = $envPaths -join ';'
}
}
查看我的要点以获取相应的Remove-EnvPath
函数。
注意1:在提升的powershell上运行这些命令(例如具有管理员权限)
注意 2:在使您的命令正常工作的每一步之后,关闭会话并再次打开它
不要让自己头疼,想要一个简单的单行解决方案:
添加永久环境变量:
[Environment]::SetEnvironmentVariable("NewEnvVar", "NewEnvValue", "Machine")
修改/更改:
[Environment]::SetEnvironmentVariable("oldEnvVar", "NewEnvValue", "Machine")
删除/删除:
[Environment]::SetEnvironmentVariable("oldEnvVar", "", "Machine")
尽管当前接受的答案在路径变量从 PowerShell 的上下文中永久更新的意义上起作用,但它实际上并没有更新存储在 Windows 注册表中的环境变量。
为此,您显然也可以使用 PowerShell:
$oldPath=(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path
$newPath=$oldPath+’;C:\NewFolderToAddToTheList\’
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH –Value $newPath
更多信息在博文中使用 PowerShell 修改您的环境路径
如果您使用 PowerShell 社区扩展,则将路径添加到环境变量路径的正确命令是:
Add-PathVariable "C:\NewFolderToAddToTheList" -Target Machine
所有暗示永久更改的答案都有相同的问题:它们破坏了路径注册表值。
SetEnvironmentVariable
将REG_EXPAND_SZ
值%SystemRoot%\system32
转换为C:\Windows\system32
的REG_SZ
值。
路径中的任何其他变量也会丢失。 使用%myNewPath%
添加新的将不再起作用。
这是我用来解决此问题的脚本Set-PathVariable.ps1
:
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[parameter(Mandatory=$true)]
[string]$NewLocation)
Begin
{
#requires –runasadministrator
$regPath = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
$hklm = [Microsoft.Win32.Registry]::LocalMachine
Function GetOldPath()
{
$regKey = $hklm.OpenSubKey($regPath, $FALSE)
$envpath = $regKey.GetValue("Path", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
return $envPath
}
}
Process
{
# Win32API error codes
$ERROR_SUCCESS = 0
$ERROR_DUP_NAME = 34
$ERROR_INVALID_DATA = 13
$NewLocation = $NewLocation.Trim();
If ($NewLocation -eq "" -or $NewLocation -eq $null)
{
Exit $ERROR_INVALID_DATA
}
[string]$oldPath = GetOldPath
Write-Verbose "Old Path: $oldPath"
# Check whether the new location is already in the path
$parts = $oldPath.split(";")
If ($parts -contains $NewLocation)
{
Write-Warning "The new location is already in the path"
Exit $ERROR_DUP_NAME
}
# Build the new path, make sure we don't have double semicolons
$newPath = $oldPath + ";" + $NewLocation
$newPath = $newPath -replace ";;",""
if ($pscmdlet.ShouldProcess("%Path%", "Add $NewLocation")){
# Add to the current session
$env:path += ";$NewLocation"
# Save into registry
$regKey = $hklm.OpenSubKey($regPath, $True)
$regKey.SetValue("Path", $newPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
Write-Output "The operation completed successfully."
}
Exit $ERROR_SUCCESS
}
我在博客文章中更详细地解释了这个问题。
这会设置当前会话的路径并提示用户永久添加它:
function Set-Path {
param([string]$x)
$Env:Path+= ";" + $x
Write-Output $Env:Path
$write = Read-Host 'Set PATH permanently ? (yes|no)'
if ($write -eq "yes")
{
[Environment]::SetEnvironmentVariable("Path",$env:Path, [System.EnvironmentVariableTarget]::User)
Write-Output 'PATH updated'
}
}
您可以将此函数添加到您的默认配置文件 ( Microsoft.PowerShell_profile.ps1
),通常位于%USERPROFILE%\Documents\WindowsPowerShell
。
我的建议是这个:
我已经对此进行了测试,以将C:\oracle\x64\bin
永久添加到环境变量Path
中,并且效果很好。
$ENV:PATH
第一种方法很简单:
$ENV:PATH=”$ENV:PATH;c:\path\to\folder”
但这种变化不是永久性的。 只要您关闭 PowerShell 终端并重新打开它, $env:path
将默认恢复为之前的状态。 那是因为您在会话级别而不是在源级别(即注册表级别)应用了更改。 要查看$env:path
的全局值,请执行以下操作:
Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH
或者更具体地说:
(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path
现在要改变这一点,首先我们捕获需要修改的原始路径:
$oldpath = (Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path
现在我们定义新路径应该是什么样子。 在这种情况下,我们将附加一个新文件夹:
$newpath = “$oldpath;c:\path\to\folder”
注意:确保$newpath
看起来像您希望的样子。 如果没有,那么您可能会损坏您的操作系统。
现在应用新值:
Set-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH -Value $newPath
现在做最后一次检查,它看起来像你期望的那样:
(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).Path
您现在可以重新启动 PowerShell 终端(甚至重新启动机器)并查看它不会再次回滚到其旧值。
请注意,路径的顺序可能会发生变化,因此它是按字母顺序排列的,因此请确保检查整行。 为方便起见,您可以使用分号作为分隔符将输出拆分为行:
($env:path).split(“;”)
在@Michael Kropat 的回答的基础上,我添加了一个参数以将新路径添加到现有PATH
变量中,并进行检查以避免添加不存在的路径:
function Add-EnvPath {
param(
[Parameter(Mandatory=$true)]
[string] $Path,
[ValidateSet('Machine', 'User', 'Session')]
[string] $Container = 'Session',
[Parameter(Mandatory=$False)]
[Switch] $Prepend
)
if (Test-Path -path "$Path") {
if ($Container -ne 'Session') {
$containerMapping = @{
Machine = [EnvironmentVariableTarget]::Machine
User = [EnvironmentVariableTarget]::User
}
$containerType = $containerMapping[$Container]
$persistedPaths = [Environment]::GetEnvironmentVariable('Path', $containerType) -split ';'
if ($persistedPaths -notcontains $Path) {
if ($Prepend) {
$persistedPaths = ,$Path + $persistedPaths | where { $_ }
[Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
}
else {
$persistedPaths = $persistedPaths + $Path | where { $_ }
[Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
}
}
}
$envPaths = $env:Path -split ';'
if ($envPaths -notcontains $Path) {
if ($Prepend) {
$envPaths = ,$Path + $envPaths | where { $_ }
$env:Path = $envPaths -join ';'
}
else {
$envPaths = $envPaths + $Path | where { $_ }
$env:Path = $envPaths -join ';'
}
}
}
}
只有将值推送到注册表的答案才会影响永久更改(因此该线程上的大多数答案,包括接受的答案,都不会永久影响Path
)。
以下函数适用于Path
/ PSModulePath
和User
/ System
类型。 默认情况下,它还会将新路径添加到当前会话。
function AddTo-Path {
param (
[string]$PathToAdd,
[Parameter(Mandatory=$true)][ValidateSet('System','User')][string]$UserType,
[Parameter(Mandatory=$true)][ValidateSet('Path','PSModulePath')][string]$PathType
)
# AddTo-Path "C:\XXX" "PSModulePath" 'System'
if ($UserType -eq "System" ) { $RegPropertyLocation = 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' }
if ($UserType -eq "User" ) { $RegPropertyLocation = 'HKCU:\Environment' } # also note: Registry::HKEY_LOCAL_MACHINE\ format
$PathOld = (Get-ItemProperty -Path $RegPropertyLocation -Name $PathType).$PathType
"`n$UserType $PathType Before:`n$PathOld`n"
$PathArray = $PathOld -Split ";" -replace "\\+$", ""
if ($PathArray -notcontains $PathToAdd) {
"$UserType $PathType Now:" # ; sleep -Milliseconds 100 # Might need pause to prevent text being after Path output(!)
$PathNew = "$PathOld;$PathToAdd"
Set-ItemProperty -Path $RegPropertyLocation -Name $PathType -Value $PathNew
Get-ItemProperty -Path $RegPropertyLocation -Name $PathType | select -ExpandProperty $PathType
if ($PathType -eq "Path") { $env:Path += ";$PathToAdd" } # Add to Path also for this current session
if ($PathType -eq "PSModulePath") { $env:PSModulePath += ";$PathToAdd" } # Add to PSModulePath also for this current session
"`n$PathToAdd has been added to the $UserType $PathType"
}
else {
"'$PathToAdd' is already in the $UserType $PathType. Nothing to do."
}
}
# Add "C:\XXX" to User Path (but only if not already present)
AddTo-Path "C:\XXX" "User" "Path"
# Just show the current status by putting an empty path
AddTo-Path "" "User" "Path"
正如Jonathan Leaders在这里提到的那样,运行提升的命令/脚本以便能够更改'machine' 的环境变量很重要,但是运行提升的一些命令不必通过社区扩展来完成,所以我想以某种方式修改和扩展JeanT 的答案,即使脚本本身没有运行提升,也可以执行更改机器变量:
function Set-Path ([string]$newPath, [bool]$permanent=$false, [bool]$forMachine=$false )
{
$Env:Path += ";$newPath"
$scope = if ($forMachine) { 'Machine' } else { 'User' }
if ($permanent)
{
$command = "[Environment]::SetEnvironmentVariable('PATH', $env:Path, $scope)"
Start-Process -FilePath powershell.exe -ArgumentList "-noprofile -command $Command" -Verb runas
}
}
在 PowerShell 中,可以通过键入以下内容导航到环境变量目录:
Set-Location Env:
这会将您带到 Env:> 目录。 在此目录中:
要查看所有环境变量,请键入:
Env:\> Get-ChildItem
要查看特定环境变量,请键入:
Env:\> $Env:<variable name>, e.g. $Env:Path
要设置环境变量,请键入:
Env:\> $Env:<variable name> = "<new-value>", e.g. $Env:Path="C:\Users\"
要删除环境变量,请键入:
Env:\> remove-item Env:<variable name>, e.g. remove-item Env:SECRET_KEY
更多信息在关于环境变量中。
大多数答案都没有解决UAC 。 这涵盖了 UAC 问题。
首先安装 PowerShell Community Extensions: choco install pscx
via http://chocolatey.org/ (你可能需要重启你的 shell 环境)。
然后启用 pscx
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser #allows scripts to run from the interwebs, such as pcsx
然后使用Invoke-Elevated
Invoke-Elevated {Add-PathVariable $args[0] -Target Machine} -ArgumentList $MY_NEW_DIR
明确一点,1990 年代的 Windows 方式点击开始,右键点击这台电脑,选择属性,然后选择高级系统设置,然后在弹出的对话框中选择环境变量,在列表中双击在PATH上,然后使用New 、 Edit 、 Move Up和Move Down仍然可以更改 PATH。 Power shell 和 Windows 的其余部分可以获取您在此处设置的任何内容。
是的,您可以使用这些新方法,但旧方法仍然有效。 在基础级别,所有永久更改方法都是编辑注册表文件的受控方式。
打开 PowerShell 并运行:
[Environment]::SetEnvironmentVariable("PATH", "$ENV:PATH;<path to exe>", "USER")
我依赖 PowerShell 的类型强制,它会自动将字符串转换为枚举值,所以我没有定义查找字典。
我还拉出了根据条件将新路径添加到列表的块,这样工作就完成了一次并存储在一个变量中以供重复使用。
然后根据$PathContainer
参数将其永久应用或仅应用于 Session。
我们可以将代码块放在我们直接从命令提示符调用的函数或 ps1 文件中。 我选择了 DevEnvAddPath.ps1。
param(
[Parameter(Position=0,Mandatory=$true)][String]$PathChange,
[ValidateSet('Machine', 'User', 'Session')]
[Parameter(Position=1,Mandatory=$false)][String]$PathContainer='Session',
[Parameter(Position=2,Mandatory=$false)][Boolean]$PathPrepend=$false
)
[String]$ConstructedEnvPath = switch ($PathContainer) { "Session"{${env:Path};} default{[Environment]::GetEnvironmentVariable('Path', $containerType);} };
$PathPersisted = $ConstructedEnvPath -split ';';
if ($PathPersisted -notcontains $PathChange) {
$PathPersisted = $(switch ($PathPrepend) { $true{,$PathChange + $PathPersisted;} default{$PathPersisted + $PathChange;} }) | Where-Object { $_ };
$ConstructedEnvPath = $PathPersisted -join ";";
}
if ($PathContainer -ne 'Session')
{
# Save permanently to Machine, User
[Environment]::SetEnvironmentVariable("Path", $ConstructedEnvPath, $PathContainer);
}
# Update the current session
${env:Path} = $ConstructedEnvPath;
我为 DevEnvRemovePath.ps1 做了类似的事情。
param(
[Parameter(Position=0,Mandatory=$true)][String]$PathChange,
[ValidateSet('Machine', 'User', 'Session')]
[Parameter(Position=1,Mandatory=$false)][String]$PathContainer='Session'
)
[String]$ConstructedEnvPath = switch ($PathContainer) { "Session"{${env:Path};} default{[Environment]::GetEnvironmentVariable('Path', $containerType);} };
$PathPersisted = $ConstructedEnvPath -split ';';
if ($PathPersisted -contains $PathChange) {
$PathPersisted = $PathPersisted | Where-Object { $_ -ne $PathChange };
$ConstructedEnvPath = $PathPersisted -join ";";
}
if ($PathContainer -ne 'Session')
{
# Save permanently to Machine, User
[Environment]::SetEnvironmentVariable("Path", $ConstructedEnvPath, $PathContainer);
}
# Update the current session
${env:Path} = $ConstructedEnvPath;
到目前为止,它们似乎有效。
很多附加或覆盖的例子。 这是一个在 Powershell 上为 Linux、Ubuntu 18.04 和pwsh
7.1.3 添加路径的示例
$ENV:PATH = "/home/linuxbrew/.linuxbrew/bin:$ENV:PATH"
我专门添加了 linuxbrew (homebrew for linux) bin 目录以优先于已安装的系统。 它帮助解决了我遇到的一个问题,虽然这是最有帮助的地方,但它也让我“实验”了。
请注意, :
是 Linux 路径分隔符,而在 Windows(或至少我的 Windows)上,您将使用;
通常用于powershell。
在@ali Darabi 的答案中编辑注册表项对我来说效果最好,但是当我没有从Powershell 执行此操作的正确权限时。 所以我直接在regedit中编辑了。
我想在这个答案中进一步扩展这个主题。
重新启动 Powershell 也不足以传播更改。 我必须打开任务管理器并重新启动 explorer.exe 才能触发重新加载注册表。
导航注册表可能非常繁琐,因此为了保持用户友好的体验,您可以从 Powershell 执行此操作:
REG ADD "HKCU\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit" /v "LastKey" /d "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment" /f; regedit
它将最后打开的窗口设置为某个注册表路径,以便下次打开 regedit 时它会以正确的键打开。
如果您需要动态设置变量名称并且仅用于会话,请使用:
New-Item env:\$key -Value $value -Force | Out-Null
这些脚本是幂等的(可以多次运行)。
他们更新了 Windows 路径和当前/未来的 Powershell 会话:
$targetDir="c:\bin"
$oldPath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path
$oldPathArray=($env:Path) -split ';'
if(-Not($oldPathArray -Contains "$targetDir")) {
write-host "Adding $targetDir to Windows paths"
$newPath = "$oldPath;$targetDir"
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value "$newPath"
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
}
write-host "Windows paths:"
($env:Path).Replace(';',"`n")
$targetDir="c:\bin"
$oldPath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path
$oldPathArray=($env:Path) -split ';'
if($oldPathArray -Contains "$targetDir") {
write-host "Removing $targetDir from Windows paths"
$newPathArray = $oldPathArray | Where-Object { $_ –ne "$targetDir" }
$newPath = $newPathArray -join ";"
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value "$newPath"
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
}
write-host "Windows paths:"
($env:Path).Replace(';',"`n")
我发现将C:\vcpkg
永久添加到我的PATH
环境变量中而没有缺点的最简单的解决方案是:
$current_PATH = [Environment]::GetEnvironmentVariable("PATH", "USER");[Environment]::SetEnvironmentVariable("PATH", "$current_PATH;C:\vcpkg;", "USER")
您可以将"USER"
更改为"MACHINE"
以更改系统环境变量(需要一个管理终端,您可能需要将Environment
更改为System.Environment
)或者甚至更改为"PROCESS"
以仅更改本地PATH
环境变量(不是永久更改) . 分别是"USER"=1
"MACHINE"=2
和"PROCESS"=0
这里是关于这些命令的文档: GetEnvironmentVariable SetEnvironmentVariable
我找到的另外两个答案有很大的缺点,我不建议使用它们。 两者都使用SETX
,因为它由 PowerShell 实现以永久更改环境变量。 这些命令的缺点是您会将系统 PATH 复制到您的语言环境 PATH 并且您需要 PowerShell 才能使用它们:
setx PATH "$($Env:PATH);C:\vcpkg;"
更长但允许与其他环境变量一起使用:
$($Env:PATH).Split(';') | %{ $str += "$($_.Trim('"'));" }; %{ $str += "C:\vcpkg;" } ; setx PATH $str; %{ $str = "" }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.