Pipe

破折號:將 STDIN 傳送到多個命令,並將它們的輸出按定義的順序輸出到 STDOUT

  • September 17, 2017

起初我認為這個答案是解決方案,但現在我認為我需要一個臨時文件作為緩衝區。

這不可靠:

#!/bin/sh
echo 'OK' |
{
   {
       tee /dev/fd/3 | head --bytes=1 >&4
   } 3>&1 | tail --bytes=+2 >&4
} 4>&1

當我在終端中執行它時,有時我會得到:

好的

有時我會得到:

_

似乎完全隨機。因此,作為一種解決方法,我將輸出寫入文件並在管道完成後將其tail讀回。stdout

#!/bin/sh
echo 'OK' |
{
   {
       tee /dev/fd/3 | head --bytes=1 >&4
   } 3>&1 | tail --bytes=+2 >file
} 4>&1
cat file

這可以在dash沒有臨時文件的情況下完成嗎?Shell 變數作為緩衝區也不是一個選項,因為輸出可能包含 NUL 字節。

如果您想並行執行消費者和生產者,但序列化消費者的輸出,則需要延遲第二個消費者的輸出。為此,您需要以某種方式儲存其輸出,最好的方法是使用臨時文件。

zsh

{cat =(producer > >(consumer1 >&3) | consumer2)} 3>&1

bash有一個問題是它不等待程序替換命令,所以你必須在那裡使用討厭的工作

在這裡,我們使用=(...)程序替換的形式將輸出儲存comsumer2在一個臨時文件中,cat然後再儲存。我們不能為超過 2 個消費者這樣做。為此,我們需要手動創建臨時文件。

不使用=(...)時,我們必須手動清理臨時文件。我們可以通過預先創建和刪除它們來處理它,這樣就不必擔心腳本被殺死的情況。仍然zsh

tmp1=$(mktemp) && tmp2=$(mktemp) || exit
{
 rm -f -- $tmp1 $tmp2
 producer > >(consumer1) > >(consumer2 >&3) > >(consumer3 >&5)
 cat <&4 <&6
} 3> $tmp1 4< $tmp1 5> $tmp2 6< $tmp2

編輯(我最初錯過了需要解決方案的事實dash

對於dash(或任何沒有在 fds 上設置 close-on-exec 標誌並使用管道而不是套接字對的 POSIX shell |),以及/dev/fd/x支持的系統:

tmp1=$(mktemp) && tmp2=$(mktemp) || exit
{
 rm -f -- "$tmp1" "$tmp2"
 {
   {
     {
       producer | tee /dev/fd/4 /dev/fd/6 | consumer1 >&7
     } 4>&1 | consumer2 >&3
   } 6>&1 | consumer3 >&5
 } 7>&1
 cat - /dev/fd/6 <&4
} 3> "$tmp1" 4< "$tmp1" 5> "$tmp2" 6< "$tmp2"

這將適用於 Linux 上的dash, bash, zsh, mksh, busybox sh,但不適用於. 這種方法不能超過 4 個消費者,因為我們僅限於 fds 0 到 9。posh``ksh93

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