オーディオクリップのメタ情報を取得するために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にリダイレクトしたくない場合はどうでしょうか。
bash
を使用している場合は、なぜ匿名パイプを使用しないのか、本質的にはphuneheheが言ったことの省略形です。
ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)
通常のシェル(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にリダイレクトしています。
GillesとStefan Lasiewskiの回答はどちらも良いですが、この方法の方が簡単です。
ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"
私はあなたが欲しくないと思いますffmpeg's
stdoutが印刷されました。
使い方:
これらのテストで使用されるスクリプトについては、以下を参照してください。
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
ストリームを交換できます。これにより、元の標準エラーストリームを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
(|
を使用して)コマンドの出力を別のコマンドにパイプすると、リダイレクトされるのは標準出力だけです。それが理由を説明するはずです
ffmpeg -i 01-Daemon.mp3 | grep -i Duration
あなたが望んだものを出力しません(しかしそれは動作します)。
エラー出力を標準出力にリダイレクトしたくない場合は、エラー出力をファイルにリダイレクトし、後でそれをgrepできます。
ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
bash
は標準パイプを介してstdoutをstdinストリームにリダイレクトできます-|
|&
によってstdoutとstderrの両方をstdinにリダイレクトできます
この場合、rc
シェルを使用するのが好きです。
最初にパッケージをインストールします(1MB未満です)。
これは、stdout
を破棄し、stderr
をrc
のgrepにパイプする方法の例です。
find /proc/ >[1] /dev/null |[2] grep task
あなたはバッシュを離れることなくそれを行うことができます:
rc -c 'find /proc/ >[1] /dev/null |[2] grep task'
お気づきかもしれませんが、構文は単純なので、これが私の望ましい解決策になります。
大括弧内のパイプ文字の直後にパイプ処理するファイル記述子を指定できます。
標準ファイル記述子は、次のように番号が付けられています。
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