我发现设置PATH环境变量只影响旧的命令提示符。PowerShell似乎有不同的环境设置。如何更改PowerShell (v1)的环境变量?

注意:

我希望我的更改是永久性的,这样我就不必每次运行PowerShell时都设置它。PowerShell有配置文件吗?比如Unix上的Bash配置文件?


当前回答

所有建议永久更改的答案都有相同的问题:它们破坏了路径注册表值。

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
 }

我在一篇博文中更详细地解释了这个问题。

其他回答

基于@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 ';'
            }
        }
    }
}

正如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
    }

}

如果你需要动态地为会话设置变量名,那么使用:

New-Item env:\$key -Value $value -Force | Out-Null

无痛苦的、一行的示例解决方案

尝试这三个命令来练习在PowerShell中设置和删除环境变量。

使用注意事项:

在提升的PowerShell上运行这些命令(例如,具有管理员权限)。 在每一步之后,为了使您的命令工作,关闭会话并再次打开它。

添加/创建永久环境变量:

[Environment]::SetEnvironmentVariable("MyEnvVar", "NewEnvValue", "Machine")

Machine是一个环境变量目标,将应用于当前和未来的用户,而不是User目标。


修改/改变环境变量:

[Environment]::SetEnvironmentVariable("MyEnvVar", "NewerEnvValue", "Machine")

删除/删除变量:

[Environment]::SetEnvironmentVariable("MyEnvVar", "", "Machine")

我发现将C:\vcpkg永久添加到我的PATH env变量中而没有缺点的最简单的解决方案是:

$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以下是关于这些命令的文档

我发现了另外两个答案,但有很大的缺点,我不建议使用它们。两者都使用SETX,因为它是由PowerShell实现的,以永久地改变env变量。这些命令的缺点是,你将复制你的系统路径到你的区域路径,你需要PowerShell来使用它们:

setx PATH "$($Env:PATH);C:\vcpkg;"

更长,但允许使用其他env变量:

$($Env:PATH).Split(';') | %{ $str += "$($_.Trim('"'));" }; %{ $str += "C:\vcpkg;" } ; setx PATH $str; %{ $str = "" }