【Windows, PowerShell】SETX vs. [Environment]::SetEnvironmentVariable
概要
SETX
と [Environment]::SetEnvironmentVariable
の違いに躓いたので調査したことを共有します!
どちらもWindowsで環境変数を設定する手段ですが、細かいところで違いがありました。
知らずに使ってしまうと最悪Windows上でアプリが起動しなくなるかもしれないので、 よかったら参考にしてください。
以下の説明はすべてはPowerShellでの実行を想定
結論
説明が長くなったので、先に結論です。
- 展開(%...%)を使用するなら、
SETX
使って、環境変数の末尾に/
を入れない- x:
C:\Program Files\Git\bin;%SystemRoot%\System32\WindowsPowerShell\v1.0\
- o:
C:\Program Files\Git\bin;%SystemRoot%\System32\WindowsPowerShell\v1.0
- x:
- 展開(%...%)を使用しないなら、
[Environment]::SetEnvironmentVariable
でも可
詳しくは以下
違い
設定時のレジストリキーの種類が異なる
試しに環境変数 Foo
に %SystemRoot%\System32
を登録してみます。
SETX
の場合は REG_SZ
と REG_EXPAND_SZ
から適した方
SETX Foo 'C:\Windows\System32'
->REG_SZ
SETX Foo '%SystemRoot%\System32'
->REG_EXPAND_SZ
つまり、SETX
は%SystemRoot%
のような環境変数を展開するような書式を検知したらREG_EXPAND_SZ
として登録してくれます。
[Environment]::SetEnvironmentVariable
は REG_SZ
固定
つまり%SystemRoot%
があろうがなかろうが REG_SZ
固定になります。
以下は [Environment]::SetEnvironmentVariable('Foo', '%SystemRoot%', [System.EnvironmentVariableTarget]::User)
の場合です。
REG_SZの場合、変数が展開されません!これは PATH を設定するときは非常にまずいです!
これに関しては dotnet のGitHubのIssueでずっと議論になっているようです。
変数が展開されていないことの確認
以下のコマンドを実施する場合は echo ([Environment]::GetEnvironmentVariable('PATH', [System.EnvironmentVariableTarget]::Machine))
などで既存の変数を控えて最後に戻すようにしてください!
特に PATH は元に戻さないとアプリがおかしくなる可能性があります!
[Environment]::SetEnvironmentVariable('PATH', '%SystemRoot%\System32', [System.EnvironmentVariableTarget]::Machine)
実行して、PowerShellを再起動してPATHを確認すると、以下のように展開されていないことがわかります。
そして、もちろんパスも通らないので、PATHを使用しているアプリがおかしくなります!
> echo $Env:PATH %SystemRoot%\System32;...
SETX /M PATH '%SystemRoot%\System32'
であれば REG_EXPAND_SZ
で登録されるので展開されたものがPATHとして登録されます。
> echo $Env:PATH C:\WINDOWS\System32;...
(バグ?) SETXの場合、 末尾にダブルクォーテーションが挿入されるパターンがある
条件は以下で発生することを確認しています
- 値の中にスペースが有る
- 値の最後が
/
例: C:\Program Files\Git\bin;C:\WINDOWS\System32\WindowsPowerShell\v1.0\
Program Files
にスペース、v1.0\
で末尾がバックスラッシュみたいな場合におかしくなります。(実際に起こった事象を例にしています)。
> SETX /M PATH 'C:\Program Files\Git\bin;C:\WINDOWS\System32\WindowsPowerShell\v1.0\' 成功: 指定した値は保存されました。 > echo ([Environment]::GetEnvironmentVariable('PATH', [System.EnvironmentVariableTarget]::Machine)) C:\Program Files\Git\bin;C:\WINDOWS\System32\WindowsPowerShell\v1.0"
最後に "
がなぜが挿入されます。。。 (SETX
のバグじゃないかと思っています)
ちなみに、PATHに登録する場合は "
がついた方のパス(上記では C:\WINDOWS\System32\WindowsPowerShell\v1.0
)がPATHから除外されてしまいます。
;
区切りのパスの末尾には /
は不要なので、基本的には最後は /
にしないほうが安全だと思います。
結論で述べた「環境変数の末尾に /
を入れない」はこれが理由になります。
ご注意ください!
簡易解説
せっかくなのでSETX
と[Environment]::SetEnvironmentVariable
を少し解説します。
環境変数の設定方法
例えば Foo
という環境変数に値 Bar;Baz
を入れる場合は以下のようにコールします。
ユーザー環境変数の場合
SETX Foo 'Bar;Baz'
[Environment]::SetEnvironmentVariable('Foo', 'Bar;Baz', [System.EnvironmentVariableTarget]::User)
システム環境変数の場合
システム環境変数とは対象のマシンのユーザー全体で共通となる環境変数のことです。
実行には管理者権限が必要となります。
SETX /M Foo 'Bar;Baz'
[Environment]::SetEnvironmentVariable('Foo', 'Bar;Baz', [System.EnvironmentVariableTarget]::Machine)
環境変数の確認方法
コマンドライン (混合)
※環境変数設定後はPowerShellの再起動が必要
echo $Env:Foo
コマンドライン (個別)
# ユーザー環境変数の場合 echo ([Environment]::GetEnvironmentVariable('Foo', [System.EnvironmentVariableTarget]::User)) # システム環境変数の場合 > echo ([Environment]::GetEnvironmentVariable('Foo', [System.EnvironmentVariableTarget]::Machine))
レジストリ エディタ
- レジストリエディタを開く
- ユーザー環境変数の場合は
コンピューター\HKEY_CURRENT_USER\Environment
- システム環境変数の場合は
コンピューター\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
システムのプロパティ -> 環境変数
- Windowsキーを押す
- "env" と検索
システム環境変数の編集
をクリック
環境変数
をクリック
余談、PATHのREG_SZ
登録をREG_EXPAND_SZ
に切り替える方法
途中までは「システムのプロパティ -> 環境変数」と同様
システム環境変数の編集
をクリック環境変数
をクリック- 変数
Path
を選択して、編集をクリック- ユーザー変数 と システム変数 どちらも同様
- "環境変数名の編集" の OK をクリック
- "環境変数" の OK をクリック
以上
どうやら、"環境変数名の編集" の OK を押したときに REG_EXPAND_SZ
で登録される処理が発生するようです。
もし、PATHに %SystemRoot%
を使用していて展開されていない場合は試してみてください。
環境変数の補足
Windows上では %SystemRoot%
= C:\Windows
です。
ちなみに %USERPROFILE%
は現在のユーザーのホームフォルダを表します。
エクスプローラーで以下のように入力するとホームフォルダに移動することができます。
雑感
前回の記事で、Git for Windowsの設定するときのコマンドでSetEnvironmentVariable
使ってたのですが、
%SystemRoot%
が展開されないことに気づいて急遽記事を書きました。
もし、前回の記事を試してローカルがおかしくなった方がいたらごめんなさい。。。
「余談、PATHのREG_SZ
登録をREG_EXPAND_SZ
に切り替える方法」を実施すれば解消すると思います 🙇♂️
「GetEnvironmentVariable
使うなら、対のSetEnvironmentVariable
でしょ!」って感じで使ってたらこんな罠があったとは。。。
ご参考になれば幸いです。
それでは~