teeを使用してgrepにリダイレクトする方法


13

Tシャツの使用経験はあまりないので、これがあまり基本的ではないことを願っています。

この質問に対する答えの1つを見た後、私は奇妙な行動に出会いましたtee

最初の行と見つかった行を出力するために、これを使用できます:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

ただし、これを(zshで)初めて実行したとき、結果の順序が間違っていて、列ヘッダーがgrepの結果の下にありました(ただし、これは再び起こりませんでした)。

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

最初の行のみが印刷され、それ以外は何も出力されません!teeを使用してgrepにリダイレクトできますか、それとも間違った方法でこれを行っていますか?

この質問を入力しているときに、2番目のコマンドが実際に1回機能し、5回実行してから1行の結果に戻りました。これは私のシステムですか?(tmux内でzshを実行しています)。

最後に、最初のコマンドで「grep syslog」が結果として表示されないのはなぜですか(結果が1つだけです)。

ここで制御するためのgrepは、 tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

更新: headによってコマンド全体が切り捨てられているようです(以下の回答に示されているように)。現在、以下のコマンドは次を返しています。

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806

あなたの質問への直接の答えではありませんが、ただのようなことをするほうがずっときれいでしょうps aux | sed -n -e '1p' -e '/syslog/p'
jw013

私はsedのことすら考えたことがありません。これは関連する質問に対する適切な答えかもしれませんが、これらのコマンドの一貫性のない動作に関する情報を実際に探しています!
Rqomey

回答:


19
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

grepそしてheadコマンドがほぼ同時に開始し、データが利用可能になるとの両方が、一般的に、自分のレジャーで同じ入力データを受信、しかし。行を反転させる「非同期」出力を導入できることがいくつかあります。例えば:

  1. の多重化データはtee、主にの実装に応じて、実際に1つのプロセスに他のプロセスより先に送信されteeます。単純なtee実装では、readある程度の入力が行われ、その後write2回(1回は標準出力に、1回はその引数に)入力されます。これは、これらの宛先の1つが最初にデータを取得することを意味します。

    ただし、パイプはすべてバッファリングされます。これらのバッファーはそれぞれ1行である可能性がありますが、より大きくなる可能性があり、受信コマンドの1つが出力に必要なすべて(つまり、grepped行)を確認してから、他のコマンド(head)がすべて。

  2. 上記にも関わらず、これらのコマンドの1つがデータを受信しますが、時間内に何もできないので、他のコマンドはより多くのデータを受信し、それを迅速に処理することも可能です。

    例えば、たとえhead及びgrep時のデータ1行に送信され、場合head、それに対処する方法を知らない(またはカーネルのスケジューリングにより遅れる)、grep前にその結果を表示することができheadさえする機会を得ます。実証するために、遅延を追加してみてください。ps aux | tee >(sleep 1; head -n1) | grep syslogこれは、ほぼ確実にgrep最初に出力を出力します。

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

head入力の最初の行を受信し、そのstdinを閉じて終了するため、ここでは1行しか取得しないことが多いと思います。ときにteeそのstdoutが閉じられたことを見て、それはそれ自身の標準入力(出力閉じてps)して終了します。これは実装に依存する可能性があります。

事実上、ps送信するデータは最初の行(headこれを制御しているためです)と、おそらくstdin記述子の前にあるheadtee閉じている他の行だけです。

2行目が表示されるかどうかの不一致は、タイミングによって発生しheadます。stdinを閉じますが、psデータを送信しています。これらの2つのイベントは十分に同期されていないため、を含む行にはsyslogまだteeの引数(grepコマンド)になる可能性があります。これは上記の説明に似ています。

stdin / exitingを閉じる前にすべての入力を待機するコマンドを使用することにより、この問題を完全に回避できます。たとえば、awkではなく、headすべての行を読み取って処理します(出力が発生しない場合でも):

ps aux | tee >(grep syslog) | awk 'NR == 1'

ただし、上記のように、行が順番どおりに表示されない場合があることに注意してください。

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

これがあまり詳細ではないことを願っていますが、相互にやり取りしている多くの同時的なものがあります。個別のプロセスは同期せずに同時に実行されるため、特定の実行に対するアクションは異なる場合があります。基礎となるプロセスを深く掘り下げて理由を説明すると役立つ場合があります。


1
優れた回答!基本的なプロセスに興味があるので、実際に尋ねました。物事が一定でないとき、私はそれを面白いと思います。stdoutを閉じるのps aux | tee >(grep syslog) | head -n1を停止するより良い実行方法がありますかhead。うわー、このコマンドは、出力を与えることを始めましたが、あなたの答えに沿って起こるように、切り捨てられているようですUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey

1
の代わりにstdinを閉じないものを使用できますhead。私はこの例で答えを更新しました:ps aux | tee >(grep syslog) | awk 'NR == 1'
MRB

1
@KrzysztofAdamski、を使用する>(cmd)と、シェルは名前付きパイプを作成し、それを引数としてコマンドに渡します(tee)。次にtee、stdout(にパイプされますawk)およびその引数にも書き込みます。これは、mkfifo a_fifo ; grep ... a_fifoあるシェルおよびps | tee a_fifo | awk ...別のシェルと同じです。
mrb

1
@ KrzysztofAdamskignu.org / software / bash/ manual / html_node/ — Try echo >(exit 0)。これはシェルから渡された実際の引数をエコーし​​ます(私の場合はになります/dev/fd/63)。これは、bashとzshで同じように機能するはずです。
Mrb

1
@mrb:それは私が前に知らなかった非常に興味深い機能です、ありがとう。bashでは奇妙な方法で動作していますが、pastebin.com / xFgRcJdFを参照してください。残念ながら、今これを調査する時間はありませんが、明日は調査します。
レジストフアダムスキー

2

grep syslogタイミングに依存するため、常に表示されるとは限りません。シェルパイプラインを使用する場合、コマンドをほぼ同時に実行しています。しかし、ここで重要なことは「ほぼ」という言葉です。psgrepが起動する前にすべてのプロセスのスキャンが終了した場合、リストには表示されません。システムの負荷などに応じてランダムな結果を得ることができます。

Tシャツでも同様のことが起こります。サブシェルのバックグラウンドで実行され、grepの前または後に起動される場合があります。これが、出力順序に一貫性がない理由です。

Tシャツの質問に関しては、その動作は非常に奇妙です。これは、通常の方法では使用されないためです。引数なしで実行されます。つまり、データを標準入力から標準出力にコピーするだけです。ただし、stdoutはサブシェル実行ヘッド(最初の場合)またはgrep(2番目の場合)にリダイレクトされます。ただし、次のコマンドにもパイプされます。この場合に起こることは、実際には実装に依存していると思います。たとえば、私のbash 4.2.28では、サブシェルstdinには何も書き込まれません。zshでは、あなたが望む方法(psの最初の行と検索された行の両方を印刷する)で信頼できる動作をします。


とにかく一つのことを説明しますが、Tシャツがgrepの実行を顕著に遅らせることに驚いています!
Rqomey

0

少しハックですが、ここにpsgrep()私が使用するシェル関数の形での私の解決策があります:

psヘッダー行をSTDERR、次にgrepon STDOUTにリダイレクトしますが、最初にgrepコマンド自体を削除して、それ自体から生じる「ノイズ」行を回避しgrepます。

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.