Windows環境変数の設定に「SETX」コマンドを使ってはいけない理由

# 対象読者
- 「[Windows環境変数の超基本を理解する為のアプローチ](https://qiita.com/jeyei/items/2c385e4e0488a5fa2591)」の記事から**何となしにリンクを踏んだ方**。
- 環境変数というものが何となくわかったので「SETX」を何とか有効利用しようと思っている**初学者**。
- おいおい、たかが「SET」に毛が生えた「SETX」だろ～？と高を括っている**中級者**。
- 環境変数に一家言をもつ**上級者**（≒警察）。

# 伝えたいこと
掲題通り。

# 文言の「呼称」について前置き
Windowsレジストリの「キー」は
環境変数における「変数」「値」に置き換えると
「SET HOGE=fuga」のHOGEを「**値**」、fugaを「**データ**」と呼ぶそうです。

本稿ではレジストリの話も少しだけ出てくるので、
環境変数上の「変数（HOGE）」ないしレジストリ上の「値」である所の「**左辺**」をまとめて
便宜上、本稿では「**キー値**」と呼称します。

また、環境変数上の「値（fuga）」やレジストリ上の「データ」である所の「**右辺**」は
そのまま「**データ**」と呼称します。

あしからず。

```sh:環境変数
C:\>SET HOGE=fuga
↓
HOGE : 変数
fuga : 値
↓
HOGE : 'キー値'と呼称
fuga : 'データ'と呼称
```
```sh:レジストリ
# 例
HKCU\Volatile Environment : キー
USERNAME : 値
hogeyama : データ
↓
USERNAME : 'キー値'と呼称
hogeyama : 'データ'と呼称
#レジストリキーについては本稿では言及しません。
```


# 何故「SETX」を使ってはいけないのか？
結論から言います。
「SETX」コマンドによる環境変数（キー値）の**「作成」はできる**けど、
「SETX」コマンドによる環境変数（キー値）の**「削除」ができない**から、です。

# もう少し噛み砕くと
キー値の「削除」ができないがゆえに、
既存のシステム環境変数をユーザ環境変数として
同じキー値でオーバーライドした場合、消せないのはまだしも
**元のシステム環境変数へすらもアクセスができなくなる**のです。

# つまり、どういうこと？
そうなんです！
「SETX」を使ってしまうと以下のルールが崩れるという事なのです！（メガネクイ）

1. システム環境変数としてのみ宣言している変数は管理者権限がなくてもアクセスが可能。

2. システム環境変数とユーザ環境変数には**同じ変数を指定**できる。

3. システム環境変数とユーザ環境変数に同じ変数があった場合、
  **ユーザ環境変数を優先**して参照する。
　（ただし管理者権限でのインスタンスは除く）
　（ただし環境変数PATHを除く）

# わけわかんねぇよ。日本語で話せよ。
オーケー。（震え声）
百聞は一見に如かず、とりあえずやってみましょう。

　１．**システム環境変数「HOGE」**というキー値を用意します。
　　　「管理者権限のインスタンス」から「新規追加」しましょう。
　　　データは「systemdazo」とします。

```sh:DOSプロンプト（管理者権限のインスタンス）
# 「/M」オプションを付与して「システム環境変数」を追加する
C:\Windows\System32>SETX HOGE systemdazo /M
成功: 指定した値は保存されました。
C:\Windows\System32>
```

　２．「通常のインスタンス」（管理者権限ではないインスタンス）から
　　　キー値「HOGE」のデータを確認してみましょう。
　　　システム環境変数を引けてますね。

```sh:DOSプロンプト（通常のインスタンス）
C:\Users\user>ECHO %HOGE%
systemdazo
```

　３．次に**ユーザ環境変数「HOGE」**というキー値を用意します。
　　　今度は「通常のインスタンス」から「新規追加」しましょう。
　　　データは「userdayo」とします。

```sh:DOSプロンプト（通常のインスタンス）
# 今度は「/M」オプションなしで「ユーザ環境変数」を追加する
C:\Users\user>SETX HOGE userdayo
成功: 指定した値は保存されました。
C:\Users\user>
```

　４．「通常のインスタンス」を**再起動**して、キー値「HOGE」を確認しましょう。
　　　オーバーライドされた事によってユーザ環境変数を優先して引けていますね。

```sh:DOSプロンプト（通常のインスタンス：再起動後）
# 再起動した後です。
C:\Users\user>ECHO %HOGE%
userdayo
```

　５．なるほど。じゃあ**システム環境変数「HOGE」に参照を戻したい**んだけど
　　　**このユーザ環境変数「HOGE」を消す**にはどうしたらいいんだろう…
　　　巷に溢れているインターネッツの記事だと「**SETX HOGE ""**」とするのが有効だそうな。
　　　なるほど。じゃあやってみるよ。
　　　「通常のインスタンス」からユーザ環境変数「HOGE」を消してみるね。

```sh:DOSプロンプト（通常のインスタンス）
C:\Users\user>SETX HOGE ""
成功: 指定した値は保存されました。
C:\Users\user>
```

　　　できたじゃん。
　　　成功したって言ってるじゃん。


　　６．いやーよかったよかった。
　　　　では、あらためてユーザ環境変数「HOGE」のデータを確認してみましょう。
　　　　通常のインスタンスを**再起動**してください。

```sh:DOSプロンプト（通常のインスタンス：再起動後）
C:\Users\user>ECHO %HOGE%
%HOGE%
# いやー、よかったよかっ…え？
C:\Users\user>SET HOGE
環境変数 HOGE が定義されていません
# は？
```
　　　　おい。
　　　　「空」をセットしたんだから、せめて「空」が出てくるのならまだしも
　　　　**「HOGEが定義されていない」ってどういうこと**？
　　　　まだ「システム環境変数」の「HOGE」は消していないよ？

　　７．環境変数の設定ダイアログを見てみましょう。

![kan1.png](https://qiita-image-store.s3.amazonaws.com/0/354720/551860f3-ae36-f5d9-12cd-3bad6d42601c.png)

　　　　だよなぁ。システム環境変数の「HOGE」はまだいるよなぁ。。
　　　　消してないし。。


　　８．巷の記事をよくよく見ると「**レジストリも消さないといけない**」らしい。
　　　　レジストリにゴミが残ってたりするのかなぁ。
　　　　ていうか**何でそこまでする必要があるの…？**（辟易）
　　　　とりあえず探してみるか…

![reg1.png](https://qiita-image-store.s3.amazonaws.com/0/354720/0a2de760-fb7f-7ea0-d14e-71ec2ed86a64.png)

　　　　よーし、あったあった。消そう。

```sh:DOSプロンプト（管理者権限のインスタンス）
#一応「REG QUERY」でも確認
C:\WINDOWS\system32>REG QUERY HKCU /s /v HOGE

HKEY_CURRENT_USER\Environment
    HOGE    REG_SZ

検索の完了: 該当 1 件

C:\WINDOWS\system32>REG DELETE HKCU\Environment /v HOGE
レジストリ値 HOGE を削除しますか? (Yes/No) y
この操作を正しく終了しました。
C:\WINDOWS\system32>
```

　　９．これでもういないよね？ね？

![kan2.png](https://qiita-image-store.s3.amazonaws.com/0/354720/c5850573-fed3-14d5-945e-d75d25a33d95.png)

　　　　確かにいない。。
　　　　レジストリも一応確認しよう。

```sh:DOSプロンプト（管理者権限のインスタンス）
#一応「REG QUERY」でも確認
C:\WINDOWS\system32>REG QUERY HKLM /s /v HOGE
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment
    HOGE    REG_SZ    systemdazo
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
    HOGE    REG_SZ    systemdazo
検索の完了: 該当 2 件
C:\WINDOWS\system32>REG QUERY HKCU /s /v HOGE
検索の完了: 該当 0 件
C:\WINDOWS\system32>REG QUERY HKCR /s /v HOGE
検索の完了: 該当 0 件
C:\WINDOWS\system32>REG QUERY HKU /s /v HOGE
検索の完了: 該当 0 件
C:\WINDOWS\system32>REG QUERY HKCC /s /v HOGE
検索の完了: 該当 0 件
C:\WINDOWS\system32>
```

　　　　うん。「システム環境変数HOGEはあって然るべき」だからこれで問題ない。
　　　　もう一回、確認してみよう。

　　１０．「管理者権限のインスタンス」「通常のインスタンス」それぞれを**再起動**して確認します。

```sh:DOSプロンプト（管理者権限のインスタンス：再起動後）
C:\WINDOWS\system32>ECHO %HOGE%
systemdazo
# そらそうだ。これは正しい。
```
```sh:DOSプロンプト（通常のインスタンス：再起動後）
C:\Users\user>ECHO %HOGE%
%HOGE%
C:\Users\user>SET HOGE
環境変数 HOGE が定義されていません
# は？（二回目）
```
　　　　**わけがわからないよ…**

# やってみたらおかしくなったぞ！どうしてくれる！
　　「環境変数の設定ダイアログ」をもう一度見てみます。

![kan2.png](https://qiita-image-store.s3.amazonaws.com/0/354720/c5850573-fed3-14d5-945e-d75d25a33d95.png)


　　確かにキー値「HOGE」はシステム環境変数にしか存在していない状態のようです。
　　では、ここでユーザ環境変数の「新規」ボタンを押して、
　　先ほど消してしまった**キー値「HOGE」を新規で作成**します。
　　（データは「nandedayonandedayo」とします。）

![atara1.png](https://qiita-image-store.s3.amazonaws.com/0/354720/b13d9e8a-fcea-3957-188b-333dcff5798e.png)


　　キー値作成後は**「OK」ボタンを押して「環境変数の設定ダイアログ」も閉じて**ください。

![kan4.png](https://qiita-image-store.s3.amazonaws.com/0/354720/68b6aa91-5ecf-9e6f-1b77-1c3bea567971.png)

　　それでは「通常のインスタンス」を起動して確認しましょう。

```sh:DOSプロンプト（通常のインスタンス）
C:\Users\user>ECHO %HOGE%
nandedayonandedayo
C:\Users\user>SET HOGE
HOGE=nandedayonandedayo
```

　　おぉ！ユーザ環境変数「HOGE」が読めるようになった！！

　　では、**ユーザ環境変数「HOGE」を「環境変数の設定ダイアログ」から「削除」**しましょう。

![kan3.png](https://qiita-image-store.s3.amazonaws.com/0/354720/fd2e4a0f-a2bd-f78d-cffb-1015feb04923.png)

　　先ほどと同様に**「OK」ボタンを押して「環境変数の設定ダイアログ」を閉じて**ください。

![kan4.png](https://qiita-image-store.s3.amazonaws.com/0/354720/68b6aa91-5ecf-9e6f-1b77-1c3bea567971.png)

　　結果を確認します。

```sh:DOSプロンプト（通常のインスタンス）
C:\Users\user>ECHO %HOGE%
systemdazo
C:\Users\user>SET HOGE
HOGE=systemdazo
```
　　なるほど、復旧した！って、えぇ…？
　　キー値を用意し直して消せばよいのか？
　　一体、何が起こっているんだ…

# つまり、どういうこと？（二度目）
　１．キー値を「SETX」コマンドで作ったら、**環境変数ダイアログからでないと消す事ができない**。
　２．システム環境変数のキー値をオーバーライドする形でユーザ環境変数を作った場合、
　　　環境変数ダイアログ経由でユーザ環境変数を消さない限り、
　　　そのキー値では**システム環境変数すら読めなくなってしまう**。

# 環境変数ダイアログで消せるんならいいじゃん
　「コマンド」で定義した内容を、**何故「ダイアログ」で消さなくてはいけないのですか？**（迫真）

# こういう風に使ってはいけないよという導き
　１．「SET」コマンドのようにテンポラリな変数として、
　　　Windowsバッチのインスタンス内で安易に「SETX」を使いたい。
　　　　⇒ダメ。**SETXの定義はセッションが終わっても残る**ものだから。

　２．「SETX」で一時的にシステム環境変数をオーバーライドしたい。
　　　　⇒ダメ。上記の通り**コマンドで消せない**から。（戻すのも手間だし）

　３．「SETX」で環境変数を大量に用意したい。（使った後は消したいかもしれない）
　　　　⇒ダメ。それ、後で消すときに**画面からポチポチ消す羽目になる**よ？

# じゃあ一体どこで使えばいいのさ？
 上記の通り、「SETX」は「作成はできるけど削除はできない」という「**一方通行**」のコマンドです。
 なので、「SETX」で構築される環境変数の**要件**としては、
>
ただ一回（Once）作成をした後は永続的なライフサイクルであること。
（キー値の破棄が不要であること）
>

かつ
>
キー値が恒久的に一意であること
（ユーザ環境編変数でもシステム環境変数でも
　既存のキー値及び将来的なキー値と被りようがないこと）
>

が満たせればよいと考えます。

なお「データ」の内容がimmutable、固定値である必要はないと思います。
変更はできますので。

…え？**具体的な用途を挙げろ**、だって？
なんだろう…
**配布PCにプリインするソフトの環境変数をキッティングの際に初期設定したい**時とか…？
うーん。微妙。

少なくとも、Windowsバッチを普段使いで触る限りにおいて使用できるシーンは
**個人的には「ない」**との結論です。

「普段使い」も人それぞれでしょうが…あくまで「個人的」という事で。

# まとめ
無理に「SETX」コマンドを使わずとも
環境変数のライフサイクルを制御するだけなら
**「SET」コマンドで十分、代替が可能**です。

また、無理やり「SETX」コマンドを使うという前提の元、
環境変数の操作要件として「環境変数の設定ダイアログ上で消す」という手順を
運用的な操作の一環として含まなければいけないのであれば
**最初から最後まで「環境変数の設定ダイアログ」上で実施する手順**でいいんじゃね？と思います。

なお、上記で記載した通り
「SETXで作成される環境変数」の**要件**を満たすのであれば、
無論「SETX」を採用する方式で全然よいと思います。
「SETX」を有効に使えるシーンも恐らくどこかにあるでしょう。
~~私は思いつけませんが。~~

が、**要件として不適切であるにも関わらず「SETXコマンドを使わざるを得ない」**という状況は
**「方式」や「設計」が間違っていた（習熟不足）**と言わざるをえません。

迂闊に、安易に、ほんの軽い気持ちで「SETX」コマンドに手を出したせいで
画面でポチポチやらざるを得ないという状況に陥るくらいであれば
はなっから**「SETX」コマンドなんて単語を口に出すべきではない**でしょう。

というわけで、
>
**（皆もやっているから、という軽い気持ちで）**
**「SETX」コマンド、ダメ、絶対。**
>

でした。

よくわからないまま「SET」の延長線上で「SETX」コマンドを触るのはやめましょう。

ご覧頂きありがとうございました。


※
もし、
『SETXコマンドなんてそこまで用途を制限せずとも飼いならせるだろ？』
『御託はいいからあまり吠えるなよ、ボンズ？』
という有識者の方がおられましたら、コメントをお寄せ頂ければ幸甚です。
ご意見・ご指摘をお待ちしております。

# (Appendix) 「SETX HOGE ""」の謎。ただし迫りはしない。
以下は「SETX HOGE ""」をした直後の「環境変数の設定ダイアログ」ですが…
おわかりいただけただろうか？

![kan1.png](https://qiita-image-store.s3.amazonaws.com/0/354720/551860f3-ae36-f5d9-12cd-3bad6d42601c.png)

実は、**見えているユーザ環境変数「HOGE」は画面からは絶対に入れられない**のです。

![atara2.png](https://qiita-image-store.s3.amazonaws.com/0/354720/f0f10447-31b0-918b-f2d0-e5d8c74a22c9.png)

何故って、**データ（変数値）は必須**だからね。

**悪い例を示すために「あえて」**本稿では、この後レジストリがどうのこうのと、
ネットでよく見る内容を引用した上でこねくり回しましたが
上図の**「SETX HOGE ""」をした直後の「環境変数の設定ダイアログ」**の状態で
**ユーザ環境変数「HOGE」を「編集」なり「削除」なりすると簡単に復旧**します。
（なんだってー！）

それこそ**早く言え**って話なのですが、
あまりにもネット上に
**「SETX HOGE ""」や「REG DELETE」が出回っている由々しき事態**を憂いた上で
皆様にもこの危険性を細部に渡って共有したく、
手順と経緯を交えてダラダラと記載させて頂いた次第であります。

たとえ、本稿より少し早い段階で復旧できたところで
「**手動で復旧させざるを得なかった**」という顛末は変わりませんしね。
なので**「本稿ではあえて、より悪い例示をしている」**のです。

**ホントだよ？**

閑話休題。
でだ、本当に伝えたいのは

>
「環境変数の設定ダイアログ」上では決して生成できない環境変数を
「SETX」コマンドは何故、顕在化できるのか？
>

という点なんですよ。
「手動で消さないと正常な状態に戻せない」というリスクがあるにも関わらず
「SETX」は「異常な状態にする」事に成功してしまうという…。
しかも、何の警告もださずに、だ。
こいつはキナ臭ぇ匂いがプンプンするぜ…。
（ピンポーン）**ん？…誰だ？こんな時間に…**
　
　
…本来なら「この謎に迫る！」と銘打って掘り下げるべきだと思うんですが
題目通り、**謎には迫りません**。
謎は謎のままにしておいた方がよい事も、世の中にはあるのです…！
それに、アンタのためでもあるんだぜ…。
~~もう掘り下げるのめんどいし、SETXのバグって事でいい。~~
　
　
以上。