Shell

為什麼“粘貼”不能在標準錯誤旁邊列印標準輸入?

  • July 5, 2019

通常在相鄰列中paste列印兩個命名(或等效)文件,如下所示:

paste <(printf '%s\n' a b) <(seq 2)

輸出:

a   1
b   2

但是當這兩個文件是/dev/stdinand/dev/stderr時,它的工作方式似乎不同。

假設我們有b缺少b程序,它在標準輸出上輸出兩行,在標準錯誤上輸出兩行。為了說明的目的,這可以用一個函式來模擬:

bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }

現在執行annotate-output,(在Debian/Ubuntu/etc.上的**devscripts包中),以顯示它可以工作:

annotate-output bash -c 'bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }; bb'
22:06:17 I: Started bash -c bb() { seq 2 | tee >(sed s/^/e/ > /dev/stderr) ; }; bb
22:06:17 O: 1
22:06:17 E: e1
22:06:17 O: 2
22:06:17 E: e2
22:06:17 I: Finished with exitcode 0

所以它有效。餵給:bb_paste

bb | paste /dev/stdin /dev/stderr

輸出:

1   e1
e2
^C

它掛起 -^C意味著按Control-C退出。

更改|為 a;也不起作用:

bb ; paste /dev/stdin /dev/stderr

輸出:

1
2
e1
e2
^C

也掛起 -^C表示按Control-C退出。

期望的輸出:

1    e1
2    e2

可以使用paste嗎?如果不是,為什麼不呢?

為什麼不能使用 /dev/stderr 作為管道

問題不paste在於 ,也不在於/dev/stdin。它與/dev/stderr.

所有命令都使用一個打開的輸入描述符(0:標準輸入)和兩個輸出(1:標準輸出和 2:標準錯誤)創建。這些通常可以分別使用 names和訪問/dev/stdin,但請參閱/dev/stdin、/dev/stdout 和 /dev/stderr 的可移植性如何?. 許多命令,包括,也會將文件名解釋為 STDIN。/dev/stdout``/dev/stderr``paste``-

當您單獨執行bb時,STDOUT 和 STDERR 都是控制台,通常會出現命令輸出。這些行通過不同的描述符(如您的 所示annotate-output)但最終在同一個地方結束。

當您添加一個|和第二個命令時,創建一個管道……

bb | paste /dev/stdin /dev/stderr

|告訴 shell 將 的輸出連接到bb的輸入pastepaste首先嘗試從 讀取/dev/stdin,它(通過一些符號連結)解析為它自己的標準輸入描述符(shell 剛剛連接),因此該行1通過。

但是外殼/管道對 STDERR 沒有任何作用。 bb仍然將(e1 e2等)發送到控制台。同時,paste嘗試從掛起的同一個控制台讀取(直到您輸入某些內容)。

您的連結為什麼我不能使用文本編輯器閱讀 /dev/stdout?在這裡仍然相關,因為同樣的限制適用於/dev/stderr.

如何製作第二個管道

您有一個同時產生標準輸出和標準錯誤的命令,並且您希望paste這兩行彼此相鄰。這意味著兩個並發管道,每列一個。shell 管道... | ...提供了其中之一,您需要自己創建第二個,並將 STDERR 重定向到使用2>filename.

mkfifo RHS
bb 2>RHS | paste /dev/stdin RHS

如果這是在腳本中使用,您可能更願意在臨時目錄中創建該 FIFO,並在使用後將其刪除。

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