web-dev-qa-db-ja.com

標準エラーストリーム(stderr)をgrepする方法

オーディオクリップのメタ情報を取得するためにffmpegを使用しています。しかし、私はそれをgrepすることができません。

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --Arch=i386 --extra-cflags=-O2 
      ...

私がチェックしたところ、このffmpeg出力はstderrに送信されます。

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

したがって、grepはエラーストリームを読み取って一致する行をキャッチすることができないと思います。 grepでエラーストリームを読み取れるようにするにはどうすればよいですか?

nixCraft リンクを使用して、標準エラーストリームを標準出力ストリームにリダイレクトすると、grepが機能しました。

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

しかし、stderrをstdoutにリダイレクトしたくない場合はどうでしょうか。

90
Andrew-Dufresne

bashを使用している場合は、なぜ匿名パイプを使用しないのか、本質的にはphuneheheが言ったことの省略形です。

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

66
Jé Queue

通常のシェル(zshも含む)はどれも、stdoutからstdin以外のパイプを許可しません。ただし、すべてのBourneスタイルのシェルは、ファイル記述子の再割り当てをサポートしています(1>&2など)。したがって、stdoutを一時的にfd 3に迂回させ、stderrをstdoutに迂回させ、後でfd 3をstdoutに戻すことができます。 stuffがstdoutにいくつかの出力を生成し、stderrに一部の出力を生成し、標準出力をそのままにしてエラー出力にfilterを適用する場合は、{ stuff 2>&1 1>&3 | filter 1>&2; } 3>&1を使用できます。

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error

これはphuneheheの「一時ファイルトリック」に似ていますが、代わりに名前付きパイプを使用するため、結果が出力されたときに少し近づくことができ、実行時間の長いコマンドに便利です。

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

この構成では、stderrは「mypipe」という名前のパイプに送られます。 grepはファイル引数を指定して呼び出されているため、STDINの入力を参照しません。残念ながら、完了したら、その名前付きパイプをクリーンアップする必要があります。

Bash 4を使用している場合、command1 2>&1 | command2、つまりcommand1 |& command2のショートカット構文があります。ただし、これは純粋に構文のショートカットであると私は考えています。まだSTDERRをSTDOUTにリダイレクトしています。

22
Steven D

GillesとStefan Lasiewskiの回答はどちらも良いですが、この方法の方が簡単です。

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

私はあなたが欲しくないと思いますffmpeg's stdoutが印刷されました。

使い方:

  • 最初にパイプ
    • ffmpegとgrepが開始され、ffmpegのstdoutがgrepのstdinに移動します
  • 次に、左から右へのリダイレクト
    • ffmpegのstderrは、そのstdoutが何であるかに設定されています(現在はパイプ)
    • ffmpegの標準出力は/ dev/nullに設定されています
11
Mikel

これらのテストで使用されるスクリプトについては、以下を参照してください。

Grepはstdinでのみ動作できるため、Grepが解析できる形式でstderrストリームを変換する必要があります。

通常、stdoutとstderrはどちらも画面に出力されます。

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

Stdoutを非表示にして、それでもstderrを印刷するには、次のようにします。

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

しかし、grepはstderrでは動作しません!次のコマンドは 'err'を含む行を抑制しますが、そうではありません。

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

これが解決策です。

次のBash構文はstdoutへの出力を非表示にしますが、標準エラーを表示します。 Unixパイプはstdoutでのみ動作するため、最初にstdoutを/ dev/nullにパイプし、次にstderrをstdoutに変換します。あなたはまだテキストをgrepできます。

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr

(上記のコマンドはdifferent then ./command >/dev/null 2>&1、これは非常に一般的なコマンドです)。

テストに使用したスクリプトは次のとおりです。これにより、1行がstdoutに出力され、1行がstderrに出力されます。

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0
8

ストリームを交換できます。これにより、元の標準エラーストリームをgrepしながら、端末の標準出力に最初に送信された出力を取得できます。

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'

これは、まず出力用に開く新しいファイル記述子(3)を作成し、それを標準エラーストリーム(3>&2)。次に、標準エラーを標準出力(2>&1)。最後に、標準出力が元の標準エラーにリダイレクトされ、新しいファイル記述子が閉じられます(1>&3-)。

あなたの場合:

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration

それをテストする:

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output
3
Kusalananda

|を使用して)コマンドの出力を別のコマンドにパイプすると、リダイレクトされるのは標準出力だけです。それが理由を説明するはずです

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

あなたが望んだものを出力しません(しかしそれは動作します)。

エラー出力を標準出力にリダイレクトしたくない場合は、エラー出力をファイルにリダイレクトし、後でそれをgrepできます。

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
3
phunehehe

bashは標準パイプを介してstdoutをstdinストリームにリダイレクトできます-||&によってstdoutとstderrの両方をstdinにリダイレクトできます

1
0x00

この場合、rcシェルを使用するのが好きです。

最初にパッケージをインストールします(1MB未満です)。

これは、stdoutを破棄し、stderrrcのgrepにパイプする方法の例です。

find /proc/ >[1] /dev/null |[2] grep task

あなたはバッシュを離れることなくそれを行うことができます:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

お気づきかもしれませんが、構文は単純なので、これが私の望ましい解決策になります。

大括弧内のパイプ文字の直後にパイプ処理するファイル記述子を指定できます。

標準ファイル記述子は、次のように番号が付けられています。

  • 0:標準入力
  • 1:標準出力
  • 2:標準エラー
1
Rolf

Bashサブプロセスの例のバリエーション:

stderrおよびtee stderrに2行をエコーし​​てファイルに出力し、trepをgrepしてstdoutにパイプバックします。

(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)

stdout:

fff

some.load.errors(例:stderr):

asdf
fff
0
jmunsch