Shell / Bashのstderrに印刷された項目の簡単なテストはありますか?

Shell / Bashのstderrに印刷された項目の簡単なテストはありますか?

継続的な統合のために、シェルスクリプトからコマンドを呼び出したいと思います。終了状態 0 は成功を示し、そうでなければ失敗します。複数のコマンドを実行し、いずれかのコマンドでエラーが発生した場合に失敗するようにラッパースクリプトを作成しています。

ただし、コマンドの1つ(サードパーティ製ソフトウェア)は、失敗時に「事実上」シャットダウンステータス!= 1に従わないことに注意してください。ただし、失敗が発生した場合は、stderrにエラーを印刷します。

現在のラッパースクリプトは、両方が正常に動作している場合、スイッチによってmycommandシャットダウン状態が原因で失敗します。 = 0:other-command-e

#!/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の通常の出力(やはり出力)では失敗しないでください。
  • 標準出力に他の有用な出力を維持したいと思います。
  • 私は現在stderrに印刷されているすべての出力を維持したいと思います(stdoutかもしれませんが、隠してはいけません)。

したがって、ラッパーのように動作し、不正な状態でのみ終了し、印刷物を保持する必要があります。

stderrで何でも確認できるもう少しエレガントな機能があったらと思います。侮辱は許可されています。

答え1

次のようにできます(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コマンドがゼロ終了ステータスを返しますが、いくつかのエラー出力を生成する状況には終了コードを使用します。

次のように使用されます。

fail_if_stderr cmd its args || echo "Failed with $?"

答え2

# 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"

答え3

最も多くの票を得た回答ほとんどの場合に機能します。しかし、set +o errexitbashで使用しているため、エラーが発生します。これはbashでよりうまく機能します。

fail_if_stderr() (
  # save current options
  bash_options="${-}"
  
  # disable exit on error
  set +o errexit
  
  # Save return code of command in rc
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  
  # Save return code of grep in err_in_stderr
  err_in_stderr=$?
  
  # enable exit on error if it was previously enabled
  test "${bash_options#*e*}" != "$bash_options" && set -o errexit
  
  # exit with original return code if it's not zero
  [ "$rc" -eq 0 ] || exit "$rc"
  
  # exit with return code 125 if something was in stderr
  [ "$err_in_stderr" -ne 0 ] || exit 125
) 3>&1

関連情報