終了コードの確認方法|PowerShell・コマンドプロンプト・GitHub Actionsの違い
Windowsでコマンドの終了コードを確認し、PowerShell、コマンドプロンプト、GitHub Actionsへ失敗を正しく引き渡す方法をコード例付きで解説します。
コマンドが画面上では動いているように見えても、自動処理では失敗を検知できないことがあります。Windowsでは、PowerShellの$LASTEXITCODE、コマンドプロンプトの%ERRORLEVEL%、GitHub Actionsのステップ結果が似た役割を持ちますが、確認方法は同じではありません。この記事では、外部プログラムの失敗を見つけ、後続処理を止めるところまでを、公式の一次情報に沿って比較します。
終了コードで分かること
終了コード(exit code)は、プログラムが終了時に呼び出し元へ返す数値です。最初に覚える結論は3つです。
- 一般には
0を成功、非ゼロ(0以外)を失敗として扱う。 - ただし番号の意味はプログラムごとに違う。
- 画面に出るメッセージと終了コードは別物である。
1つ目は、GitHub Actionsの公式資料でも「終了コード0は成功、非ゼロは失敗」としてアクションの状態に対応づけられています(Setting exit codes for actions)。この資料はJavaScriptアクションとDockerコンテナアクション向けの説明ですが、0と非ゼロの基本の読み方はそのまま使えます。
2つ目の「番号の意味はプログラムごと」の実例が2つあります。Node.jsは、1を捕まえられなかった致命的な例外に、9を不正な引数に割り当てています(Node.jsのProcess公式資料)。番号の一覧は更新されることがあるので、利用時点の公式資料で確認してください。逆に、robocopyのように非ゼロへ「差分あり」など失敗以外の意味を持たせるプログラムもあります。Microsoftの公式例でも、robocopyは「8より大きい終了コードだけを失敗として扱う」形で判定しています(about_Preference_Variables)。つまり「非ゼロ=失敗」は目安であって、個別の終了コード表が最優先です。
3つ目は自動化で一番つまずく点です。警告が赤く表示されても、終了コードが0なら呼び出し元は成功と判断することがあります。画面の色ではなく、コードで分岐してください。
終了コードで判断できるのは、次のようなことです。
- 次の処理へ進んでよいか
- 再試行するか、人の確認へ回すか
- CI(継続的インテグレーション=自動の検品と組み立て)を失敗として止めるか
確認方法の違いは、次の早見表のとおりです。
| 実行場所・対象 | 直後に確認する値 | 成功の目安 | 失敗を上位へ返す方法 | 注意点 |
|---|---|---|---|---|
| PowerShell・外部プログラム | $LASTEXITCODE | 0 | exit $code | 次の外部プログラムを実行する前に保存する |
| PowerShell・コマンドレット | $?または例外 | $true | throwまたはexit <数値> | $LASTEXITCODEは更新されないことがある |
| コマンドプロンプト | %ERRORLEVEL% | 0 | exit /b %code% | if errorlevel 1は「1以上」を表す |
GitHub Actionsのrun | 使用シェルの終了コード | 0 | シェルから非ゼロを返す | ログへのエラー表示だけでは失敗にならない |
PowerShellで確認する
この節の例は、PowerShell 7系(pwsh)を前提にしています。$LASTEXITCODEと$?の基本はWindows PowerShell 5.1でも同じですが、版で挙動が変わる箇所はその都度明記します。
外部の実行ファイルやバッチを動かした直後は、$LASTEXITCODEで確認します。公式の定義では、これは「最後に実行したネイティブプログラム(=PowerShellの外のプログラム)またはPowerShellスクリプトの終了コード」です(about_Automatic_Variables)。
cmd /c "exit /b 7"
$code = $LASTEXITCODE
Write-Host "終了コード: $code"
if ($code -ne 0) {
exit $code
}
要点は、対象コマンドの直後に値を変数へ退避することです。別の外部プログラムを実行すると、$LASTEXITCODEはその値へ上書きされます。
$?は、最後の操作が成功したかをTrueかFalseで示します。ネイティブコマンドでは、終了コード0のとき$?がTrue、非ゼロのときFalseになります。数値そのものを上位へ返したいなら$LASTEXITCODE、コマンドレット(PowerShell内部の命令)の成否だけ見たいなら$?や例外、という使い分けです。なお、PowerShell 7より前の版では、コマンドを丸括弧や部分式で囲むと$?がTrueへリセットされる挙動がありました。5.1で$?を使うときは、囲まずに直後で見てください。
外部プログラムはtry/catchだけで捕まらない
外部プログラムが非ゼロを返しても、PowerShellの既定動作ではcatchへ入りません。公式資料は「外部プログラムはPowerShellのエラーの仕組みに直接参加せず、非ゼロでもエラーレコードを作らず、catchやtrapを起動しない」と明記しています(about_Error_Handling)。だから、外部プログラムの直後は$LASTEXITCODEの確認が必要です。
PowerShell 7.4以降では、$PSNativeCommandUseErrorActionPreferenceを$trueにすると、非ゼロ終了をPowerShellのエラーとして扱えます(7.3で実験機能として入り、7.4で正式になりました)。さらに$ErrorActionPreference = 'Stop'と組み合わせると、catchできる停止エラーになります。ただしこの設定を入れても、robocopyのように非ゼロが失敗と限らないプログラムでは誤検知になります。対象コマンドごとに許容値を決めて明示的に判定する方が、あとから意図を追いやすくなります。
スクリプトの呼び方で$LASTEXITCODEの決まり方が変わる
同じスクリプトでも、呼び方で結果の受け取り方が変わります。
- スクリプトを直接、または
&で呼んだ場合: そのスクリプトがexitを実行するか、中でネイティブコマンドを動かさない限り、$LASTEXITCODEは変わりません。 pwsh -File スクリプト.ps1で呼んだ場合: 正常に最後まで走れば0、exit 数値で終われば その数値、例外で終われば1が返ります。
「手元で動かすと取れるのに、タスクスケジューラや別プロセスから呼ぶと取れない」ときは、まずこの呼び方の差を疑ってください。
パイプ処理の注意点
パイプ(|でつなぐ処理)では、直後に確認できる状態が末尾の処理に対応することがあります。producer.exe | formatter.exeの直後に$LASTEXITCODEを見ても、どちらが失敗したかを安全に切り分けられません。重要な処理ではパイプを分け、各コマンドの直後に確認します。
& .\producer.exe --output result.txt
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& .\formatter.exe result.txt
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
ログの読み方はCodexが途中で止まる原因をログから切り分ける方法も参考になります。
コマンドプロンプトで確認する
コマンドプロンプト(cmd)では、直前の終了コードをecho %ERRORLEVEL%で表示できます。バッチファイルでは、別のコマンドで値が変わる前に保存します。
@echo off
some-command.exe
set "code=%ERRORLEVEL%"
if not "%code%"=="0" (
echo 処理に失敗しました。終了コード: %code%
exit /b %code%
)
echo 処理に成功しました。
exit /b 0
exit /b <数値>は、Cmd.exe全体ではなく現在のバッチだけを終了し、その数値をERRORLEVELへ設定します(exitの公式資料)。バッチの外(コマンドラインに直接)で実行するとCmd.exe自体が終了する点も、同じ資料に書かれています。元の失敗を呼び出し元へ返すなら、保存した%code%を渡します。
cmd固有の落とし穴は2つあります(ifの公式資料)。
if errorlevel 1は「1と等しい」ではなく「1以上なら真」です。特定値との一致を見るならif "%code%"=="1"のように書き、成功と失敗の分岐なら上の例のように0との不一致を見ます。%ERRORLEVEL%は、ERRORLEVELという同じ名前の環境変数を自分で作ってしまうと、本物の終了コードではなくその変数の値に置き換わります。set ERRORLEVEL=...は書かないでください。
GitHub Actionsで失敗を伝える
GitHub Actionsのrunステップの成否は、ログの色や表示ではなく、使用シェルが最後に返した終了コードで決まります。公式のワークフロー構文の資料によると、組み込みのpwshとpowershellでは、ランナーがスクリプトの先頭に$ErrorActionPreference = 'stop'を、末尾に「$LASTEXITCODEがあればその値でexitする」処理を自動で追加します(Workflow syntax for GitHub Actions)。
同じ資料から、シェルごとの違いも分かります。
shell: bashを明示すると--noprofile --norc -eo pipefailで実行され、パイプの途中の失敗も検知されます。shell: cmdには完全な即時停止の仕組みがなく、各コマンドのエラーコードをスクリプト側で自分で確認する必要があります。shell:に自分でコマンドラインを書くカスタムシェルでは、組み込みシェルの自動追加を前提にできません。
複数の処理を1ステップに入れるときは、失敗した箇所で明示的に終了させると確実です。
- name: 検品を実行する
shell: pwsh
run: |
& .\tools\verify.exe
$code = $LASTEXITCODE
if ($code -ne 0) {
Write-Host "::error::検品に失敗しました(終了コード: $code)"
exit $code
}
Write-Host "検品に成功しました"
注意したいのは、::error::の表示は見た目の注釈にすぎないことです。エラー表示だけ出してexit 0で終えると、ステップは成功扱いになり得ます。「ログへ表示すること」と「非ゼロを返すこと」は別です。
配信前の工程全体はGitHub Actionsで記事サイトを自動検品する方法も参照してください。キャッシュを使っても、GitHub Actionsのキャッシュ入門のとおり検品自体は省略しません。
自動化へ組み込む確認の型
どの環境でも、失敗を見逃さない書き方は同じ型になります。
- コマンドを実行する。
- 直後に終了コードを変数へ保存する。
- そのコマンド固有の許容値と比較する(既定は
0、robocopyのような例外は公式の終了コード表に従う)。 - 非ゼロなら、秘密を含まない範囲でコマンド名と終了コードをログへ残す。
- 保存した同じコードで終了し、後続処理を実行しない。
終了コードは、表示しただけでは自動化を止めません。非ゼロを上位へ返し、後続を止めるところまでを一つの設計として扱います。
よくある質問
PowerShellで終了コードを表示するだけなら、何を入力しますか?
外部プログラムの実行直後に$LASTEXITCODEを入力します。説明付きならWrite-Host "終了コード: $LASTEXITCODE"です。あとで使う場合は、別の外部プログラムに上書きされる前に変数へ保存してください。
$LASTEXITCODEと$?は同じものですか?
同じではありません。$LASTEXITCODEは数値の終了コード、$?は最後の操作が成功したかのTrue/Falseです。ネイティブコマンドでは終了コード0がTrue、非ゼロがFalseに対応します。数値を上位へ返すなら$LASTEXITCODEを使います。
外部プログラムの失敗をtry/catchで捕まえられますか?
既定では捕まりません。外部プログラムの非ゼロ終了は、エラーレコードを作らずcatchも起動しないためです。PowerShell 7.4以降なら、$PSNativeCommandUseErrorActionPreferenceを$trueにし、$ErrorActionPreferenceをStopにすると、catchできる停止エラーにできます。版と対象コマンドの終了コード表を確認してから使ってください。
GitHub Actionsでエラーメッセージを出したのに成功扱いになるのはなぜですか?
メッセージの表示とステップの終了コードが別だからです。失敗させるには、使用中のシェルからexit 1などの非ゼロを返します。
失敗後も通知やログ保存のステップだけ動かせますか?
できます。後続ステップのifにfailure()などのステータス確認関数を指定します。GitHubの式の公式資料によると、ステータス確認関数を含まないifにはsuccess()が既定で適用されるため、失敗後にも動かす処理は条件を明示する必要があります(Evaluate expressions in workflows and actions)。通知の作り方はGitHub Actionsで失敗通知を受け取る方法も参照できます。
まとめ
PowerShellの外部プログラムは$LASTEXITCODE、コマンドレットは$?か例外、コマンドプロンプトは%ERRORLEVEL%とexit /b、GitHub Actionsは使用シェルの終了コード——確認する場所は違っても、「実行→直後に保存→許容値と比較→同じコードで返す」という型は共通です。次の3つから始めてください。
- 手元のスクリプトで、外部コマンドの直後に終了コードを変数へ保存する1行を足し、非ゼロなら同じコードで
exitする形に直す。 - よく使うコマンドの公式資料で終了コードの一覧を開き、「どの値まで成功として許容するか」を決めてスクリプトに書く。番号の意味は変わることがあるので、利用時点の公式案内で確認してください。
- GitHub Actionsを使っているなら、わざと失敗するコマンドを入れてステップが赤く止まるかを一度試す。AIへ自動化を頼むときも、「成功条件・許容する終了コード・失敗したら止める境界」を依頼文に含める。
公式一次情報
確認日: 2026-07-26
- about_Automatic_Variables - PowerShell | Microsoft Learn:
$LASTEXITCODEと$?の定義、スクリプトの呼び方による終了コードの決まり方が書かれています - about_Error_Handling - PowerShell | Microsoft Learn: 外部プログラムの非ゼロ終了が既定ではcatchを起動しないこと、7.4以降の扱いが書かれています
- about_Preference_Variables - PowerShell | Microsoft Learn:
$PSNativeCommandUseErrorActionPreferenceの説明と、robocopyを個別判定する公式例が書かれています - if | Microsoft Learn:
if errorlevelが「指定値以上」で真になる規則と、%ERRORLEVEL%と同名環境変数の注意が書かれています - exit | Microsoft Learn:
exit /bが現在のバッチだけを終了し、指定した数値をERRORLEVELへ設定することが書かれています - Workflow syntax for GitHub Actions - GitHub Docs: 組み込み
pwshが自動追加する処理と、bash・cmdなど各シェルの失敗検知の違いが書かれています - Setting exit codes for actions - GitHub Docs: アクションの終了コード
0が成功、非ゼロが失敗として状態に使われることが書かれています - Evaluate expressions in workflows and actions - GitHub Docs:
if条件にsuccess()が既定で適用されることと、failure()の動作が書かれています - Process | Node.js Documentation: Node.jsの終了コードの割り当て(
1=未捕捉の致命的例外など)とprocess.exitCodeの使い方が書かれています
続き(結論と実データ)はnoteに置いています
ここでは手順のところまで書きました。実際に出た数字、うまくいかなかった条件、そのまま使える設定ファイルは、 note の記事にまとめてあります。