How to make a pipe loop in bash

前端 未结 7 1538
半阙折子戏
半阙折子戏 2020-12-05 03:25

Assume that I have programs P0, P1, ...P(n-1) for some n > 0. How can I easily redirect the output of program Pi

7条回答
  •  萌比男神i
    2020-12-05 03:51

    After spending quite some time yesterday trying to redirect stdout to stdin, I ended up with the following method. It isn't really nice, but I think I prefer it over the named pipe/fifo solution.

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

    The { ... } >/dev/fd/0 is to redirect stdout to stdin for the pipe sequence as a whole (i.e. it redirects the output of P(n-1) to the input of P0). Using >&0 or something similar does not work; this is probably because bash assumes 0 is read-only while it doesn't mind writing to /dev/fd/0.

    The initial read-pipe is necessary because without it both the input and output file descriptor are the same pts device (at least on my system) and the redirect has no effect. (The pts device doesn't work as a pipe; writing to it puts things on your screen.) By making the input of the { ... } a normal pipe, the redirect has the desired effect.

    To illustrate with my calc/square example:

    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
    

    Running the above code gives the following output:

    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
    

    Of course, this method is quite a bit of a hack. Especially the read part has an undesired side-effect: termination of the "real" pipe loop does not lead to termination of the whole. I couldn't think of anything better than read as it seems that you can only determine that the pipe loop has terminated by try to writing write something to it.

提交回复
热议问题