stdoutではなくstderrをパイプするにはどうすればよいですか?


982

stdoutand stderrに情報を書き込むプログラムがあり、stdoutを無視しgrepて、stderrに送信される内容を確認する必要があります。

もちろん、2つのステップで実行できます。

command > /dev/null 2> temp.file
grep 'something' temp.file

しかし、私はこれを一時ファイルなしでできるようにしたいと思います。賢い配管のコツはありますか?


同様の質問が、保持STDOUT:unix.stackexchange.com/questions/3514/...
joeytwiddle

この質問はBashに関するものでしたが、Bourne / Almquistシェルに関するこの関連記事について言及する価値があります。
Stephen Niedzielski 2014

10
私はこのようなものを期待していました:command 2| othercommand。Bashは完璧なので、開発は1982年に終了しました。そのため、bashでそれが見られることはないと思います。
ロルフ

回答:


1190

最初にstderrをstdout(パイプ)にリダイレクトします。次にstdoutをリダイレクトします/dev/null(stderrの場所を変更せずに):

command 2>&1 >/dev/null | grep 'something'

さまざまなI / Oリダイレクトの詳細については、Bashリファレンスマニュアルのリダイレクトに関する章を参照してください。

I / Oリダイレクトのシーケンスは左から右に解釈されますが、I / Oリダイレクトが解釈される前にパイプが設定されることに注意してください。1や2などのファイル記述子は、開いているファイルの説明への参照です。この操作2>&1により、ファイル記述子2別名stderrは、ファイル記述子1別名stdoutが現在参照しているのと同じオープンファイル記述を参照します(dup2()およびを参照open())。次に、操作>/dev/nullはファイル記述子1を変更して、のオープンファイル記述を/dev/null参照しますが、ファイル記述子2が、ファイル記述子1が最初に指していたオープンファイル記述、つまりパイプを参照するという事実は変更されません。


44
先日/ dev / stdout / dev / stderr / dev / stdinを偶然見つけましたが、それらが同じことをする良い方法であるかどうか知りたいですか?2>&1は少し難読化されているといつも思っていました。だから、のようなもの: command 2> /dev/stdout 1> /dev/null | grep 'something'
マイク・リヨン

17
あなたは使うことができ/dev/stdoutら、または使用/dev/fd/N。シェルがそれらを特別なケースとして扱わない限り、それらはわずかに効率が悪くなります。純粋な数値表記は名前によるファイルへのアクセスを含みませんが、デバイスを使用することはファイル名のルックアップを意味します。それを測定できるかどうかは議論の余地があります。私は数値表記の簡潔さが好きですが、私はそれを非常に長い間(四半世紀以上;痛い!)使用しており、現代の世界でそのメリットを判断する資格がありません。
ジョナサンレフラー、2011年

23
@ジョナサンレフラー:私はあなたのプレーンテキストの説明で少し問題を取る「stdoutにリダイレクト標準エラー出力と、その後 stdoutには/ dev / nullに」 - 1は(ない左から右へ)右から左へのリダイレクトチェーンを読み込む必要があるため、我々 :また、これに私たちのプレーンテキストの説明を合わせる必要があります「をstderrをstdoutが可能に使用される場所へのリダイレクトをstdoutには/ dev / nullに、そして」を
カートPfeifle

116
@KurtPfeifle:au contraire!リダイレクトチェーンは、シェルがそれらを処理する方法であるため、左から右に読む必要があります。最初の操作はです2>&1。これは、「stderrを現在 stdoutが使用するファイル記述子に接続する」ことを意味します。2番目の操作は「stdoutを変更して次のようにする/dev/null」であり、stderrを元のstdoutであるパイプに残します。シェルはパイプシンボルで最初に物事を分割するため、パイプのリダイレクトは2>&1または>/dev/nullリダイレクトの前に発生しますが、それだけです。その他の操作は左から右です。(右から左へは機能しません。)
ジョナサンレフラー

14
これについて本当に驚いたのは、それがWindowsでも機能することです(/dev/nullWindowsの同等の名前に変更した後nul)。
Michael Burr

364

または、標準エラーと標準出力の出力を入れ替えるには、次のコマンドを使用します。

command 3>&1 1>&2 2>&3

これにより、新しいファイル記述子(3)が作成され、1(標準出力)と同じ場所に割り当てられます。次に、fd 1(標準出力)がfd 2(標準エラー)と同じ場所に割り当てられ、最後にfd 2(標準エラー)が割り当てられます。 )fd 3(標準出力)と同じ場所に。

標準エラーが標準出力として利用可能になり、古い標準出力は標準エラーに保持されます。これはやり過ぎかもしれませんが、Bashファイル記述子の詳細が表示されると幸いです(各プロセスで9つ利用できます)。


100
最後の微調整は3>&-、stdoutから作成したスペア記述子を閉じることです
ジョナサンレフラー

1
我々は持っているファイルディスクリプタを作成することができますstderrの組み合わせを持っており、別のものをstderrstdout?つまりstderr、2つの異なるファイルに同時に移動できますか?
スチュアート

