web-dev-qa-db-ja.com

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

私はティーの使用経験があまりないので、これはあまり基本的なことではないと思います。

この質問への回答の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つだけです)?

ここでの制御は、teeなしのgrepです。

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

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

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806
13
Rqomey
_$ 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の多重化されたデータは、主にteeの実装に応じて、実際には1つのプロセスに送られます。単純なteeの実装は、ある程度の入力をreadしてから、それを2回writeします。つまり、これらの宛先の1つが最初にデータを取得します。

    ただし、パイプはすべてバッファリングされます。これらのバッファーはそれぞれ1行である可能性が高いですが、バッファーが大きくなる可能性があり、受信コマンドの1つが他のコマンド(grep)がデータを受信する前に、出力に必要なすべて(つまり、headped行)を表示する可能性がありますまったく。

  2. 上記にかかわらず、これらのコマンドの1つがデータを受信して​​も、間に合うようにデータを処理できず、他のコマンドがより多くのデータを受信して​​迅速に処理する可能性もあります。

    たとえば、headgrepが一度に1行ずつデータを送信されたとしても、headがその対処方法を知らない場合(またはカーネルスケジューリングによって遅延される場合)、grepheadの前に結果を表示して、 。実例として、遅延を追加してみてください: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が閉じていることを確認すると、自身のstdin(psからの出力)を閉じて終了します。これは実装に依存する可能性があります。

事実上、psが送信する唯一のデータは最初の行(headがこれを制御しているため)であり、headteeの前にある他のいくつかの行がstdin記述子を閉じます。

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

Stdin/exitを閉じる前にすべての入力を待つコマンドを使用することで、この問題を完全に回避できます。たとえば、awkの代わりにheadを使用します。これにより、すべての行が読み取られて処理されます(出力が発生しない場合でも)。

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

ただし、上記のように、線が順不同で表示される可能性があることに注意してください。

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

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

20
mrb

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

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

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

2

少しハッキリですが、これが私の解決策です。使用するpsgrep()シェル関数の形式で:

psヘッダー行をSTDERRにリダイレクトし、次にgrepSTDOUTにリダイレクトしますが、最初にgrepコマンド自体を削除して、「 grep自体に起因するノイズ」行:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
0
fnl