PowerShell 自動変数“$?”と“$LASTEXITCODE”の違いを検証
概要
PowerShellではじめてスクリプトを作成した当時、エラー制御(エラーハンドリング)でハマってしまいました。
よくネット記事のサンプルコードやサンプルプログラムに登場する自動変数は$?
ですが、
外部コマンドと連携した際、使い方を間違えてしまい思わぬ挙動になってしまいました。
コマンドプロンプトのエラー制御は%ERRORLEVEL%
のみでエラー制御しますが、
PowerShellでは、$?
以外に$LASTEXITCODE
も合わせて使用する必要がある事がわかりました。
今回、PowerShellの自動変数、$?
と$LASTEXITCODE
の挙動を直接コマンド(CLI・コマンドレット)と 外部コマンド[外だし自作のスクリプトファイル(ps1ファイル)を使用]の2パターンで検証してみた結果を紹介します。
この記事のターゲット
- PowerShellの初心者の方
- エラー制御がうまくいかず悩んでいる方
- 自動変数の
$?
と$LASTEXITCODE
の違いを詳しく知りたい方 - コマンドプロンプト(cmd)の
%ERRORLEVEL%
とも動作を比較したい方
環境
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.3031
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.3031
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
PS C:\WINDOWS\system32>
C:\WINDOWS\system32>ver
Microsoft Windows [Version 10.0.19045.3324]
C:\WINDOWS\system32>
結論
PowerShellで自動変数、$?
と$LASTEXITCODE
の動作内容と使用用途は下記の通りです。
自動変数 | データ型 | 動作内容 | 使用用途 |
---|---|---|---|
$? |
System.Boolean | 直前のコマンドの結果がTrue or False で値がセットされる。 |
直前で実行したコマンド結果を見る時に使用。 外部コマンドと連携する場合は、独特な動きとなりエラー制御が難しいので不向き。 |
$LASTEXITCODE |
System.Int32 | 終了コードが指定された場合(exit コマンド、exit 0 や exit -1 などを指定)、指定された値がセットされる。終了コードが指定されていない場合、変更されず初期値のnull( $null )のままとなる。 |
外部コマンドと連携する場合、使いやすい。 ただし、その外部コマンドで終了コードが指定されていなかった場合は使う事ができない。 |
以降より検証した結果を記載します。
最初に混同しやすいコマンドプロンプトでの動きについて検証。
コマンドプロンプトの戻り値(%ERRORLEVEL%)
過去、コマンドプロンプトでバッチファイルを作成していた方は、混乱しやすいと思うので、
まずはコマンドプロンプトの動きを紹介。
コマンドプロンプトでエラー制御する場合は、%ERRORLEVEL%
を使い戻り値を取得します。
準備したバッチファイル
D:\Downloads\return_test\bat>dir
ドライブ D のボリューム ラベルは ボリューム です
ボリューム シリアル番号は 1298-06B4 です
D:\Downloads\return_test\bat のディレクトリ
2023/08/21 10:31 <DIR> .
2023/08/21 10:31 <DIR> ..
2023/08/21 11:18 20 ng-pattern.bat
2023/08/21 11:18 33 ng-pattern_set-exitcode404.bat
2023/08/21 09:13 12 ok-pattern.bat
3 個のファイル 65 バイト
2 個のディレクトリ 56,885,624,832 バイトの空き領域
D:\Downloads\return_test\bat>
ECHO CMD-OKAY!
NOT-EXISTS-COMMAND
NOT-EXISTS-COMMAND
EXIT /B 404
戻り値(%ERRORLEVEL%)の検証
以下の検証結果からコマンドプロンプトで実行した場合、直接コマンドで打つ時や外部コマンドを打つ時など、
どのシチュエーションにおいても直前のコマンド結果(終了コードを指定した場合は、指定した値)が%ERRORLEVEL%
に反映される。
※一部、%ERRORLEVEL%
に値がセットされないコマンドあり(DELコマンドなど)
直接コマンドを実行した場合(%ERRORLEVEL%)
C:\WINDOWS\system32>ECHO CMD-OKAY! // 直接コマンドを実行(存在するコマンド)
CMD-OKAY!
C:\WINDOWS\system32>ECHO %ERRORLEVEL% // 戻り値を確認
0
C:\WINDOWS\system32>NOT-EXISTS-COMMAND // 直接コマンドを実行(存在しないコマンド)
'NOT-EXISTS-COMMAND' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
C:\WINDOWS\system32>
C:\WINDOWS\system32>ECHO %ERRORLEVEL% // 戻り値を確認
9009
C:\WINDOWS\system32>
外部コマンド(自作のバッチファイル)を実行した場合
D:\Downloads\return_test\bat>ok-pattern.bat // バッチの実行
D:\Downloads\return_test\bat>ECHO CMD-OKAY!
CMD-OKAY!
D:\Downloads\return_test\bat>ECHO %ERRORLEVEL% // 戻り値を確認
0
D:\Downloads\return_test\bat>ng-pattern.bat // バッチの実行
D:\Downloads\return_test\bat>NOT-EXISTS-COMMAND
'NOT-EXISTS-COMMAND' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
D:\Downloads\return_test\bat>
D:\Downloads\return_test\bat>ECHO %ERRORLEVEL% // 戻り値を確認
9009
D:\Downloads\return_test\bat>
D:\Downloads\return_test\bat>ng-pattern_set-exitcode404.bat // バッチの実行
D:\Downloads\return_test\bat>NOT-EXISTS-COMMAND
'NOT-EXISTS-COMMAND' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
D:\Downloads\return_test\bat>EXIT /B 404
D:\Downloads\return_test\bat>ECHO %ERRORLEVEL% // 戻り値を確認
404
PowerShellの戻り値($?と$LASTEXITCODE)
PowerShell CLIやプログラム(ps1ファイル)のエラー制御する場合は、$?
または $LASTEXITCODE
を使って実装します。
準備したスクリプトファイル
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> dir
ディレクトリ: D:\Downloads\return_test\ps
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2023/08/21 11:18 20 ng-pattern.ps1
-a---- 2023/08/22 9:09 28 ng-pattern_set-exitcode000.ps1
-a---- 2023/08/21 11:18 30 ng-pattern_set-exitcode404.ps1
-a---- 2023/08/21 10:36 23 ok-pattern.ps1
PS C:\WINDOWS\system32>
Write-Host "PS-OKAY!"
Write-Host "PS-OKAY!"
exit 0
Not-Exists-Command
Not-Exists-Command
exit 404
Not-Exists-Command
exit 0
戻り値($?)の検証
以下の検証結果からPowerShellの戻り値(自動変数)$?
では、直接コマンドを実行すると直前のコマンド結果が「 True or False 」で反映されることがわかった。
また、外部コマンドを実行した際、終了コードを指定しないと正常値の「 True 」で反映、
終了コードを0
で指定すると正常値の「 True 」、0
以外で指定すると異常値の「 False」が反映されるもよう。
直接コマンドを実行した場合($?)
PS C:\WINDOWS\system32> Write-Host "PS-OKAY!" // 直接コマンドを実行
PS-OKAY!
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $? // 戻り値を確認
True
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> Not-Exists-Command // 直接コマンドを実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラム
の名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認
してから、再試行してください。
発生場所 行:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $? // 戻り値を確認
False
PS C:\WINDOWS\system32>
外部コマンド(自作のスクリプトファイル)を実行した場合
PS D:\Downloads\return_test\ps> .\ok-pattern.ps1 // スクリプトの実行
PS-OKAY!
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $? // 戻り値を確認
True
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ok-pattern_set-exitcode000.ps1 // スクリプトの実行
PS-OKAY!
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $? // 戻り値を確認
True
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ng-pattern.ps1 // スクリプトの実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラム
の名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認
してから、再試行してください。
発生場所 D:\Downloads\return_test\ps\ng-pattern.ps1:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $? // 戻り値を確認
True
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ng-pattern_set-exitcode404.ps1 // スクリプトの実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラム
の名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認
してから、再試行してください。
発生場所 D:\Downloads\return_test\ps\ng-pattern_set-exitcode404.ps1:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $? // 戻り値を確認
False
PS D:\Downloads\return_test\ps>
ps1ファイルで終了コードを404
で指定してみた結果、$?
がFalseになりました。
では、終了コードを0
で指定してみて実行するとどうなるか下記で確認してみました。
PS D:\Downloads\return_test\ps> .\ng-pattern_set-exitcode000.ps1 // スクリプトの実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラム
の名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認
してから、再試行してください。
発生場所 D:\Downloads\return_test\ps\ng-pattern_set-exitcode000.ps1:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $? // 戻り値を確認
True
PS D:\Downloads\return_test\ps>
上記の通り、終了コードを0
で指定した結果、$?
がTrueとなりました。
これまでの結果から、PowerShellで外部コマンドを実行した場合、終了コードを指定しないと「 True 」となる。
一方、終了コードを0
で指定すると$?
は「 True 」となり0
以外で指定すると$?
は「 False 」となる事がわかりました。
外部コマンド + $?
の組み合わせは、かなり独特な動きとなる為、外部コマンドのエラー制御で$?
は使用しない方が良いと思われます。
戻り値($LASTEXITCODE)の検証
以下の検証結果からPowerShellの戻り値(自動変数)$LASTEXITCODE
では、直接コマンドを実行した場合は反映されない事がわかった。
また、外部コマンドを実行した際、終了コードを指定しないと結果が反映されないが、
終了コードをで指定すると指定した値がそのまま$LASTEXITCODE
に反映される事が判明。
直接コマンドを実行した場合($LASTEXITCODE)
PS C:\WINDOWS\system32> Write-Host "PS-OKAY!" // 直接コマンドを実行
PS-OKAY!
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $LASTEXITCODE // 戻り値を確認
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> Not-Exists-Command // 直接コマンドを実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラム
の名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認
してから、再試行してください。
発生場所 行:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $LASTEXITCODE // 戻り値を確認
PS C:\WINDOWS\system32>
外部コマンド(スクリプトファイル)を実行した場合
PS D:\Downloads\return_test\ps> .\ok-pattern.ps1 // スクリプトの実行
PS-OKAY!
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $LASTEXITCODE // 戻り値を確認
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ok-pattern_set-exitcode000.ps1 // スクリプトの実行
PS-OKAY!
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $LASTEXITCODE // 戻り値を確認
0
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ng-pattern.ps1 // スクリプトの実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラ
ムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確
認してから、再試行してください。
発生場所 D:\Downloads\return_test\ps\ng-pattern.ps1:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $LASTEXITCODE // 戻り値を確認
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ng-pattern_set-exitcode404.ps1 // スクリプトの実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラ
ムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確
認してから、再試行してください。
発生場所 D:\Downloads\return_test\ps\ng-pattern_set-exitcode404.ps1:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> $LASTEXITCODE // 戻り値を確認
404
PS D:\Downloads\return_test\ps>
PS D:\Downloads\return_test\ps> .\ng-pattern_set-exitcode000.ps1 // スクリプトの実行
Not-Exists-Command : 用語 'Not-Exists-Command' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラ
ムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確
認してから、再試行してください。
発生場所 D:\Downloads\return_test\ps\ng-pattern_set-exitcode000.ps1:1 文字:1
+ Not-Exists-Command
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Not-Exists-Command:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS D:\Downloads\return_test\ps> $LASTEXITCODE // 戻り値を確認
0
PS D:\Downloads\return_test\ps>
ここまで$LASTEXITCODE
を確認しましたが、終了コードを指定していないと変更されず初期値のnull($null
)のままとなり、
指定すると、その値が反映されるという結果。
使用用途が違うものの$?
と比較すると$LASTEXITCODE
の方が分かりやすいですね。
外部コマンドなどと連携する際は$LASTEXITCODE
を使った方が良いと思われます。
まとめ
- PowerShell -
$?
は- 動作内容:直前のコマンド結果がBoolean(正常:True、異常:False)で反映される
直前のコマンドの結果がBoolean型、True
orFalse
で値がセットされる。 - 使用用途:単純なエラー制御を行う際に使用
単純に直前に実行したコマンドの結果だけ見たいときに使用。
外部コマンド(自作のスクリプトファイルなど)と連携する場合、独特な動きとなりエラー制御が難しいので不向き。
- 動作内容:直前のコマンド結果がBoolean(正常:True、異常:False)で反映される
- PowerShell -
$LASTEXITCODE
は- 動作内容:指定した終了コードが反映される
終了コード(exit
コマンド、exit 0
やexit -1
などを指定)が実行された場合、指定された値がセットされる。
終了コードが指定されていない場合、変更されず初期値のnull($null
)のままとなる。 - 使用用途:外部コマンドと連携しエラー制御する際に使用
外部コマンド(自作のスクリプトファイルなど)と連携する場合に向いている。
ただし、その外部コマンドで終了コードが指定されていなかった場合は使用できない。
- 動作内容:指定した終了コードが反映される
- (ちなみに)コマンドプロンプト -
%ERRORLEVEL%
は- 動作内容:直前のコマンド結果が数値(正常:
0
、異常:0
以外)で反映される - 使用用途:コマンドプロンプトを扱う上であらゆるエラー制御で使用
- 動作内容:直前のコマンド結果が数値(正常:
関連記事
Discussion