Bash

如何使用 tee 重定向到 grep

  • August 2, 2013

我沒有太多使用 tee 的經驗,所以我希望這不是很基礎。

在查看了這個問題的一個答案後,我遇到了一種奇怪的行為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,還是我以錯誤的方式執行此操作?

當我輸入這個問題時,第二個命令實際上為我工作了一次,我又執行了五次,然後返回到單行結果。這只是我的系統嗎?(我在 tmux 中執行 zsh)。

最後,為什麼第一個命令沒有顯示“grep syslog”作為結果(只有一個結果)?

這裡的控制是沒有 greptee

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 | 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. 一個簡單的tee實現將read輸入一些輸入,然後輸入write兩次:一次到標準輸出,一次到它的參數。這意味著其中一個目的地將首先獲取數據。

但是,管道都是緩衝的。這些緩衝區很可能每個為 1 行,但它們可能更大,這可能導致其中一個接收命令在grep另一個命令(head全部。 2. 儘管如此,也有可能這些命令之一接收到數據但無法及時對其進行任何處理,然後另一個命令接收到更多數據并快速處理它。

例如,即使一次發送head一行grep數據,如果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接收到第一行輸入然後關閉它的標準輸入並退出。當tee看到其標準輸出已關閉時,它會關閉自己的標準輸入(來自 的輸出ps)並退出。這可能取決於實現。

實際上,唯一ps要發送的數據是第一行(當然,因為head正在控制它),可能還有其他一些行,然後head關閉tee它們的標準輸入描述符。

第二行是否出現的不一致是定時引入的:head關閉stdin,但ps仍在發送數據。這兩個事件沒有很好地同步,因此包含的行syslog仍然有機會使其成為tee的參數(grep命令)。這與上面的解釋類似。

您可以通過使用在關閉標準輸入/退出之前等待所有輸入的命令來完全避免這個問題。例如,使用awk代替head,它將讀取並處理其所有行(即使它們不會導致輸出):

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

但請注意,這些行仍然可能出現亂序,如上所示,可以通過以下方式證明:

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

希望這不是太多細節,但是有很多同時發生的事情相互作用。單獨的程序同時執行而沒有任何同步,因此它們在任何特定執行上的操作都可能有所不同;有時深入探勘底層過程來解釋原因會有所幫助。

引用自:https://unix.stackexchange.com/questions/47932