すぎしーのXRと3DCG

主にXR, Unity, 3DCG系の記事を投稿していきます。

【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
  • 展開(%...%)を使用しないなら、[Environment]::SetEnvironmentVariableでも可

詳しくは以下

違い

設定時のレジストリキーの種類が異なる

試しに環境変数 Foo%SystemRoot%\System32 を登録してみます。

SETX の場合は REG_SZREG_EXPAND_SZ から適した方

  • SETX Foo 'C:\Windows\System32' -> REG_SZ
  • SETX Foo '%SystemRoot%\System32' -> REG_EXPAND_SZ

つまり、SETX%SystemRoot% のような環境変数を展開するような書式を検知したらREG_EXPAND_SZとして登録してくれます。

[Environment]::SetEnvironmentVariableREG_SZ 固定

つまり%SystemRoot%があろうがなかろうが REG_SZ 固定になります。

以下は [Environment]::SetEnvironmentVariable('Foo', '%SystemRoot%', [System.EnvironmentVariableTarget]::User) の場合です。

REG_SZの場合、変数が展開されません!これは PATH を設定するときは非常にまずいです!

これに関しては dotnetGitHubのIssueでずっと議論になっているようです。

github.com

変数が展開されていないことの確認

以下のコマンドを実施する場合は 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でしょ!」って感じで使ってたらこんな罠があったとは。。。

ご参考になれば幸いです。

それでは~