web-dev-qa-db-ja.com

シェル/バッシュのstderrに印刷されたものの簡単なテストはありますか?

継続的インテグレーションのために、シェルスクリプト内でコマンドを呼び出したいのですが。終了ステータス0は成功、それ以外の場合は失敗を意味します。複数のコマンドを実行し、そのうちの1つからエラーが発生した場合に失敗するラッパースクリプトを作成しています。

ただし、コマンドの1つ(サードパーティソフトウェア)は、「事実上の」終了ステータス!= 1に準拠していません。ただし、障害が発生した場合は、エラーをstderrに出力します。

other-commandスイッチが原因でmycommand-eの両方が終了ステータス!= 0で失敗する場合は、現在のラッパースクリプトが正常に機能しているはずです。

#!/bin/bash -ex
mycommand --some-argument /some/path/to/file
other-command --other-argument /some/other/file

Stderrに出力されているものをチェックするにはどうすればよいですか(メインスクリプトを失敗させるため)?これが私が試したものです:

  1. stderr出力はファイルにリダイレクトされ、ファイルの内容を確認します。
    一時ファイルを作成しないようにします。
  2. Stderrをサブシェルstdinにリダイレクトします。例:

    mycommand 2> >(if grep .; then echo NOK; else echo OK; fi)
    

    これは正常に動作するようですが、ここでメインシェルを制御して終了することはできません。つまり、exit 1はメインプログラムを終了しません。また、サブシェルの外部で変数を制御してその結果を伝播することもできません。名前付きパイプなどを本当に作成する必要がありますか?

  3. この答え のような追加のファイル記述子を設定します。
    本当にエレガントに見えません。

いくつかの「要件」:

  • Stdoutでの通常の出力(そこへの出力も)で失敗しないはずです。
  • それ以外の場合は有用な出力をstdoutに保持したいと思います。
  • 現在stderrに出力されている出力を保持したい(標準出力にすることもできますが、非表示にしないでください)。

したがって、印刷された出力を保持したまま、クリーンでないステータスでのみ終了するラッパーのように動作する必要があります。

Stderrで何かをチェックするためにもっとエレガントなものが存在することを期待していました。バシズムは受け入れられる。

5
gertvdijk

あなたができる(POSIXly):

if { cmd 2>&1 >&3 3>&- | grep '^' >&2; } 3>&1; then
  echo there was some output on stderr
fi

または、元の終了ステータスがゼロ以外の場合にそれを保持するには:

fail_if_stderr() (
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  err=$?
  [ "$rc" -eq 0 ] || exit "$rc"
  [ "$err" -ne 0 ] || exit 125
) 3>&1

終了コードの使用125は、コマンドが0の終了ステータスで戻るが、いくつかのエラー出力を生成した場合。

として使用する:

fail_if_stderr cmd its args || echo "Failed with $?"
3
# output "NOK" if standard error has any output; "OK" otherwise:
errlog=$(mktemp)
somecommand 1>> "$stdlog" 2> "$errlog"
if [[ -s "$errlog" ]]; then
    # File exists and has a size greater than zero
    echo "NOK"
else
    echo "OK"  
fi
# Done parsing standard error; tack it to the regular log
cat "$errlog" >> "$stdlog"
rm -f "$errlog"
0
DopeGhoti