web-dev-qa-db-ja.com

Windowsバッチプログラミングでパイプ内のコマンドのエラーレベルを取得するにはどうすればよいですか?

バッチファイルは、デフォルトで最後のコマンドのエラーコードを返します。

以前のコマンドのエラーコードを返すことはどういうわけか可能ですか?最も注目すべきは、パイプ内のコマンドのエラーコードを返すことは可能ですか?

たとえば、この1行のバッチスクリプト

foo.exe

fooのエラーコードを返します。しかし、これは:

foo.exe | tee output.txt

常にteeの終了コードを返します。これはゼロです。

27
mkl

成功または失敗だけで正確なエラーコードを検出する必要がなかったので、同様の問題が発生し、次の解決策に落ち着きました。

echo > .failed.tmp    

( foo.exe && del .failed.tmp ) | tee foo.log

if exist .failed.tmp (
    del .failed.tmp
    exit /b 1
) else (
    exit /b 0
)
9
Nick Collier

パイプされたコマンドが実行される前に、%ERRORLEVEL%変数は更新されません。他の回答でアドバイスされているように、ラッパーを使用する必要があります。

ただし、can「IFERRORLEVEL#」を使用します。例えば:

(
type filename
@REM Use an existing (or not) filename to test each branch
IF ERRORLEVEL 1 (echo ERROR) ELSE (echo OKAY)
) > logfile.txt

ECHOは、エラーが返された場合にのみ実行されます。ただし、%ERRORLEVEL%には一貫性がないようです。

編集:サンプルをそのまま実行できるように変更しました。

3
whitey04

回避策の1つは、ファイルを介して間接参照を作成することです。

このような

foo.exe > tmp.txt
set FOOERR=%ERRORLEVEL%
cat tmp.txt
exit %FOOERR%
3
mkl

約1日掘った後、私はそれを行う方法を見つけました。

set error_=0
9>&1 1>&2 2>&9 (for /f "delims=" %%i in ('9^>^&1 1^>^&2 2^>^&9 ^(^(^(2^>^&1 call "%homeDir%%1"^) ^|^| ^(1^>^&2 2^>nul echo FAILED^)^) ^| 2^>nul "%homeDir%mtee" /T /+ "%homeDir%logs\%date_%_%1.log"^)') do (set error_=1))

exit /b %error_%

上記の例では、「%homeDir %% 1」が実行されており、その出力は「%homeDir%mtee」にパイプされています。この行は失敗を検出します(それが何をするのかを理解するために、バッチコンテキストとそのstdin/stdout/stderr割り当ての図を描くことをお勧めします:-))。実際のエラーレベルを抽出する良い方法が見つかりませんでした。私が得た最良のことは、「echo」コマンドを次のようなバッチスクリプト呼び出し「callrc.bat」に置き換えることでした。

@echo %errorlevel%

次に、「set error_ = 1」を「seterror_ = %% i」に置き換えます。

しかし、問題は、この呼び出しも失敗する可能性があり、それを検出するのは簡単ではないということです。それでも、それは何もないよりもはるかに優れています-私はインターネット上でその解決策を見つけられませんでした。

2
crusader

スクリプト

foo.exe && set dummy= | tee output.txt

foo.exeのエラーコードを返します。 &&の後ろにダミーコマンドを追加すると、&&の前のコマンドの終了コードが保持されます。ダミーコマンドの終了コードは関係ありません。ダミーコマンドは何も出力しないはずであり、できればまったく効果がないことが望ましいです。パイプコマンドは独自のcmdシェルで実行されるため、環境変数をダミーコマンドとして設定しても効果はありません。

コマンドがより長いスクリプトの一部である場合、スクリプトの実行を停止し、正しい終了コードを返すため、次の行が役立ちます。

foo.exe && set dummy= | tee output.txt || exit /b !errorlevel! 

これをWindows7とWindows10でテストしましたが、&&と|の組み合わせの動作を説明するドキュメントは見つかりませんでした。誰かがそのようなドキュメントを説明または参照できますか?

2
J. Pesara

単一のコマンドではなく、エントリbat-fileに対してteeを呼び出し、errorlevelを自由に使用するには、次のようなトリックを使用します。

if "%1" == "body" goto :body
call %0 body | tee log.txt
goto :eof
:body

set nls_lang=american_america
set HomePath=%~dp0

sqlplus "usr/pwd@tnsname" "@%HomePath%script.sql" 
if errorlevel 1 goto dberror

rem Here I can do something which is dependent on correct finish of script.sql    

:dberror

echo script.sqlerror failed

teeの使用と、バッチ内のコマンドの呼び出しを分離します。

2
Nashev

コマンドファイルのラッパーを作成することで、問題を解決できます。

rem wrapper for command file, wrapper.cmd

call foo.exe

echo %errorlevel%

if errorlevel 1 goto...

次に、ラッパーにteeを追加します。

wrapper.cmd | tee result.log

もちろん、これは完全に同じではありません。ラップされたファイルに複数のファイルをログインしたい場合、それは不可能ですが、私の場合は問題を解決しました。

1
Fritz