การสื่อสารระหว่างหลายกระบวนการ


13

ฉันมีสคริปต์ทุบตีซึ่งทำงานเป็นผู้จัดการ () ฟังก์ชั่นเป็นกระบวนการที่แยกต่างหากสำหรับ x- ครั้ง เป็นไปได้อย่างไรที่จะส่งต่อข้อความถึงกระบวนการทั้งหมด () จากผู้จัดการภายในสคริปต์?

ฉันได้อ่านเกี่ยวกับไพพ์นิรนามแล้ว แต่ฉันไม่รู้ว่าจะแชร์ข้อความกับมันได้อย่างไรฉันลองทำมันด้วยชื่อไพพ์ แต่ดูเหมือนว่าฉันจะต้องสร้างไพพ์ที่มีชื่อแยกต่างหากสำหรับแต่ละกระบวนการ?

วิธีที่สง่างามที่สุดในการทำเช่นนี้คืออะไร?

นี่คือรหัสของฉัน:

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

คำตอบ:


26

คำศัพท์ที่คุณต้องการทำให้สำเร็จคือการทำมัลติเพล็กซ์

สิ่งนี้สามารถทำได้ค่อนข้างง่ายในการทุบตี แต่มันต้องมีคุณสมบัติทุบตีขั้นสูง

ฉันสร้างสคริปต์ตามที่คุณคิดว่าทำในสิ่งที่คุณพยายามจะทำ ฉันจะอธิบายด้านล่าง

#!/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 เราใช้$BASHPIDแทน$$เป็น$$ไม่ได้รับการปรับปรุงสำหรับ subshells manager(ซึ่งเป็นสิ่งที่เราจะใช้ในการเปิดตัว

fdsคืออาร์เรย์ที่จะเก็บไฟล์ descriptors ที่ชี้ไปยังท่อ STDIN ของmanagers ต่างๆที่เกิดขึ้น
จากนั้นเราวนซ้ำและสร้าง 5 กระบวนการผู้จัดการ ฉันใช้for (( ))ไวยากรณ์แทนวิธีที่คุณทำเพราะสะอาดกว่า นี่คือการทุบตีที่เฉพาะเจาะจง แต่หลายสิ่งที่สคริปต์นี้ไม่เฉพาะทุบตีดังนั้นจึงอาจไปตลอดทาง
 

exec {fd}> >(manager $i)ต่อไปเราจะได้รับการ สิ่งนี้จะทำสิ่งที่เฉพาะเจาะจงมากขึ้นทุบตี ครั้งแรกของซึ่งเป็น
{fd}>คว้านี้อธิบายไฟล์ที่มีอยู่ต่อไปในหรือหลังวันจำนวน 10 $fdเปิดท่อกับทางด้านการเขียนของท่อได้รับมอบหมายให้อธิบายไฟล์นั้นและกำหนดหมายเลขไฟล์อธิบายให้กับตัวแปร

การ>(manager $i)เปิดตัวmanager $iและทดแทนโดยทั่วไป>(manager $i)ด้วยเส้นทางไปยัง STDIN ของกระบวนการนั้น ดังนั้นหากmanagerเปิดตัวเป็น PID 1234 >(manager $i)อาจได้รับการแทนที่ด้วย/proc/1234/fd/0(นี่คือระบบปฏิบัติการที่ขึ้นอยู่กับ)

ดังนั้นสมมติว่าหมายเลขตัวอธิบายไฟล์ที่มีอยู่ถัดไปคือ 10 และตัวจัดการถูกเปิดใช้งานด้วย PID 1234 exec {fd}> >(manager $i)โดยทั่วไปคำสั่งจะกลายเป็นexec 10>/proc/1234/fd/0จริงและตอนนี้ทุบตีจะมีตัวอธิบายไฟล์ชี้ไปที่ STDIN ของตัวจัดการนั้น
จากนั้นเมื่อทุบตีใส่หมายเลขตัวอธิบายไฟล์$fdนั้นเราจึงเพิ่มตัวอธิบายนั้นลงในอาร์เรย์fdsเพื่อใช้ในภายหลัง
 

ที่เหลือก็ค่อนข้างเรียบง่าย ต้นแบบอ่านบรรทัดจาก STDIN ซ้ำทุกตัวอธิบายไฟล์ใน$fdsและส่งบรรทัดไปยังไฟล์ desciptor ( 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

ที่ผมพิมพ์และhelloworld


@Patrick the is works แต่คุณได้รับข้อผิดพลาด {fd} และควรเป็น $ {fd}
c4f4t0r

3
@ c4f4t0r นั่นไม่ใช่การพิมพ์ผิด
Patrick

@ แพทริกบน suse 11 "bash type.bash type.bash: บรรทัด 10: exec: {fd}: ไม่พบ" ฉันเปลี่ยนเป็น exec $ {fd} และมันทำงานอย่างนั้น
c4f4t0r

2
@ c4f4t0r เวอร์ชันของ bash ใน open suse 11 นั้นค่อนข้างเก่า (3.2) คุณลักษณะนี้มีการใช้งานใน bash 4.0
Patrick

ขอบคุณสำหรับข้อมูลที่ดีมากมาย! A nitpick: ฉันสามารถเข้าใจได้ว่าทำไมคุณถึงพูดecho -- "$line"หรือprintf "%s\n" "$line"- แต่ทำไมคุณต้องใช้--เมื่ออาร์กิวเมนต์ต่อไปมีรหัสตายตัว (และไม่ได้ขึ้นต้นด้วย-)
สกอตต์

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.