【PowerShell】PowerShellの$LASTEXITCODEとcmdの%ERRORLEVEL%が一致しないケース

前書き

約二年ぶりの更新です。元々メモ書きのようなブログですが、ここ二年ほどメモするほどの内容がありませんでした。

Spring Boot とか、Flutter とか、vue.js とか、Azure のアレコレとか、AWS のアレコレとか、Firebase のアレコレとか、色々やってたんですけどね。

上記のうち、Flutter の話はいつかしますが、今日は PowerShell の話です。

$LASTEXITCODEとは

PowerShell には自動変数と呼ばれる、一種の予約語のような変数があります。

$LASTEXITCODEはその中の一つであり、

最後に実行された Windows ベースのプログラムの終了コードが格納されます。

と書いてある通り、他プロセスの実行結果が格納されます。

.batでバリバリとバッチ処理を作ったことがある人なら「あ、%ERRORLEVEL%みたいなもんね。」と思うかもしれません。合ってます。合っていますが、完全に%ERRORLEVEL%と互換性があるわけではなく、それが原因でめちゃめちゃにハマりました。

$LASTEXITCODE%ERRORLEVEL%が一致しなくなるバッチ

こんな.batファイルを作ります。サブルーチンの方でexit /b 1を返し、メイン処理は特に何も返さない、それだけの処理です。

@echo off
call :hoge
exit /b
:hoge
exit /b 1

作った.batファイルをコマンドプロンプトで呼び出し、%ERRORLEVEL%を調べます。普通に1が返ってきます。

> test.bat

> echo %ERRORLEVEL%
1

PowerShell で同じように呼び出し、$LASTEXITCODEを調べます。

> .\test.bat

> $LASTEXITCODE
0

$LASTEXITCODE%ERRORLEVEL%を見ているわけではないので、メイン処理の返り値を取得してしまいます。よって、0が表示されます。

ワークアラウンド

自分で作った.batならメイン処理でexit /b %ERRORLEVEL%とすればいいんですが、諸々の事情*1.batの方を直せない場合、PowerShell 側で何とかする必要があります。

考え方は簡単です。明示的にcmd.exe /Cでバッチを叩いた後、exit %ERRORLEVEL%すればいいだけです。

&でコマンドを連続実行してやればワンライナーで楽勝です。遅延環境変数を展開できるよう/V:ONの指定を忘れずに。

> cmd /V:ON /C "test.bat & exit !ERRORLEVEL!"

> $LASTEXITCODE
1

まとめ

うーん。めんどくさい。

*1:他チームが作ったバッチ処理なので勝手に直せない、とか…。