次の場合でも、エラーはstdoutに出力されます。何が欠けていますか?ls -l not_a_file 3>&1 1>&2 2>&3> errors.txt
user48956

1
@JonasDahlbæk:調整は主に整頓の問題です。真に不可解な状況では、プロセスがEOFを検出することと検出しないことの違いが生じる可能性がありますが、これには非常に特殊な状況が必要です。
Jonathan Leffler 2017年

1
注意:これは、FD 3がまだ使用されていないこと、FD 3を閉じないこと、ファイル記述子1と2の交換を取り消さないことを前提としているため、これをパイプしてさらに別のコマンドに進むことはできません。詳細と回避策については、この回答を参照してください。{ba、z} shのより明確な構文については、この回答を参照してください。
トム・ヘイル

218

Bashでは、プロセス置換を使用してサブシェルにリダイレクトすることもできます。

command > >(stdlog pipe)  2> >(stderr pipe)

目前の場合:

command 2> >(grep 'something') >/dev/null

1
画面への出力に非常によく機能します。grepの出力をファイルにリダイレクトした場合に、再現されていないコンテンツが再び表示される理由はありますか?command 2> >(grep 'something' > grep.log)grep.logにungrepped.logと同じ出力が含​​まれた後command 2> ungrepped.log
Tim

9
を使用し2> >(stderr pipe >&2)ます。そうでない場合、「stderrパイプ」の出力は「stdlogパイプ」を通過します。
2016年

ええ、2> >(...)うまく2>&1 > >(...)
いきまし

次に、これを行う方法を調べるときに役立つ可能性がある小さな例を示します。次のことを考えてみましょう... awk -f /new_lines.awk <in-content.txt > out-content.txt 2> >(tee new_lines.log 1>&2 ) この例では、私はしたかっ私のコンソールにエラーとして出てきたものを参照してください。しかし、STDOUTは出力ファイルに移動していました。したがって、サブシェル内では、そのSTDOUTを括弧内のSTDERRにリダイレクトする必要があります。これが機能している間、teeコマンドからのSTDOUT出力はout-content.txtファイルの最後で終了します。それは私には矛盾しているようです。
意志

@datdinhquoc私は何とかそれをしました2>&1 1> >(dest pipe)
Alireza Mohamadi

195

これらの答えの最良のものを組み合わせる場合:

command 2> >(grep -v something 1>&2)

...すべての標準出力は標準出力として保存されすべてのstderrがstderrとして保存されますが、文字列「something」を含むstderrの行は表示されません。

これには、stdoutとstderrを元に戻したり破棄したりせず、それらをまとめたり、一時ファイルを使用したりしないという独特の利点があります。


command 2> >(grep -v something)(なしで1>&2)同じではありませんか?
Francesc Rosas

11
いいえ、それがなければ、フィルタリングされたstderrはstdoutにルーティングされます。
Pinko 2013年

1
これが私が必要とするものです-tarはディレクトリに対して常に「ファイルが変更されると変更された」を出力するので、その1行を除外し、他のエラーが発生していないか確認します。だから、tar cfz my.tar.gz mydirectory/ 2> >(grep -v 'changed as we read it' 1>&2)動作するはずです。
2016年

「文字列で始まる」と言うのは間違っています。指定された文字列で始まる行のみを除外する、提示されたgrepの構文については何もありません。行のどこかに特定の文字列が含まれている場合、その行は除外されます。
Mike Nakis、2018

@MikeNakisありがとう-修正済み!(それは...それは意味を成している私の元の答えドラフトから余った)
ピンコ

102

「リダイレクト」と「パイプ」で実際に何が行われているのかを考えると、物事を視覚化する方がはるかに簡単です。bashのリダイレクトとパイプは、プロセスファイル記述子0、1、および2が指す場所を変更する(/ proc / [pid] / fd / *を参照)ことを行います。

ときパイプや「|」演算子がコマンドラインに存在する場合、最初に発生するのは、bashがfifoを作成し、左側のコマンドのFD 1をこのfifoにポイントし、右側のコマンドのFD 0を同じfifoにポイントすることです。

次に、各サイドのリダイレクト演算子が左から右に評価さ、記述子の重複が発生するたびに現在の設定が使用されます。パイプが最初に設定されたため、FD1(左側)とFD0(右側)は通常の状態から既に変更されており、これらの重複はその事実を反映しているため、これは重要です。

したがって、次のように入力すると、

command 2>&1 >/dev/null | grep 'something'

これが順番に起こります:

  1. パイプ(fifo)が作成されます。「コマンドFD1」はこのパイプを指しています。「grep FD0」もこのパイプを指しています
  2. 「コマンドFD2」は、「コマンドFD1」が現在指している場所(パイプ)を指します。
  3. 「コマンドFD1」は/ dev / nullを指しています

したがって、「コマンド」がそのFD 2(stderr)に書き込むすべての出力は、パイプに送られ、反対側の「grep」によって読み取られます。「コマンド」がFD 1(stdout)に書き込むすべての出力は、/ dev / nullに送られます。

代わりに、以下を実行します。

