複数のプロセス間の通信


13

私はmanager()関数をx回の個別のプロセスとして実行するbashスクリプトを持っています。スクリプト内からすべてのmanager()プロセスにメッセージを転送するにはどうすればよいですか?

匿名パイプについて読んだことがありますが、メッセージを共有する方法がわかりません。名前付きパイプでそれをやろうとしましたが、プロセスごとに個別の名前付きパイプを作成する必要があるようです。

これを行う最もエレガントな方法は何ですか?

これまでの私のコードは次のとおりです。

#!/bin/bash

manager () {
    while :
    do
        echo "read what has been passed to \$line"
    done
}

x=1
while [ $x -le 5 ]
do
  manager x &
  x=$(( $x + 1 ))
done


while :
do
while read line
do
  echo "What has been passed through the pipe is ${line}"
  # should pass $line to every manager process

done < $1
done

exit 0

回答:


25

あなたが達成しようとしていることの用語は多重化です。

これはbashでかなり簡単に実現できますが、より高度なbash機能が必要です。

私はあなたが達成しようとしていることを行うと思うあなたに基づいてスクリプトを作成しました。以下で説明します。

#!/bin/bash
manager() {
  while IFS= read -r line; do
    echo "manager[$1:$BASHPID]: $line"
  done
}

fds=()
for (( i=0; i<5; i++ )); do
  exec {fd}> >(manager $i)
  fds+=( $fd )
done

while IFS= read -r line; do
  echo "master: $line"
  for fd in "${fds[@]}"; do
    printf -- '%s\n' "$line" >&$fd
  done
done

managerは、STDINから単に読み取り、その識別子と行をSTDOUTに書き込むbash関数です。私たちは、使用$BASHPIDの代わり$$として$$、我々が起動するように使用されるものである(サブシェルのために更新されませんmanager

fdsは、manager生成されたさまざまなのSTDINパイプを指すファイル記述子を保持する配列です。
次に、ループして5つのマネージャープロセスを作成します。私はfor (( ))あなたがそれがきれいだったので、あなたがそれをしていた方法の代わりに構文を使用します。これはbash固有ですが、このスクリプトが行うことのいくつかはbash固有であるため、最後まで進む可能性があります。
 

次にに行きexec {fd}> >(manager $i)ます。これにより、bash固有の処理がいくつか行われます。
最初のものは{fd}>です。これにより、番号10以降で次に利用可能なファイル記述子が取得され、そのファイル記述子に割り当てられたパイプの書き込み側でパイプが開かれ、ファイル記述子番号が変数に割り当てられます$fd

>(manager $i)起動manager $iと基本的に代替>(manager $i)そのプロセスのSTDINへのパスを持ちます。したがって、managerPID 1234として起動された場合>(manager $i)/proc/1234/fd/0(これはOSに依存します)に置き換えられる可能性があります。

したがって、次に使用可能なファイル記述子番号が10で、マネージャーがPID 1234で起動されると仮定するexec {fd}> >(manager $i)exec 10>/proc/1234/fd/0、コマンドは基本的にになり、bashはそのマネージャーのSTDINを指すファイル記述子を持ちます。
次に、bashはそのファイル記述子番号をに入れるため、後で使用するために$fdその記述子を配列に追加しfdsます。
 

残りは非常に簡単です。マスターはSTDINから行を読み取り、内のすべてのファイル記述子を反復処理して、$fdsその行をそのファイル記述子(printf ... >&$fd)に送信します。

 

結果は次のようになります。

$ /tmp/test.sh
hello
master: hello
manager[0:8876]: hello
manager[1:8877]: hello
manager[4:8880]: hello
manager[2:8878]: hello
manager[3:8879]: hello
world
master: world
manager[0:8876]: world
manager[1:8877]: world
manager[3:8879]: world
manager[2:8878]: world
manager[4:8880]: world

私が入力helloした場所とworld


@Patrickは動作しますが、タイプミス{fd}があり、$ {fd}になります
c4f4t0r

3
@ c4f4t0rそれはタイプミスではありません
パトリック

SUSE 11の@Patrick私は$ {FD}をexecしするように変更し、それがそのように動作し、 "bashのtype.bash type.bash:行10:幹部:{fdが}が見つかりません"
c4f4t0r

2
@ c4f4t0r Open Suse 11のbashのバージョンはかなり古い(3.2)です。この機能はbash 4.0で実装されました。
パトリック

たくさんの良い情報をありがとう!ちょっとした質問:どうして言うのecho -- "$line"か、またはprintf "%s\n" "$line"-を理解できますが--、次の引数がハードコードされている(そしてで始まらない-)場合に使用する必要があるのはなぜですか?
スコット14

0

teeおよびbash

cat foo | tee >(manager) >(manager) >(manager) >(manager) >(manager) >/dev/null

マネージャーの数を構成可能にする場合、または異なるマネージャーからの出力を混在させたくない場合:

export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.