できない.dev

PowerShell で設定した環境変数が別のウィンドウに反映されない

$Env:X = 'value' はプロセススコープの変更で、そのセッションを閉じると消える。
恒久化するには [Environment]::SetEnvironmentVariable() に User か Machine スコープを渡すか、プロファイルに書く。

公開:

要約

PowerShell で環境変数を設定したのに、新しいウィンドウを開くと消えている。
あるいはコントロールパネルで設定したのに、開きっぱなしのウィンドウでは古い値のままである。
どちらもスコープの理解で説明が付く。

$Env:API_TOKEN = 'abc123'
$Env:API_TOKEN        # => abc123(このセッションでは見える)
# 新しいウィンドウを開いて
$Env:API_TOKEN        # => 何も出ない

公式ドキュメント(新しいタブで開く)のとおり、Windows の環境変数には Machine・User・Process の 3 つのスコープがあり、$Env: 構文で書き換わるのは Process スコープだけである。
Process スコープはセッションの起動時に Machine と User の値から組み立てられ、セッションを閉じれば破棄される。

恒久化したいなら [Environment]::SetEnvironmentVariable() に User か Machine を渡す。

よくある原因

  1. $Env: はプロセススコープ: Windows コマンドシェルの set や Unix の setenv と同じ扱いで、現在のセッションにしか効かない。
    子プロセスには引き継がれるので、そのセッションから起動したビルドツールでは見える。
    この「見えるのに残らない」挙動が混乱の元になる。
  2. 既存セッションには伝わらない: Process スコープは起動時に Machine と User から構成される。
    したがってコントロールパネルや別のウィンドウで User スコープを変えても、すでに動いているセッションは古い値を持ち続ける。
  3. スコープ引数を省略した: [Environment]::SetEnvironmentVariable('Foo','Bar') は第 3 引数が無いとプロセススコープへの書き込みになる。$Env:Foo = 'Bar' と結果は変わらない。
  4. Machine スコープに権限が足りない: Machine スコープの書き込みには権限が要る。
    権限が無いとコマンドは失敗しエラーになる。
  5. PATH を上書きしている: $Env:PATH = 'C:\Tools' と代入すると、それまでの PATH がすべて消える。
    そのセッションで git すら見つからなくなる。

解決策

1. User スコープに恒久化する

[Environment]::SetEnvironmentVariable('API_TOKEN', 'abc123', 'User')

第 3 引数に 'User''Machine' を渡すとレジストリに書かれ、以後に起動するセッションへ引き継がれる。
削除は空文字列を渡す。

[Environment]::SetEnvironmentVariable('API_TOKEN', '', 'User')

書き込んだ値は現在のセッションには入らないので、確認は新しいウィンドウで行う。
同じセッションでも使いたいなら両方に書く。

[Environment]::SetEnvironmentVariable('API_TOKEN', 'abc123', 'User')
$Env:API_TOKEN = 'abc123'

2. PowerShell だけで良いならプロファイルに書く

notepad $PROFILE
# Microsoft.PowerShell_profile.ps1
$Env:API_TOKEN = 'abc123'
$Env:PATH += ';C:\Tools'

プロファイルは PowerShell の起動ごとに読まれるので、どのバージョン・どのプラットフォームでも同じ方法が使える。
ただし cmd.exe やエクスプローラーから起動したアプリには効かない。
PowerShell 以外にも見せたい値なら方法 1 を選ぶ。

なおプロファイルが読み込まれるにはスクリプト実行が許可されている必要がある。
実行ポリシーが Restricted のままだとプロファイル自体が読まれない。

3. PATH は追記する

$Env:PATH += ';C:\Tools'

恒久化する場合も、既存の値を読んでから足す。

$old = [Environment]::GetEnvironmentVariable('PATH', 'User')
[Environment]::SetEnvironmentVariable('PATH', "$old;C:\Tools", 'User')

GetEnvironmentVariable にも同じスコープを渡すのが重要である。$Env:PATH は Machine と User が結合済みの値なので、それをそのまま User スコープへ書き戻すと Machine 側の内容が User にも複製され、PATH が二重になる。

4. 反映されているか確認する

[Environment]::GetEnvironmentVariable('API_TOKEN', 'User')

スコープを指定して読むと、レジストリに入った値をセッションの状態と切り離して確認できる。Get-ChildItem Env: で見えるのは Process スコープの一覧なので、恒久化の確認には使えない。

この記事は役立ちましたか?