標準エラーストリーム(stderr)をどのようにgrepしますか?

標準エラーストリーム(stderr)をどのようにgrepしますか?

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

    $ 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からエラーストリームを読み取るにはどうすればよいですか?

使用ニッククラフトリンクを介して標準エラーストリームを標準出力ストリームにリダイレクトし、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にリダイレクトしたくない場合はどうすればよいですか?

答え1

bash匿名パイプを使用しない理由は、基本的にphuneheheが言ったことを省略したことです。

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

答え2

|すべての汎用シェル(zshも含む)は、stdoutからstdinを除いて演算子を持つパイプを許可しません。ただし、すべてのBourneスタイルのシェルはファイル記述子の再割り当て(例1>&2:)をサポートしています。したがって、一時的にstdoutをfd 3に移動し、stderrをstdoutに移動してからfd 3をstdoutに戻すことができます。stuffstdoutでいくつかの出力を生成し、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

Ksh、bash、zsh は任意のファイル記述子でパイプをサポートしますが、異なる動作をします。 1つのコマンドが基本コマンドであり、入力または出力を別のコマンドにパイプできます。プロセスの置き換え基本コマンドのリダイレクトに使用できるパイプに接続されている標準入力または標準出力を使用して、バックグラウンドでコマンドを実行します。ここでは標準エラーをフィルタリングしたいので、それを出力プロセスの代わりにリダイレクトします。

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

>2文字の間にスペースがあることに注意してください。これは2>標準出力と>(…)プロセス置換をリダイレクトするためのものです。2>>…追加のリダイレクトとして解釈されるため、スペースが必要です。

答え3

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

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

この構成では、stderrは「mypipe」と呼ばれるパイプで接続されています。ファイルパラメータとして呼び出されたため、grep入力を取得するためにSTDINが見つかりません。残念ながら、作業を終えた後でも名前付きパイプをクリーンアップする必要があります。

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

答え4

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

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

通常、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 の出力を非表示にしますが、まだ stderr を表示します。まず、stdoutを/ dev / nullにパイプしてから、stderrをstdoutに変換します。なぜなら、Unixパイプはstdoutでしか機能できないからです。それでもテキストを見つけることができます。

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

(上記のコマンドは次のとおりです。その他then は./command >/dev/null 2>&1非常に一般的なコマンドです)。

テスト用のスクリプトです。これにより、stdoutに1行、stderrに1行が印刷されます。

#!/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

関連情報