komunikasi antara berbagai proses

13

Saya memiliki skrip bash, yang menjalankan fungsi manager () sebagai proses terpisah untuk x-times. Bagaimana mungkin meneruskan pesan ke semua proses manajer () dari dalam skrip?

Saya sudah membaca tentang pipa anonim tetapi saya tidak tahu bagaimana cara berbagi pesan dengannya .. Saya mencoba melakukannya dengan pipa bernama, tetapi tampaknya saya harus membuat pipa bernama terpisah untuk setiap proses?

Apa cara paling elegan untuk melakukan ini?

Inilah kode saya sejauh ini:

#!/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
Aljaz
sumber

Jawaban:

25

Istilah untuk apa yang ingin Anda capai adalah multiplexing .

Ini dapat dilakukan dengan cukup mudah di bash, tetapi membutuhkan beberapa fitur bash yang lebih canggih.

Saya membuat skrip berdasarkan pada Anda yang menurut saya melakukan apa yang ingin Anda capai. Saya akan jelaskan di bawah ini.

#!/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

manageradalah fungsi bash yang hanya membaca dari STDIN dan menulis pengenalnya dan baris ke STDOUT. Kami menggunakan $BASHPIDalih-alih $$karena $$tidak mendapatkan pembaruan untuk subkulit (yang akan kami gunakan untuk memulai manager.

fdsadalah sebuah array yang akan menahan deskriptor file yang menunjuk ke pipa STDIN dari berbagai managers yang dimunculkan.
Lalu kami mengulang dan membuat 5 proses manajer. Saya menggunakan for (( ))sintaks daripada cara Anda melakukannya karena lebih bersih. Ini khusus untuk bash, tetapi beberapa hal yang dilakukan skrip ini adalah khusus untuk bash, jadi sebaiknya lakukan semuanya.
 

Selanjutnya kita sampai exec {fd}> >(manager $i). Ini melakukan beberapa hal spesifik bash lebih.
Yang pertama adalah {fd}>. Ini mengambil deskriptor file yang tersedia berikutnya pada atau setelah nomor 10, membuka pipa dengan sisi penulisan pipa yang ditugaskan untuk deskriptor file tersebut, dan memberikan nomor deskriptor file ke variabel $fd.

The >(manager $i)peluncuran manager $idan pada dasarnya pengganti >(manager $i)dengan jalan ke STDIN dari proses itu. Jadi jika managerdiluncurkan sebagai PID 1234, >(manager $i)bisa diganti dengan /proc/1234/fd/0(ini tergantung OS).

Jadi dengan asumsi nomor deskriptor file yang tersedia berikutnya adalah 10, dan manajer diluncurkan dengan PID 1234, perintah exec {fd}> >(manager $i)pada dasarnya menjadi exec 10>/proc/1234/fd/0, dan bash sekarang memiliki deskriptor file yang menunjuk ke STDIN dari manajer itu.
Kemudian karena bash memasukkan nomor deskriptor file ke dalam $fd, kami menambahkan deskriptor itu ke array fdsuntuk digunakan nanti.
 

Sisanya cukup sederhana. Master membaca baris dari STDIN, mengulangi semua deskriptor file di $fds, dan mengirimkan baris ke file desciptor ( printf ... >&$fd).

 

Hasilnya terlihat seperti ini:

$ /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

Di mana saya mengetik hellodan world.

Patrick
sumber
@Patrick the is works, tetapi Anda mendapatkan salah ketik, {fd} dan seharusnya $ {fd}
c4f4t0r
3
@ c4f4t0r Itu bukan kesalahan ketik
Patrick
@Patrick on suse 11 "bash type.bash type.bash: baris 10: exec: {fd}: tidak ditemukan" saya berubah menjadi exec $ {fd} dan berfungsi seperti itu
c4f4t0r
2
@ c4f4t0r Versi bash di open suse 11 cukup kuno (3.2). Fitur ini diimplementasikan di bash 4.0.
Patrick
Terima kasih atas banyak informasi bagus! A nitpick: Saya bisa mengerti mengapa Anda akan mengatakan echo -- "$line"atau printf "%s\n" "$line"- tetapi mengapa Anda perlu menggunakan --ketika argumen berikutnya adalah hard-coded (dan tidak dimulai dengan -)?
Scott
0

teedan bash:

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

Jika jumlah manajer dapat dikonfigurasi atau jika Anda ingin output dari manajer yang berbeda tidak tercampur:

export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}
Ole Tange
sumber