簡體   English   中英

如何在bash中創建一個管道循環

[英]How to make a pipe loop in bash

假設對於某些n > 0 ,我有程序P0P1 ,... P(n-1) 如何輕松地將程序Pi的輸出重定向到所有i程序P(i+1 mod n)0 <= i < n )?

例如,假設我有一個程序square ,它重復讀取一個數字而不是打印該數字的正方形,以及一個程序calc ,它有時打印一個數字,之后它希望能夠讀取它的平方。 我如何連接這些程序,以便每當calc打印一個數字, square正方形,它返回到calc

編輯:我應該用“輕松”來澄清我的意思。 命名管道/ fifo解決方案確實有效(我過去曾使用過),但如果將它與使用bash管道進行比較,它實際上需要相當多的工作才能正常工作。 (您需要獲取一個尚未存在的文件名,使用該名稱創建一個管道,運行“管道循環”,清理命名管道。)想象一下,您無法再編寫prog1 | prog2 prog1 | prog2並且總是必須使用命名管道來連接程序。

我正在尋找像寫“普通”管道一樣簡單的東西。 例如{ prog1 | prog2 } >&0 { prog1 | prog2 } >&0會很棒。

在昨天花了很長時間試圖將stdout重定向到stdin ,我最終得到了以下方法。 它不是很好,但我認為我比命名管道/ fifo解決方案更喜歡它。

read | { P0 | ... | P(n-1); } >/dev/fd/0

{ ... } >/dev/fd/0是將stdout重定向到stdin作為整個管道序列(即它將P(n-1)的輸出重定向到P0的輸入)。 使用>&0或類似的東西不起作用; 這可能是因為bash假定0是只讀的,而它不介意寫入/dev/fd/0

初始read管道是必要的,因為如果沒有它,輸入和輸出文件描述符都是相同的pts設備(至少在我的系統上)並且重定向沒有效果。 (pts設備不能用作管道;寫入它會將內容放在屏幕上。)通過輸入{ ... }正常管道,重定向具有所需的效果。

用我的calc / square示例來說明:

function calc() {
  # calculate sum of squares of numbers 0,..,10

  sum=0
  for ((i=0; i<10; i++)); do
    echo $i                   # "request" the square of i

    read ii                   # read the square of i
    echo "got $ii" >&2          # debug message

    let sum=$sum+$ii
  done

  echo "sum $sum" >&2           # output result to stderr
}

function square() {
  # square numbers

  read j                         # receive first "request"
  while [ "$j" != "" ]; do
    let jj=$j*$j
    echo "square($j) = $jj" >&2  # debug message

    echo $jj                     # send square

    read j                       # receive next "request"
  done
}

read | { calc | square; } >/dev/fd/0

運行上面的代碼給出以下輸出:

square(0) = 0
got 0
square(1) = 1
got 1
square(2) = 4
got 4
square(3) = 9
got 9
square(4) = 16
got 16
square(5) = 25
got 25
square(6) = 36
got 36
square(7) = 49
got 49
square(8) = 64
got 64
square(9) = 81
got 81
sum 285

當然,這種方法相當有點黑客。 尤其是read部件具有不希望的副作用:“實際”管道環路的終止不會導致整體的終止。 我想不出比read更好的東西,因為看起來你只能通過嘗試寫一些東西來確定管道循環已經終止。

命名管道可能會這樣做:

$ mkfifo outside
$ <outside calc | square >outside &
$ echo "1" >outside ## Trigger the loop to start

這是一個非常有趣的問題。 我(含糊地)記得17年前大學里的任務非常相似。 我們必須創建一個管道數組,我們的代碼將獲取每個管道的輸入/輸出的文件句柄。 然后代碼將分叉並關閉未使用的文件句柄。

我想你可以用bash中的命名管道做類似的事情。 使用mknod或mkfifo創建一組具有唯一名稱的管道,您可以引用它們然后分叉您的程序。

我的解決方案使用pipexec (大多數函數實現來自你的答案):

square.sh

function square() {
  # square numbers

  read j                         # receive first "request"
  while [ "$j" != "" ]; do
    let jj=$j*$j
    echo "square($j) = $jj" >&2  # debug message

    echo $jj                     # send square

    read j                       # receive next "request"
  done
}

square $@

calc.sh

function calc() {
  # calculate sum of squares of numbers 0,..,10

  sum=0
  for ((i=0; i<10; i++)); do
    echo $i                   # "request" the square of i

    read ii                   # read the square of i
    echo "got $ii" >&2          # debug message

    let sum=$sum+$ii
 done

 echo "sum $sum" >&2           # output result to stderr
}

calc $@

命令

pipexec [ CALC /bin/bash calc.sh ] [ SQUARE /bin/bash square.sh ] \
    "{CALC:1>SQUARE:0}" "{SQUARE:1>CALC:0}"

輸出(與答案相同)

square(0) = 0
got 0
square(1) = 1
got 1
square(2) = 4
got 4
square(3) = 9
got 9
square(4) = 16
got 16
square(5) = 25
got 25
square(6) = 36
got 36
square(7) = 49
got 49
square(8) = 64
got 64
square(9) = 81
got 81
sum 285

注釋:pipexec旨在啟動進程並在其間構建任意管道。 由於bash函數不能作為進程處理,因此需要將函數放在單獨的文件中並使用單獨的bash。

命名管道。

使用mkfifo創建一系列的fifos

即fifo0,fifo1

然后將每個流程附加到您想要的管道:

processn <fifo(n-1)> fifon

我懷疑sh / bash可以做到。 憑借其MULTIOS和coproc功能,ZSH將是更好的選擇。

命令堆棧可以由任意命令數組組成一個字符串,並使用eval進行評估。 以下示例給出結果65536。

function square ()
{
  read n
  echo $((n*n))
}    # ----------  end of function square  ----------

declare -a  commands=( 'echo 4' 'square' 'square' 'square' )

#-------------------------------------------------------------------------------
#   build the command stack using pipes
#-------------------------------------------------------------------------------
declare     stack=${commands[0]}

for (( COUNTER=1; COUNTER<${#commands[@]}; COUNTER++ )); do
  stack="${stack} | ${commands[${COUNTER}]}"
done

#-------------------------------------------------------------------------------
#   run the command stack
#-------------------------------------------------------------------------------
eval "$stack" 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM