PowerShell での終了ステータス($?、$LastExitCode) について確認してみた

Linux の場合、`echo $?`と実行することで前回実行コマンドの終了ステータスが取得できるが、PowerShell の場合にどうなるか確認した時のメモ。

# 参考

+ [Difference between $? and $LastExitCode in PowerShell](http://stackoverflow.com/questions/10666035/difference-between-and-lastexitcode-in-powershell)
+ [about_Automatic_Variables](https://technet.microsoft.com/en-us/library/dd347675.aspx)

# 確認してみた

`$?`を参照することで最後に実施した操作の成功・失敗を確認できます。

[about_Automatic_Variables](https://technet.microsoft.com/en-us/library/dd347675.aspx)

> $?  
       Contains the execution status of the last operation. It contains
    TRUE if the last operation succeeded and FALSE if it failed.
    
直前のコマンドが成功の場合にはTRUEとなり、失敗の場合にはFALSEとなります。
参照は`echo $?`としても、直接`$?`としても大丈夫だった。

```powershell
PS C:\Users\Administrator> echo "test"
test

# 前回のコマンドが成功となったため、True
PS C:\Users\Administrator> echo $?
True

# コマンドを失敗させる
PS C:\Users\Administrator> aaaa
aaaa : The term 'aaaa' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ aaaa
+ ~~~~
    + CategoryInfo          : ObjectNotFound: (aaaa:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

# 前回のコマンドが失敗のため、False
PS C:\Users\Administrator> $?
False
```

[about_Automatic_Variables](https://technet.microsoft.com/en-us/library/dd347675.aspx)

>$LastExitCode
       Contains the exit code of the last Windows-based program that was run.
       
 
と書いてある。
微妙に記載が違う。
`$LastExitCode` は **Windows-based program** との記載となり、`$?` は **execution status of the last operation** となっている。

先程の操作を確認してみる。

```powershell
# PowerShell起動後、何も出力されない
PS C:\Users\Administrator> $LastExitCode
PS C:\Users\Administrator> echo "aaa"
aaa

# 前回のコマンドが成功しても何も出力されない
PS C:\Users\Administrator> $LastExitCode

# コマンドを失敗
PS C:\Users\Administrator> hoge
hoge : The term 'hoge' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ hoge
+ ~~~~
    + CategoryInfo          : ObjectNotFound: (hoge:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

# やはり出力されない
PS C:\Users\Administrator> $LastExitCode
PS C:\Users\Administrator> echo $LastExitCode
```

なるほど。
PowerShell上のコマンドの結果は`$LastExitCode`の値が入らない。


試しにスクリプトを作って比較

```test.ps1
ls hoge
```

これを実行

```powershell
# $? は正常終了を示す True になってしまう
PS C:\Users\Administrator> .\test.ps1
ls : Cannot find path 'C:\Users\Administrator\hoge' because it does not exist.
At C:\Users\Administrator\test.ps1:1 char:1
+ ls hoge
+ ~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\Administrator\hoge:String) [Get-ChildItem], ItemNotFoundExcept
   ion
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS C:\Users\Administrator> echo $?
True

# $LASTEXITCODE は何も出ない
PS C:\Users\Administrator> .\test.ps1
ls : Cannot find path 'C:\Users\Administrator\hoge' because it does not exist.
At C:\Users\Administrator\test.ps1:1 char:1
+ ls hoge
+ ~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\Administrator\hoge:String) [Get-ChildItem], ItemNotFoundExcept
   ion
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS C:\Users\Administrator> $LASTEXITCODE
```

つまりこれだけだと PowerShell が成功扱いになりそう
ErrorActionPreferenceを変えるようにしてみる

[PowerShellでのエラーハンドリングについて](https://qiita.com/toshihirock/items/936b33f0c15723565dce)

```test.ps1
$ErrorActionPreference = "Stop"
ls hoge
```

```powershell
# $? が失敗を示す False になった
PS C:\Users\Administrator> .\test.ps1
ls : Cannot find path 'C:\Users\Administrator\hoge' because it does not exist.
At C:\Users\Administrator\test.ps1:2 char:1
+ ls hoge
+ ~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\Administrator\hoge:String) [Get-ChildItem], ItemNotFoundE
   xception
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS C:\Users\Administrator> echo $?
False

# $LASTEXITCODE　は同じく出力されない
PS C:\Users\Administrator> .\test.ps1
ls : Cannot find path 'C:\Users\Administrator\hoge' because it does not exist.
At C:\Users\Administrator\test.ps1:2 char:1
+ ls hoge
+ ~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\Administrator\hoge:String) [Get-ChildItem], ItemNotFoundE
   xception
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS C:\Users\Administrator> $LASTEXITCODE
```

`$LASTEXITCODE`を見る場合、明示的に指定しないとだめな気がするので以下のようにする。

```test.ps1
try{
  hogefuga -ErrorAction stop
} catch {
  exit 1
}
```

実行して取得。

```ps1
PS C:\Users\Administrator> .\test.ps1
PS C:\Users\Administrator> echo $LastExitCode
1
```

取得できた。
`$?`と`$LastExitCode`は意味が違うので注意が必要。