バッチファイルの for /f で実行したコマンドのエラーを検出する

課題

バッチファイルで、コマンドの実行結果を変数に保存(キャプチャ) したいときに for /f コマンドがしばしば用いられます。


次のような使い方ですね。

for /f "usebackq delims=" %%i in (`dir /b`) do echo Value: %%~i


ただ、コマンド(ここでは dir コマンド) が正常に終了する場合は、このような記載で問題ないのですが、多くのコマンドはたまに失敗することがあります。


例えば、下記のようなバッチファイルを実行してみます。
HOGE は存在しないファイルとします。dir コマンドは存在しないファイルを表示する場合に失敗します。

@echo off
for /f "usebackq delims=" %%i in (`dir /b HOGE`) do echo Value: %%~i

実行結果です。

ファイルが見つかりません


標準エラー出力にエラーが出力されるため、人が見ればエラーがあったことが分かりますが、バッチスクリプト的(for コマンドの do 以降で実施される処理的) にはエラーがあったかどうか分かりません。

解決

このような場合、次のように記述すれば for /f で実行したコマンドのエラーを検出することができます。

@echo off
for /f "usebackq delims=" %%i in (`dir /b HOGE ^|^| echo *ERROR*`) do if "%%i" == "*ERROR*" (
	echo dir コマンドに失敗しました。
) else (
	echo dir コマンドに成功しました。
	echo Value: %%~i
)

実行結果です。

ファイルが見つかりません
dir コマンドに失敗しました。

コマンドのエラーを検出できていることが分かります。

ポイント

下記のようなバッチの記述方法を利用しています。

コマンド1 || コマンド2

"コマンド2" は "コマンド1" が失敗した場合にのみ実行されます。


上述の例で言えば dir コマンドが失敗した場合にのみ、echo *ERROR* が実行され、文字列 *ERROR* が %%i 変数にキャプチャされます。
このため、%%i 変数の内容が *ERROR* か否かによってエラーの有無を判別することができます。


本例ではエラーの有無を判別する文字列を *ERROR* としていますが、全くの任意です。ただ、"コマンド1" の出力として現れないような文字列にしておくことがベターかと思われます。

なお、| (縦線) を for /f コマンドで記述する際は、上述の例のとおり ^ (ハット) でエスケープして ^| と記述する必要があります。

注意点

下記の記述方法で失敗と判断("コマンド2" が実行) される条件ですが、"コマンド1" が 0 以外を返却した場合のもようです。

コマンド1 || コマンド2


次の実験で確認できます。

C:\>cmd /c "exit /b 1" || echo ERROR
ERROR

C:\>cmd /c "exit /b 0" || echo ERROR

C:\>cmd /c "exit /b -1" || echo ERROR
ERROR


このため、正常に終了する場合でも 0 以外を返却するコマンドに対しては、今回の手法は利用できません。

例えば robocopy コマンドは、正常に終了する場合でも 0 以外を返却する場合があります。