Pablo Burgos
Pablo Burgos

Reputation: 1254

How to update Windows PowerShell session environment variables from registry?

This could be a simple question, but I couldn't find a proper answer.

How do you update the environment variables from within a current Windows PowerShell session without closing the current one?

Up to now, when I modify for example the PATH environment variable from Control Panel > System, I have to close current session and open a new one, so that variables are refreshed, or issue a SetEnviromentVariable which is cumbersome.

I'm coming from the Linux world, so I'm looking for something like source command.

Upvotes: 44

Views: 53181

Answers (6)

Eamon Nerbonne
Eamon Nerbonne

Reputation: 48136

This is an old question, but if you use chocolatey, it also installs the handy refreshenv command, which works fine for me.

Upvotes: 15

Kirk Munro
Kirk Munro

Reputation: 1360

Unfortunately the other answers are missing some important details. PSModulePath has some special handling in PowerShell that needs to be considered when updating environment variables. Also, variables may be added, modified, or removed. And you need to take path environment variables into consideration.

To make it worse, depending on what product/host you are in when you do this, that product may have some special environment variable handling of its own that should be considered, and aside from hoping that Path environment variables (those that contain multiple values, semi-colon delimited) actually contain "Path" as part of their name, you may miss out on something (unless of course this is how Windows handles those environment variables internally, which could be, I didn't dig that far down the rabbit hole).

Here is a script that I came up with that is about as close as I think you'll get to properly updating environment variables in your current PowerShell session without restarting it:

# 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}"
}

Note that this is largely untested however the principle is sound and it is based on work I have done in the past in this area.

Upvotes: 6

Jaykul
Jaykul

Reputation: 15824

There's no need to go dumpster diving in the registry for this stuff:

foreach($level in "Machine","User") {
   [Environment]::GetEnvironmentVariables($level)
}

If you want to make PATH variables work right, you'd have to treat them specially (that is, concatenate the (potentially) new stuff to what you already have, and not lose anything, because you can't tell for sure what you should remove).

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)" }
}

Notice that Set-Content actually sets the variables in your process environment, the same as doing something like $env:Temp = Convert-Path ~\AppData\Local\Temp

Upvotes: 42

Pablo Burgos
Pablo Burgos

Reputation: 1254

Thank you @Joey for your answer (I marked that correct :) ) Just to note that, given I want to refresh all variables according to the registry in that moment, I got rid of the if-else clause...

I double checked the registry variable location and are ok according to Where are environment variables stored in registry?

Snippet used is here just for completeness....

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

Upvotes: 5

Keith Hill
Keith Hill

Reputation: 201992

If you want to update the Path for your current PowerShell session only, do this:

$env:Path += ";<new path>"

If you need to update the PATH env variable so that it is persistent across new PowerShell sessions use:

[Environment]::SetEnvironmentVariable("PATH", $env:Path + ";<new path>", 'User')

If you want to change the path for all users, then change 'User' to 'Machine'.

Upvotes: 13

Joey
Joey

Reputation: 354774

The environment gets populated on process start-up which is why you'll need to restart the process.

You can update the environment by reading it from the registry. Maybe with a script like the following:

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

(untested)

Upvotes: 17

Related Questions