23

私がいくつかのプログラムP0、、P1...を持っていると仮定します。プログラムの出力をすべてのプログラムに簡単にリダイレクトするにはどうすればよいですか()?P(n-1)n > 0PiP(i+1 mod n)i0 <= i < n

たとえばsquare、数値を繰り返し読み取り、その数値の2乗を出力するプログラムと、calc数値を出力した後、その2乗を読み取ることができると期待するプログラムがあるとします。calcこれらのプログラムを接続して、数値を出力するたびにsquare平方が返されるようにするにはどうすればよいcalcですか?

編集:私はおそらく「簡単に」とはどういう意味かを明確にする必要があります。名前付きパイプ/FIFOソリューションは実際に機能するものです(そして私は過去に使用しました)が、bashパイプを使用する場合と比較すると、実際に適切に実行するにはかなりの作業が必要です。(まだ存在しないファイル名を取得し、その名前でパイプを作成し、「パイプループ」を実行し、名前付きパイプをクリーンアップする必要があります。)名前付きパイプを作成できなくなりprog1 | prog2、プログラムを接続するために常に名前付きパイプを使用する必要があると想像してください。

「通常の」パイプを書くのとほぼ同じくらい簡単なものを探しています。たとえば、のようなもの{ prog1 | prog2 } >&0は素晴らしいでしょう。

4

7 に答える 7

27

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パイプループが終了したと判断できるのは、何かを書き込もうとすることでしか判断できないように思われるので、これ以上のことは考えられませんでした。

于 2008-09-04T08:22:01.790 に答える
15

名前付きパイプはそれを行うかもしれません:

$ mkfifo outside
$ <outside calc | square >outside &
$ echo "1" >outside ## Trigger the loop to start
于 2008-09-02T20:57:53.503 に答える
5

これは非常に興味深い質問です。私は(漠然と)17年前の大学で非常によく似た課題を覚えています. コードが各パイプの入出力用のファイルハンドルを取得するパイプの配列を作成する必要がありました。次に、コードは未使用のファイルハンドルを fork して閉じます。

bash の名前付きパイプで同様のことができると思います。mknod または mkfifo を使用して、参照できる一意の名前を持つ一連のパイプを作成し、プログラムをフォークします。

于 2008-09-02T19:16:44.993 に答える
3

私のソリューションは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 を使用する必要があります。

于 2015-03-14T20:30:14.617 に答える
1

名前付きパイプ。

mkfifoを使用して、一連のFIFOを作成します

すなわちfifo0、fifo1

次に、各プロセスを必要なパイプに接続します。

processn <fifo(n-1)> fifon

于 2008-09-02T20:57:24.533 に答える
-1

sh/bashでそれができるとは思えません。MULTIOSとcoproc機能を備えたZSHの方が適しています。

于 2008-09-02T20:31:44.220 に答える