Shell

管道,數據如何在管道中流動?

  • February 2, 2015

我不明白數據如何在管道中流動,希望有人能澄清那裡發生了什麼。

我認為命令管道以逐行方式處理文件(文本、字元串數組)。(如果每個命令本身逐行執行。)每一行文本都通過管道,命令不會等待前一個完成整個輸入的處理。

但似乎並非如此。

這是一個測試範例。有幾行文字。我將它們大寫並重複每行兩次。我這樣做了cat text | tr '[:lower:]' '[:upper:]' | sed 'p'

為了遵循這個過程,我們可以“互動地”執行它——跳過cat. 管道的每個部分逐行執行:

$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2

但是完整的管道確實等待我完成輸入,EOF然後才列印結果:

$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D

應該是這樣嗎?為什麼不是逐行顯示?

stdio大多數 unix 程序使用的 C 標準 I/O 庫 ( ) 遵循一般緩衝規則。如果輸出到終端,則在每行末尾刷新;否則,僅當緩衝區(我的 Linux/amd64 系統上為 8K;在您的系統上可能不同)已滿時才會刷新它。

如果您的所有實用程序都遵循一般規則,您將在所有範例中看到輸出延遲(cat|sedcat|trcat|tr|sed)。但是有一個例外:GNUcat從不緩衝它的輸出。它要麼不使用,要麼stdio更改了預設stdio緩衝策略。

我可以相當肯定你使用的是 GNUcat而不是其他的 unix cat,因為其他的 unix 不會這樣。傳統的 unixcat有一個-u請求無緩衝輸出的選項。GNUcat忽略該-u選項,因為它的輸出總是無緩衝的。

所以只要你有一個cat左邊有a的管道,在GNU系統中,通過管道的數據傳輸不會被延遲。cat甚至沒有逐行進行-您的終端正在這樣做。當您為 cat 輸入輸入時,您的終端處於“規範”模式 - 基於行,使用退格鍵和 ctrl-U 等編輯鍵讓您有機會在發送之前編輯您輸入的行Enter

cat|tr|sed範例中,只要您按下,tr它仍然會從那裡接收數據,但遵循預設策略:它的輸出將發送到管道,因此它不會在每行之後刷新。當緩衝區已滿或收到 EOF 時,它會寫入第二個管道,以先到者為準。cat``Enter``tr``stdio

sed也遵循stdio預設策略,但它的輸出將發送到終端,因此它會在完成後立即寫入每一行。這會影響在管道的另一端出現某些內容之前您必須輸入多少 - 如果sed正在對其輸出進行塊緩衝,則您必須輸入兩倍的內容(以填充tr’ 的輸出緩衝區 sed’ 的輸出緩衝)。

GNUsed-u選項,因此如果您顛倒順序並使用cat|sed -u|tr,您會看到輸出立即再次出現。(該選項可能在其他地方可用sed -u,但我認為它不像.cat -u``tr

有一個名為的實用程序stdbuf可以讓您更改使用stdio預設值的任何命令的緩沖模式。它有點脆弱,因為它用於LD_PRELOAD完成 C 庫並非旨在支持的事情,但在這種情況下,它似乎可以工作:

cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'

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