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


76

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

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にリダイレクトしたくない場合はどうでしょうか?


1
私はそれgrepがstdoutでしか動作できないと信じています(それをバックアップする標準的なソースを見つけることができませんが)、つまり、ストリームは最初にstdoutに変換する必要があります。
ステファンLasiewski

9
@Stefan:grepstdinでのみ操作できます。grepのstdinを他のコマンドのstdoutに接続するのは、シェルによって作成されたパイプです。また、シェルはstdoutをstdinにのみ接続できます。
ジル

おっと、あなたは正しい。それが私が本当に言いたいことだと思う、私はただそれを考えなかった。@Gilesに感謝します。
ステファンLasiewski

それでも標準出力を印刷しますか?
ミケル

回答:


52

bash匿名パイプを使用しない理由を使用している場合、本質的にプネヘヘが言ったことの短縮形で:

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


3
+1いいね!Bashのみですが、他の方法よりもずっときれいです。
ミケル

1
+1 cp出力を確認するためにそれを使用しましたcp -r dir* to 2> >(grep -v "svn")
Betlista 14

5
フィルターされたリダイレクトされた出力をstderrで再度使用する場合は>&2、たとえばcommand 2> >(grep something >&2)
tlo

2
これがどのように機能するか説明してください。2>stderrをファイルにリダイレクトします。これはfileから読み取ります>(grep -i Duration)。しかし、ファイルは保存されませんか?このテクニックの詳細を読むことができるように、このテクニックは何と呼ばれていますか?
マルコアヴリャシュ

1
パイプ(ファイルではなく)を作成し、完了したらそのパイプを削除するのは「構文糖」です。ファイルシステムで名前が与えられていないため、実質的に匿名です。Bashはこのプロセス置換を呼び出します。
JEキュー

48

通常のシェル(zshでさえ)は、stdoutからstdin以外のパイプを許可しません。しかし、すべてのBourneスタイルのシェルは、ファイル記述子の再割り当てをサポートしています(1>&2)。そのため、一時的にstdoutをfd 3に、stderrをstdoutに変更し、後でfd 3をstdoutに戻すことができます。場合はstuff標準出力にいくつかの出力と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

このアプローチは機能します。残念ながら、私の場合、ゼロ以外の戻り値が返されると、失われます。返される値は0です。これは常に起こるとは限りませんが、現在見ている場合に起こります。保存する方法はありますか?
ファヒムミタ

2
@FaheemMithaあなたが何をしているかわからないが、多分pipestatus役立つだろう
ジル

1
@FaheemMithaもset -o pipefail、エラーステータスをどうするかによって、ここで役立つ場合があります。(たとえばset -e、エラーで失敗するようにオンにした場合は、おそらく必要ですset -o pipefail。)
ワイルドカード

@Wildcardはい、set -eエラーが発生すると失敗するように設定しました。
ファヒムミタ

rcシェルは、配管標準エラー出力を可能にします。以下の私の答えをご覧ください。
ロルフ

20

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

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

この構造では、stderrは「mypipe」という名前のパイプに送られます。以来grep、ファイルの引数で呼ばれてきた、それはその入力のためのSTDINに見ていないだろう。残念ながら、完了したら名前付きパイプをクリーンアップする必要があります。

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



1
この|&構文は、肥大化したRuby stderrスタックトレースをクリーンアップするのに最適です。私は最終的にそれらをあまり手間をかけずにgrepできます。
PGR

8

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

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

私はあなたがffmpeg's標準出力を印刷したくないと仮定しています。

使い方:

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

この説明は私を混乱させます。最後の2つの箇条書きは、「stderrをstdoutにリダイレクトする」、次に「stdoutを(現在stderrを使用して)リダイレクトする/ dev / null」と考えさせます。しかし、これはこれが実際に行っていることではありません。これらのステートメントは逆になっているようです。
スティーブ

1
@Steve 2番目の箇条書きには「with stderr」はありません。unix.stackexchange.com/questions/37660/order-of-redirectionsを見ましたか?
ミケル

いいえ、それはあなたが英語でどのように説明したかについての私の解釈です。ただし、提供されたリンクは非常に便利です。
スティーブ

なぜ/ dev / nullにリダイレクトする必要があったのですか?
Kanwaljeet Singh

7

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

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への出力を非表示にしますが、依然としてstderrを表示します。最初にstdoutを/ dev / nullにパイプし、次にstderrをstdoutに変換します。Unixパイプはstdoutでのみ動作するためです。それでもテキストをgrepできます。

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

(上記のコマンドであることに注意してください異なる次いで、./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

リダイレクトを切り替える場合、すべての中括弧は必要ありません。ただやる./stdout-stderr.sh 2>&1 >/dev/null | grep err
ミケル

3

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

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

あなたが望むものを出力しません(しかし、動作します)。

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

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

ありがとう。it does work, though、それはあなたのマシンで動作しているということですか?第二に、パイプを使用して指摘したように、stdoutのみをリダイレクトできます。stderrをリダイレクトできるコマンドまたはbash機能に興味があります。(ただし、一時ファイルのトリック)
アンドリュー・Dufresne

@Andrewつまり、コマンドは機能するように設計された方法で機能します。それはちょうどあなたがそれを望むように動作しません:)
phunehehe

コマンドのエラー出力を別のコマンドの標準入力にリダイレクトできる方法は知りません。誰かがそれを指摘できれば面白いでしょう。
プネヘヘ

2

ストリームを交換できます。これにより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

1

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

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

これは、破棄stdoutstderrてgrepにパイプする方法の例ですrc

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

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

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

お気づきかもしれませんが、構文は単純であるため、この方法をお勧めします。

パイプ文字の直後に、かっこ内にパイプするファイル記述子を指定できます。

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

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

0

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

2行をstderrにエコーし、stderrをファイルにエコーし、ティーをgrepし、stdoutにパイプで戻します。

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

標準出力:

fff

some.load.errors(例:stderr):

asdf
fff

-1

このコマンドを試してください

  • ランダムな名前でファイルを作成
  • 出力をファイルに送信する
  • ファイルのコンテンツをパイプに送信します
  • grep

ceva->単なる変数名(英語=何か)

ceva=$RANDOM$RANDOM$RANDOM; ffmpeg -i qwerty_112_0_0_record.flv 2>$ceva; cat $ceva | grep Duration; rm $ceva;

私は/tmp/$ceva、現在のディレクトリに一時ファイルを散らかすよりも良い方法を使用します。そして、あなたは現在のディレクトリが書き込み可能であると仮定しています。
ロルフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.