プログラムの出力をgrepして出力を正常にエコーするにはどうすればよいですか?

プログラムの出力をgrepして出力を正常にエコーするにはどうすればよいですか?

私は何かが間違っているときにエラーメッセージを印刷しますが、それに応じて終了ステータスを設定しないプログラムを使用しています。終了ステータスは常に 0 で、成功を示します。シェルスクリプトでこのプログラムを実行し、エラーメッセージが表示されたら、スクリプトがプログラムに失敗するように指示できるように、ゼロ以外の終了ステータスを取得したいと思います。

エラーメッセージは、一致が見つかったかどうかに応じて終了ステータスを一致させ設定できる予測可能なパターンに従い、grepプログラムgrepの出力をここにパイプし、grep結果を無効にして目的の!終了ステータスを取得できます。

問題は、パイプで接続するとgrepできないことです。バラよりプログラムの出力はgrep消費されたため、もはや存在しません。どういうわけか出力からエラーメッセージを検索したいのですが、エラーに加えて他の重要なメッセージがあるので、出力も正常に表示したいと思います。残念ながら、grepすべての入力、一致する行、一致しない行を渡すオプションはありません。

私が見つけた一つの方法最大作品は次のとおりです

! my_program | tee /dev/stderr | grep -q "error message"

これはプログラムの出力をstderrに入れますが、grepstderrにもコピーするので、私が見ることができるようにリダイレクトされません。私のスクリプトのstdoutとstderrの両方がターミナルに送信されても​​大丈夫です(したがって、どちらがメッセージを受け取るかは問題ではありません)。ただし、stdoutとstderrが別の場所にリダイレクトされると、メッセージが終了する可能性があります。間違った場所。

POSIXシェルとGNUツールを使用してプログラムの標準出力および/または標準エラーストリームをスキャンし、それに応じて終了ステータスを設定し、両方のストリームを通常のターゲットに到達させるbash(簡潔な)方法はありますか?grep

my_programstderrではなくstdoutにエラーメッセージを書き込むので、stdoutストリームのみをスキャンできるソリューションは理想的ではありませんが、問題ありません。違いがある場合はCentOS 7でこれを行っています。)

答え1

...grepなどのツールを使用してプログラムのstdoutストリームおよび/またはstderrストリームをスキャンし、それに応じて終了ステータスを設定し、同時に2つのストリームを通常のターゲットに到達させる(簡潔な)方法はありますか?

...my_programは、標準エラーではなく標準出力にエラーメッセージを記録します。標準出力ストリームのみをスキャンするソリューションは問題ありません。しかし、理想的ではありません。

私の解決策は答えた。勇敢な上部です。

最も簡単な方法は次のことだと思いますawk

myprogram |
awk 'BEGIN {status = 0} /error message/ {status = 1} 1; END {exit(status)}'

このawkコマンドは、入力したすべてをそのまま出力しますが、最終的に終了する状態は、「エラーメッセージ」が入力の一部かどうかによって異なります。

簡潔なバージョン(短い変数名):

myprogram | awk 'BEGIN{s=0} /error message/{s=1} 1; END{exit(s)}'

答え2

$MSG以下は、stdout / stderrからエラーメッセージをgrepし、エラーメッセージが表示されたときにプログラムを停止/停止しないいくつかのライナーです。

# grep on stdout, do not stop early
my_program | awk -v s="$MSG" '$0~s{r=1} 1; END{exit(r)}'

# grep on stdout, do stop early
my_program | awk -v s="$MSG" '$0~s{exit(1)} 1'

# grep on stderr, do not stop early
{ my_program 2>&1 >&3 | awk -v s="$MSG" '$0~s{r=1} 1; END{exit(r)}' >&2; } 3>&1

# grep on stderr, do stop early
{ my_program 2>&1 >&3 | awk -v s="$MSG" '$0~s{exit(1)} 1' >&2; } 3>&1

メモ:

  • 誰にでも対応:アプリのストリームは着信を確認しながらデフォルトのgrepttyステータスを失います。これは、ストリームに書き込まれる方法に影響を与える可能性があります。たとえば、カーソル位置を制御できないと仮定することができ、回転している進行状況バーが印刷されない可能性があります。my_programawkmy_program

  • 両方について:stdoutとstderrはマージされず、通常どおり互いに独立してリダイレクトできます。

  • 誰にでも:元の終了コードmy_programは完全に無視されます。他の簡単なオプションは次のとおりです。my_programエラーまたはエラーメッセージで終了した場合は、エラーで終了します。。この動作を得るには、pipefailパイプラインを実行しているBashシェルでそれを有効にする必要があります。たとえば、

    (set -o pipefail; my_program | awk -v s="$MSG" '$0~s{exit(1)} 1')
    
  • stderrのgrep操作の場合:上記の簡単なコマンドラインでは、ファイル記述子3が使用されていないとします。これは一般的にできることです。これらの仮定を避けるために、Bash が使用されないように保証されるファイル記述子を割り当てるようにすることができます。

    bash -c 'exec {fd}>&1; { my_program 2>&1 >&${fd} | awk -v s="$MSG" '\''$0~s{exit(1)} 1'\'' >&2; } ${fd}>&1'
    

答え3

2つの端末を並べてみてください。最初のシェルウィンドウで:

tail -F /tmp/xyzzy

2番目は正確にtmpファイルに対してのみ行うことです。

my_program | tee /tmp/xyzzy | grep -q "error message"

その順番で始めてください。

一時ファイルを頻繁に削除するか、新しい名前を選択する必要があるため、不正ですが機能します。

後で追加するには...次のようにしてみてください。

my_program | tee /tmp/xyzzy ; grep -q "error message" /tmp/xyzzy

シーケンスの終了状態は grep の終了状態です。これはあなたが望むものとは反対です、ため息。だから否定してください。

my_program | tee /tmp/xyzzy ; ! grep -q "error message" /tmp/xyzzy

答え4

baz()出力するデモ機能の作成「金持ち」到着標準出力そして「バー」到着標準エラー:

baz() { echo foo ; echo bar >& 2 ; }

簡単な場合は実行してみてgrep fooください。標準出力、およびgrep bar情報標準エラー:

{ baz 2>&1 1>&3 | grep bar 1>&2 ; } 3>&1 | grep foo

同じ内容が使用されますが、tee出力はgrepまったく使用されていないようです。

{ baz 2>&1 1>&3 | tee /dev/stderr | grep -q bar ; } 3>&1 | \
{ tee /dev/stderr | grep -q foo ; } 2>&1

grep -q bar「BEEP!」を印刷して条件を追加してください。標準エラーもしバー発見する:

{ baz 2>&1 1>&3 | tee /dev/stderr | grep -q bar && echo "BEEP!" >&2 ; } 3>&1 | \
{ tee /dev/stderr | grep -q foo ; } 2>&1

最後の2行の出力は次のようになります。

foo
bar
BEEP!

関連情報