Подтвердить что ты не робот

Как обновить переменные среды сеанса Windows PowerShell из реестра?

Это может быть простой вопрос, но я не мог найти правильный ответ.

Как обновить переменные среды из текущего сеанса Windows PowerShell, не закрывая текущий?

До сих пор, когда я изменяю, например, PATH среды PATH из Панели управления> Система, мне приходится закрывать текущий сеанс и открывать новый, чтобы переменные обновлялись, или выдавать SetEnviromentVariable что является громоздким.

Я из мира Linux, поэтому я ищу что-то вроде команды source.

4b9b3361

Ответ 1

Окружающая среда заполняется при запуске процесса, поэтому вам необходимо перезапустить процесс.

Вы можете обновить среду, читая ее из реестра. Возможно с script следующим образом:

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

(непроверенные)

Ответ 2

В этом реестре нет необходимости перебрасывать дайджест в корзину:

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

Ответ 3

Это старый вопрос, но если вы используете chocolatey, он также устанавливает удобный refreshenv, которая отлично подходит для меня.

Ответ 4

Если вы хотите обновить Path для текущего сеанса PowerShell, сделайте следующее:

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

Если вам необходимо обновить переменную env PATH, чтобы она сохранялась в новых сеансах PowerShell, используйте:

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

Если вы хотите изменить путь для всех пользователей, измените "Пользователь" на "Машина".

Ответ 5

Спасибо @Joey за ваш ответ (я отметил, что правильно:)) Просто отметим, что, учитывая, что я хочу обновить все переменные в соответствии с реестром в тот момент, я избавился от предложения if-else...

Я дважды проверил местоположение переменной реестра и в порядке в соответствии с Где хранятся переменные среды в реестре?

Используемый фрагмент используется только для полноты....

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

Ответ 6

К сожалению, в других ответах отсутствуют некоторые важные детали. PSModulePath имеет специальную обработку в PowerShell, которую необходимо учитывать при обновлении переменных среды. Кроме того, переменные могут быть добавлены, изменены или удалены. И вам нужно учитывать переменные среды пути.

Чтобы усугубить ситуацию, в зависимости от того, какой продукт/хост вы используете, когда вы это делаете, у этого продукта может быть какая-то специальная обработка переменных среды, которая должна быть рассмотрена, и не надеяться, что переменные среды Path (те, которые содержат несколько значений, с разделителями с запятой) на самом деле содержат "путь" как часть их имени, вы можете пропустить что-то (если, конечно, это не так, как Windows обрабатывает эти переменные среды внутри, что могло бы быть, я не копал это далеко вниз по кроличьей дыре).

Вот script, с которым я столкнулся, это примерно так же близко, как я думаю, вы сможете правильно обновить переменные среды в текущем сеансе 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}"
}

Обратите внимание, что это в основном непроверено, но принцип звучит, и он основан на работе, которую я делал в прошлом в этой области.