设置Windows PowerShell环境变量

Setting Windows PowerShell environment variables

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

注:

我希望使更改永久化,因此不必每次运行PowerShell时都设置它。PowerShell是否有配置文件?像Unix上的bash配置文件?


如果在PowerShell会话期间,您需要临时附加到path环境变量,可以这样做:

1
$env:Path +=";C:\Program Files\GnuWin32\bin"


更改实际的环境变量可以通过使用env: namespace / drive信息。例如,这个代码将更新路径环境变量:

1
2
$env:Path ="SomeRandomPath";             (replaces existing path)
$env:Path +=";SomeRandomPath"            (appends to existing path)

有一些方法可以使环境设置永久化,但是如果您只在PowerShell中使用它们,可能是更好的方法是使用您的配置文件启动设置。启动时,PowerShell将运行任何.ps1在WindowsPowerShell目录下找到的文件我的文档文件夹。通常您有一个profile.ps1文件已经存在。我电脑上的路径是

1
C:\Users\JaredPar\Documents\WindowsPowerShell\profile.ps1


您还可以使用以下命令永久地修改用户/系统环境变量(即在重新启动shell时保持不变):

1
2
3
4
5
6
7
8
9
10
11
12
13
### Modify a system environment variable ###
[Environment]::SetEnvironmentVariable
     ("Path", $env:Path, [System.EnvironmentVariableTarget]::Machine)

### Modify a user environment variable ###
[Environment]::SetEnvironmentVariable
     ("INCLUDE", $env:INCLUDE, [System.EnvironmentVariableTarget]::User)

### Usage from comments - add to the system environment variable ###
[Environment]::SetEnvironmentVariable(
   "Path",
    [Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::Machine) +";C:\bin",
    [EnvironmentVariableTarget]::Machine)


从PowerShell提示:

1
setx PATH"$env:path;\the\directory\to\add" -m

然后您应该看到文本:

1
SUCCESS: Specified value was saved.

重新启动会话,变量将可用。setx也可用于设置任意变量。在提示文档时键入setx /?

以这种方式处理路径之前,请确保在PowerShell提示中执行$env:path >> a.out以保存现有路径的副本。


就像Jeant的答案一样,我想要一个关于添加到路径上的抽象概念。与Jeant的答案不同,我需要它在没有用户交互的情况下运行。我在寻找的其他行为:

  • 更新$env:Path,使更改在当前会话中生效。
  • 为将来的会话保留环境变量更改
  • 当同一路径已存在时不添加重复路径

如果有用的话,这里是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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功能。


虽然当前接受的答案在从PowerShell上下文永久更新path变量的意义上有效,但它实际上并不更新存储在Windows注册表中的环境变量。

要实现这一点,您显然也可以使用PowerShell:

1
2
3
4
5
6
$oldPath=(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

$newPath=$oldPath+’;C:
ewFolderToAddToTheList\’

Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH –Value $newPath

更多信息在博客文章中使用PowerShell修改您的环境路径

如果使用PowerShell社区扩展,则向环境变量path添加路径的正确命令是:

1
2
Add-PathVariable"C:
ewFolderToAddToTheList"
-Target Machine


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

SetEnvironmentVariableREG_EXPAND_SZ%SystemRoot%\system32变成C:\Windows\system32REG_SZ值。

路径中的任何其他变量也将丢失。使用%myNewPath%添加新的将不再有效。

下面是我用来解决这个问题的脚本Set-PathVariable.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
 [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
 }

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


这将设置当前会话的路径并提示用户永久添加它:

1
2
3
4
5
6
7
8
9
10
11
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中。


正如Jonathan Leaders在这里提到的,运行提升的命令/脚本是很重要的,以便能够更改"machine"的环境变量,但是运行一些提升的命令并不需要通过社区扩展来完成,所以我想以某种方式修改和扩展Jeant的答案,更改机器变量也可以在en如果脚本本身未被提升运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    }

}

大多数答案都不是针对UAC的。这涉及UAC问题。

首先通过http://chocoley.org/安装PowerShell社区扩展:choco install pscx(您可能需要重新启动shell环境)。

然后启用pscx

1
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser #allows scripts to run from the interwebs, such as pcsx

然后使用Invoke-Elevated

1
Invoke-Elevated {Add-PathVariable $args[0] -Target Machine} -ArgumentList $MY_NEW_DIR

基于@michael kropat的答案,我添加了一个参数来预先设置现有PATH变量的新路径,并检查以避免添加不存在的路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 ';'
            }
        }
    }
}


我的建议是这个我已经测试过将c:oraclex64in永久添加到路径中,这样可以正常工作。

1
$ENV:PATH

第一种方法很简单:

1
$ENV:PATH="$ENV:PATH;c:\path\to\folder"

但这种更改并不是永久性的,$env:path一旦关闭PowerShell终端并重新打开它,就会默认回到原来的状态。这是因为您已经在会话级别而不是源级别(即注册表级别)应用了更改。要查看$env:path的全局值,请执行以下操作:

1
Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH

或者更具体地说:

1
(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path

现在要更改这一点,首先我们捕获需要修改的原始路径:

1
$oldpath = (Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path

现在我们定义新路径的外观,在本例中,我们将附加一个新文件夹:

1
$newpath ="$oldpath;c:\path\to\folder"

注意:请确保$newpath看起来像您希望的样子,否则可能会损坏您的操作系统。

现在应用新值:

1
Set-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH -Value $newPath

现在做最后一个检查,看看你期望的样子:

1
(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path

现在,您可以重新启动PowerShell终端(甚至重新启动计算机),并确保它不会再次回滚到原来的值。请注意,路径的顺序可能会发生变化,因此它是按字母顺序排列的,因此请确保检查整行,以便更容易地将输出拆分为行,方法是使用分号作为分隔符:

1
($env:path).split(";")

打开PowerShell并运行:

1
[Environment]::SetEnvironmentVariable("PATH","$ENV:PATH;<path to exe>","USER")

我试图优化SBF和Michael的代码,使其更紧凑。

我依赖于PowerShell的类型强制,它自动将字符串转换为枚举值,因此我没有定义查找字典。

我还拉出了一个块,它根据一个条件将新路径添加到列表中,这样工作就完成了一次,并存储在一个变量中以供重用。

然后根据$PathContainer参数将其永久或仅应用于会话。

我们可以将代码块放在一个函数或PS1文件中,直接从命令提示符调用该函数或PS1文件。我和devenvaddpath.ps1一起去了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;

我为devenremovepath.ps1做了类似的事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;

到目前为止,它们似乎有效。