command >/dev/null 2>&1 | grep 'something'

ここで何が起こるかです:

  1. パイプが作成され、「コマンドFD 1」と「grep FD 0 "がそれを指している
  2. 「コマンドFD 1」は/ dev / nullを指しています
  3. 「コマンドFD 2」は、FD 1が現在指している場所を指します(/ dev / null)

したがって、「コマンド」からのすべてのstdoutおよびstderrは/ dev / nullに移動します。パイプには何も入らないため、「grep」は画面に何も表示せずに終了します。

また、リダイレクト(ファイル記述子)は、読み取り専用(<)、書き込み専用(>)、または読み取り/書き込み(<>)にできることに注意してください。

最後のメモ。プログラムがFD1またはFD2に何かを書き込むかどうかは、完全にプログラマ次第です。エラーメッセージはFD 2に送られ、通常の出力はFD 1に送られるべきであるとプログラミングの慣習で規定されていますが、この2つを混ぜたり、規則を無視したずさんなプログラミングを見つけたりすることがよくあります。


6
本当にいい答えです。私の1つの提案は、「fifo」の最初の使用を「fifo(名前付きパイプ)」に置き換えることです。私はしばらくの間Linuxを使用していますが、どういうわけかそれが名前付きパイプの別の用語であることを理解することができませんでした。これは私がそれを調べることから私を救ったでしょうが、それでも私がそれを見つけたときに私が見た他のことを学ばなかったでしょう!
マークエディントン

3
@MarkEdington FIFOは、パイプおよびIPCのコンテキストでの名前付きパイプの別の用語にすぎないことに注意してください。より一般的なコンテキストでは、FIFOは先入れ先出しを意味し、キューデータ構造からの挿入と削除を記述します。
Loomchild 2017年

5
@Loomchildもちろんです。私のコメントの要点は、熟練した開発者でさえ、名前付きパイプの同義語としてFIFOが使用されるのを見たことがないということでした。言い換えれば、私はこれを知りませんでした:en.wikipedia.org/wiki/FIFO_(computing_and_electronics)#Pipes-答えを明確にすることで時間を節約できたでしょう。
マークエディントン2017年

39

Bashを使用している場合は、次を使用します。

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines


4
いいえ、stdoutとstderrを組み合わせ|&2>&1whichに等しいです。この質問では、標準出力なしで出力明示的に要求しました。
Profpatsch 2014

3
'|&'が使用されている場合、command1の標準エラーはパイプを介してcommand2の標準入力に接続されます。2>&1の省略形です|」リンクの4番目の段落から逐語的に取得します。
Profpatsch 2014

9
@Profpatsch:ケンの答えは正しいです。彼がstdoutとstderrを組み合わせる前にstdoutをnullにリダイレクトするように見てください。stdoutは以前/ dev / nullにドロップされていたので、stderrだけをパイプに入れます。
Luciano

3
しかし、私はまだ、あなたの答えが間違っている見つける>/dev/null |&に拡大>/dev/null 2>&1 | し、誰もが(#1〜#2の両方を/ dev / null iノードに接続)例えば(標準出力のinodeに縛られないための手段をstdout iノードがパイプに空であるls -R /tmp/* >/dev/null 2>&1 | grep i空与えるだろうが、ls -R /tmp/* 2>&1 >/dev/null | grep i意志は#をすることができますstdout iノードに関連付けられている2はパイプします)。
フルーツ

3
私がテストしたKen Sharpは、( echo out; echo err >&2 ) >/dev/null |& grep "."何も出力しません( "err"が必要な場合)。|&が使用されてman bashいる場合、…は2>&1 |の省略形です。この標準エラーから標準出力への暗黙的なリダイレクトは、コマンドで指定されたリダイレクトの後に実行されます。したがって、最初にコマンドのFD1をnullにリダイレクトし、次にコマンドのFD2をFD1が指し示した場所にリダイレクトします。nullなので、grepのFD0は入力を取得しません。より詳細な説明については、stackoverflow.com / a / 18342079/69663を参照してください。
2016

11

stdoutとstderrを永続的にファイルにリダイレクトしたい場合は、stderrでgrepを実行しますが、メッセージをttyに書き込むにはstdoutを保持します。

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3

6

これにより、command1 stdoutはそのままに、command1 stderrがcommand2 stdinにリダイレクトされます。

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

LDPから取得


2

名前付きパイプを使用して、あるstdoutコマンドとstderr別のコマンドに送信するためのソリューションを思いついたばかりです。

いきます

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

後で名前付きパイプを削除することをお勧めします。


0

rcシェルを使用できます。

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

これは、標準出力を破棄して標準エラーをgrepにパイプする方法の例ですrc

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

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

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

お気づきかもしれませんが、パイプの後に角かっこを使用して、パイプ処理するファイル記述子を指定できます。

標準ファイル記述子は、次のように列挙されます。

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

-3

私はフォローしてみて、それがうまくいくのを見つけて、

command > /dev/null 2>&1 | grep 'something'

動作しません。stderrを端末に送信するだけです。パイプを無視します。
Tripp Kinetics 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.