PowerShellからのPATH設定がなんかうまくいかない話。

# 一々システム環境変数編集のためにPCを右クリックして…めんどくさい、コマンドでパパッと終わらせよっと。
とそう思ってPowerShellからの環境変数操作をググりに行く。
[こちらの先輩方の記事](http://qiita.com/mmorita/items/4c44363faf74a7105199)が大変参考になる。
## 用意したもの
* Windows10 Pro
* Power Shell 5
* adb
* jdk1.8.0_112

## とりあえず試してみる。
システム環境変数に書き込むときは管理者権限が必要なことに注意する。
コマンドプロンプトからも"@powershell "を先に書くことでPowerShellを呼べるのだが、
$で作った環境変数はpoweshellでの処理が終了後、直ちに破棄されるのでこのままだと実行不可。
一行にまとめればいけると思う。
### 試したコマンドその1
```PS1
> $path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
> $path += ";" + "C:\adb\"
> [Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
```
adbならAndroid-SDKから入れろよと突っ込みを入れたくなる方もおられるだろうが、ここはスルーしてほしい。あくまでもテストだ。
入力後、すぐには反映されないのでPowerShellを再起動しよう。
### 結果

```PS1
PS > adb
Android Debug Bridge version 1.0.32

 -a                            - directs adb to listen on all interfaces for a connection
…以下略
```
うむ、素晴らしい。簡単だ。

## PATH以外の環境変数を操作してみる。
環境変数はPATHだけではない。TEMPやPATHEXTなど他にも色々ある。
今回は"JAVA_HOME"の編集を行う。
この環境変数が何かについては[こちら](http://oshiete.goo.ne.jp/qa/4061702.html)の回答No.2の方が詳しい。

### 試したコマンド2
```PS1
PS > $path = 'C:\PROGRA~1\Java\jdk1.8.0_112'
PS > [Environment]::SetEnvironmentVariable('JAVA_HOME', $path, 'Machine')
```
反映するため一度PowerShellを再起動する。
### 結果
PowerShellから環境変数の確認を行うには$Env:「変数名」と叩いて実行する。

```PS1
PS > $Env:JAVA_HOME
C:\PROGRA~1\Java\jdk1.8.0_112
```
これでJAVA_HOMEが正常に設定されたことが確認できた。

```PS1
PS > dir $Env:JAVA_HOME


    ディレクトリ: C:\Program Files\Java\jdk1.8.0_112


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2016/12/01      6:46                bin
d-----       2016/12/01      6:46                db
d-----       2016/12/01      6:46                include
d-----       2016/12/01      6:46                jre
d-----       2016/12/01      6:47                lib
-a----       2016/09/22     21:54           3244 COPYRIGHT
-a----       2016/12/01      6:46        5094020 javafx-src.zip
-a----       2016/12/01      6:46             40 LICENSE
-a----       2016/12/01      6:46            159 README.html
-a----       2016/12/01      6:47            528 release
-a----       2016/09/22     21:54       21256085 src.zip
-a----       2016/12/01      6:46         110114 THIRDPARTYLICENSEREADME-JAVAFX.txt
-a----       2016/12/01      6:46         177094 THIRDPARTYLICENSEREADME.txt


```

中身の確認も問題なく可能。環境変数をパスとして使用できていることが確認できる。
## 環境変数用いたPATH設定
環境変数をPATHの中で使う。
先のdirを用いた時のように、JAVA_HOMEは既にディレクトリパスとして使用できる。
コマンドプロンプトにおいては環境変数は%で括る。JAVA_HOMEの場合は%JAVA_HOME%となる。

```bat
>echo %JAVA_HOME%
C:\PROGRA~1\Java\jdk1.8.0_112

>
```
この表記方法はPATH設定内でも有効で、JAVAの場合は%JAVA_HOME%\bin\とするのが慣習だ。
こうしておくことで、JDKを更新したときJAVA_HOMEを書き換えれば自動的にPATHも書き換えられるようになる。
これをPowerShellから設定する。
### 試したコマンド3

```PS1
PS > $path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
PS > $path += ';' +'%JAVA_HOME%\bin\'
PS > [Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
```
勿論実行後はPowerShellを一般ユーザーとして再起動。
### 結果

```PS1
PS > javac
javac : 用語 'javac' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されませ
ん。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してくだ
さい。
発生場所 行:1 文字:1
+ javac
+ ~~~~~
    + CategoryInfo          : ObjectNotFound: (javac:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS >
```
あ、あれ…。
PATH設定ミスったのかな…。

```PS1
PS > $Env:PATH
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:
\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\;%JAVA_HOME%\bin\;C:\Users\Private\AppData\Local\Microsoft\WindowsApps;
C:\Users\Private\AppData\Local\Android\android-sdk\tools
PS >
```
確かに%JAVA_HOME%の文字がある。

```PS1
PS C:\Users\Private> dir $Env:JAVA_HOME\bin\


    ディレクトリ: C:\Program Files\Java\jdk1.8.0_112\bin


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2016/12/01      6:46          15904 appletviewer.exe
-a----       2016/12/01      6:46          16416 extcheck.exe
-a----       2016/12/01      6:46          16416 idlj.exe
-a----       2016/12/01      6:46          34336 jabswitch.exe
-a----       2016/12/01      6:46          15904 jar.exe
-a----       2016/12/01      6:46          16416 jarsigner.exe
-a----       2016/12/01      6:46          15904 java-rmi.exe
-a----       2016/12/01      6:46         206880 java.exe
-a----       2016/12/01      6:46          15904 javac.exe
-a----       2016/12/01      6:46          16416 javadoc.exe
…中略
PS > start $Env:JAVA_HOME\bin\javac
PS > adb
Android Debug Bridge version 1.0.32
…中略
PS >
```
ちゃんとjavac.exeはあるぞ？
Startで叩いてもちゃんと反応が返ってくるし。
adbだとちゃんと帰ってくるあたり、PATHを破損させてしまったとも考えにくい。

管理者権限で実行したPowerShellから試してみる。

```PS1
PS > javac -version
javac 1.8.0_112
PS >
```
管理者権限で実行すれば動くらしい。
システム環境変数の書き換えは管理者権限で実行する必要があるので一般ユーザー権限では設定できない。

コマンドプロンプトからも試してみる。まずは一般ユーザー権限だ。

```bat
>javac
'javac' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

>dir %JAVA_HOME%\bin\
 ドライブ C のボリューム ラベルは XXXXXXXXXXX です
 ボリューム シリアル番号は XXXX-XXXX です

 C:\PROGRA~1\Java\jdk1.8.0_112\bin のディレクトリ

2016/12/01  06:46    <DIR>          .
2016/12/01  06:46    <DIR>          ..
2016/12/01  06:46            15,904 appletviewer.exe
2016/12/01  06:46            16,416 extcheck.exe
2016/12/01  06:46            16,416 idlj.exe
2016/12/01  06:46            34,336 jabswitch.exe
2016/12/01  06:46            15,904 jar.exe
2016/12/01  06:46            16,416 jarsigner.exe
2016/12/01  06:46            15,904 java-rmi.exe
2016/12/01  06:46           206,880 java.exe
2016/12/01  06:46            15,904 javac.exe
2016/12/01  06:46            16,416 javadoc.exe
…中略
              54 個のファイル           3,435,377 バイト
               2 個のディレクトリ  26,902,417,408 バイトの空き領域

>echo %PATH%
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\;%JAVA_HOME%\bin\;C:\Users\Private\AppData\Local\Microsoft\WindowsApps;C:\Users\Private\AppData\Local\Android\android-sdk\tools

>%JAVA_HOME%\bin\javac -version
javac 1.8.0_112

>adb
Android Debug Bridge version 1.0.32
…中略

>
```
…だめだ、PATHは通ってるし、参照もできるけど動かない。
コマンドプロンプトも管理者権限で実行から試してみる。

```bat
>javac -version
javac 1.8.0_112

>
```
やはり管理者権限で実行すればいけるらしい。
### 仕方ないのでコントロールパネルから環境変数を確認しに行く。
Windows10ではUIが変わっているように見えるが、"テキストの編集(T)…"で以前のUIを呼び出せる。
以下がシステム変数Pathの中身だ。

```
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\;%JAVA_HOME%\bin\
```

一番最後にちゃんと%JAVA_HOME%\bin\が存在していることが見て取れる。
どこがおかしいのかわからない。
困ったときの再起動…でもダメでした。反映されない。

### なんとなく切り取り&貼り付けしてみる
以下、なんとなくやってみたこと。

```
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\;%JAVA_HOME%\bin\
```

の、

```
;%JAVA_HOME%\bin\
```

だけをいったん切り取り、

```
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\
```

にして、システム変数の編集、環境変数ウィンドウをそれぞれOKで閉じる。
もう一度環境変数(N)…から環境変数ウィンドウを開き、システム環境変数のPathを編集、
テキストの編集(T)…から

```
;%JAVA_HOME%\bin\
```
を

```
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\
```
の後ろに貼り付けて

```
C:\Program Files\Python35\Scripts\;C:\Program Files\Python35\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\adb\;%JAVA_HOME%\bin\
```
にする。

うむ、全く意味不明な行動だ。せっかく切り取ったのに同じものを同じところに貼り付けて何の意味があるのだ。

## だが動く
ようになるのだからしょうがない。
どちらも一般ユーザー権限だ。

```PS1
PS > javac -version
javac 1.8.0_112
PS >
```
```CMD
>javac -version
javac 1.8.0_112

>
```
管理者権限で実行すれば動くことからPATH表記自体は間違いない。コマンドもおそらく問題ないと思うんだが…。
切り取って貼り付けなおすと管理者限定でなくなるようだ。

## 調査
原因はわからないにせよ、再現性はきちんと確かめておきたい。
実験には%SystemRoot%を使った。これは
`C:\WINDOWS`
が入った環境変数で、初期状態で用意されているものだ。
PATHが通っているかどうか確認するために以下のhello.batファイルを作成・使用した。

```hello.bat
@echo off

echo Hello警報

pause
exit
```
大前提として、環境変数をPATHに書き込まなければ問題ないことは先に示しておく。
C:\WINDOWS\hello\hello.batとファイルを設置し、
管理者権限で以下のコマンドを実行。

```PS1
PS > $path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
PS > $path += ';' +'C:\Windows\hello\'
PS > [Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
PS >
```
その後、PowerShellを一般ユーザー権限で再起動、以下のコマンドを実行。

```PS1
PS > hello
Hello警報
続行するには何かキーを押してください . . .
PS >
```
この通り、問題ない。
### 実験1
C:\WINDOWS\hello\hello.batとファイルを設置し、
%SystemRoot%\hello\にGUIからPATHを通す。
### 実験結果1
一般ユーザー権限で実験。

```PS1
PS > hello
Hello警報
続行するには何かキーを押してください . . .
PS >
```
問題ない。
### 実験2
C:\WINDOWS\hello\hello.batとファイルを設置し、
%SystemRoot%\hello\にPowerShellからPATHを通す。

```PS1
> $path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
> $path += ';' +'%SystemRoot%\hello\'
> [Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
```

### 実験結果2
まずは一般ユーザー権限だ。

```PS1
PS > hello
hello : 用語 'hello' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されませ
ん。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してくだ
さい。
発生場所 行:1 文字:1
+ hello
+ ~~~~~
    + CategoryInfo          : ObjectNotFound: (hello:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS > $path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
```
続いて管理者権限。

```PS1
PS C:\WINDOWS\system32> hello
Hello警報
続行するには何かキーを押してください . . .
PS C:\WINDOWS\system32>
```
やはり管理者権限でなら動く。
### 実験3
実験2の後、GUIのPATH設定から%SystemRoot%\hello\を一度切り取り一度PATHを反映させ、再度貼り付ける。
### 実験結果3
一般ユーザー権限。

```PS1
PS > hello
Hello警報
続行するには何かキーを押してください . . .
PS >
```
一度切り取って貼り付ければ一般ユーザー権限でも有効になる。
### 実験4
ここまではすべてシステム環境変数で調査をしてきた。
今度はユーザー環境変数で実験をしてみる。

C:\WINDOWS\hello\hello.batとファイルを設置し、
%SystemRoot%\hello\にGUIからユーザー環境変数のPATHに通す。
### 実験結果4
```PS1
PS > hello
Hello警報
続行するには何かキーを押してください . . .
PS >
```
問題ない。
### 実験5
C:\WINDOWS\hello\hello.batとファイルを設置し、
%SystemRoot%\hello\にPowerShellからPATHを通す。
[Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')の
SetEnvironmentVariableの三つ目の引数、'Machine'を'User'に書き換えればよい。
ユーザー環境変数の書き換えは管理者権限で実行する必要はない。一般ユーザー権限でOKだ。

```PS1
> $path = [Environment]::GetEnvironmentVariable('PATH', 'User')
> $path += ';' +'%SystemRoot%\hello\'
> [Environment]::SetEnvironmentVariable('PATH', $path, 'User')
```

### 実験結果5
一般ユーザー権限

```PS1
PS > hello
hello : 用語 'hello' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されませ
ん。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してくだ
さい。
発生場所 行:1 文字:1
+ hello
+ ~~~~~
    + CategoryInfo          : ObjectNotFound: (hello:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS >
```
ユーザー環境変数でもダメらしい。
管理者権限で実行。

```PS1
PS > hello
Hello警報
続行するには何かキーを押してください . . .
PS >
```
管理者権限でならOK。
### 実験6
実験5の後、GUIからユーザー環境変数設定のPATH設定から%SystemRoot%\hello\を一度切り取り一度PATHを反映させ、再度貼り付ける。
### 実験結果6
一般ユーザー権限。

```PS1
PS > hello
Hello警報
続行するには何かキーを押してください . . .
PS >
```
問題ない。
# 結論
## なんかよくわからんけど、PowerShellからPATH設定をするとき、環境変数をPATHに組み込むと管理者権限でないと動かない。
システム環境変数だろうがユーザー環境変数だろうが一緒。
## 対策
###素直にGUI使いましょう。
コマンドでPATHを編集した後でも、編集したところをいったん切り取り、一度反映させて同じところに貼り付けなおせばOK。

…お粗末様でした。
結論がよくわからんって一番最悪なやつ。
## 余談
%JAVA_HOME%を使った参照をあきらめるならPATHを通すことは普通にできる。

```PS1
$path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
$path += ';' + $Env:JAVA_HOME + '\bin\'
[Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
```
とすれば良い。
この場合は$Env:JAVA_HOMEは変数の中身に置き換わってしまうので、出力されるPATHは
`;C:\PROGRA~1\Java\jdk1.8.0_112\bin\`
となる。
或いはコマンドプロンプトから、

```bat
setx /M PATH "%PATH%;%JAVA_HOME%\bin\
```
としてもよい。結果は同じで、出力されるPATHは
`;C:\PROGRA~1\Java\jdk1.8.0_112\bin\`
となる。