Kommunikation zwischen mehreren Prozessen

13

Ich habe ein Bash-Skript, das manager () als separaten Prozess für x-times ausführt. Wie ist es möglich, Nachrichten aus dem Skript an alle manager () -Prozesse weiterzuleiten?

Ich habe von anonymen Pipes gelesen, aber ich habe keine Ahnung, wie ich die Nachrichten damit teilen soll. Ich habe versucht, dies mit Named Pipes zu tun, aber es scheint, dass ich für jeden Prozess eine separate Named Pipes erstellen müsste.

Was ist der eleganteste Weg, dies zu tun?

Hier ist mein Code:

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

Antworten:

25

Der Begriff für das, was Sie erreichen möchten, lautet Multiplexing .

Dies kann mit bash relativ einfach erreicht werden, erfordert jedoch einige erweiterte bash-Funktionen.

Ich habe ein Skript erstellt, das auf Ihrem basiert und das das tut, was Sie erreichen wollen. Ich werde es unten erklären.

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

managerist eine Bash-Funktion, die einfach aus STDIN liest und ihren Bezeichner und die Zeile in STDOUT schreibt. Wir verwenden $BASHPIDanstelle von $$as, $$das für Subshells nicht aktualisiert wird (was wir zum Starten verwenden werden) manager.

fdsist ein Array, das die Dateideskriptoren enthält, die auf die STDIN-Pipes der verschiedenen ausgelösten managers verweisen .
Dann durchlaufen wir 5 Managerprozesse und erstellen diese. Ich verwende die for (( ))Syntax anstelle der Art und Weise, wie Sie sie ausgeführt haben, da sie sauberer ist. Dies ist bash-spezifisch, aber einige der Dinge, die dieses Skript ausführt, sind bash-spezifisch.
 

Als nächstes kommen wir zu exec {fd}> >(manager $i). Dies führt einige weitere bash-spezifische Dinge aus.
Das erste davon ist {fd}>. Dies ermittelt den nächsten verfügbaren Dateideskriptor ab Nummer 10, öffnet eine Pipe mit der Schreibseite der Pipe, die diesem Dateideskriptor zugewiesen ist, und weist der Variablen die Dateideskriptornummer zu $fd.

Das >(manager $i)startet manager $iund ersetzt im Grunde >(manager $i)mit einem Pfad zu einem STDIN dieses Prozesses. Wenn also managerals PID 1234 gestartet wurde, wird dies >(manager $i)möglicherweise durch ersetzt /proc/1234/fd/0(dies ist vom Betriebssystem abhängig).

Angenommen, die nächste verfügbare Dateideskriptornummer ist 10, und der Manager wird mit der PID 1234 gestartet, der Befehl wird im exec {fd}> >(manager $i)Grunde genommen zu exec 10>/proc/1234/fd/0und in der Bash wird nun der Dateideskriptor auf die STDIN dieses Managers gezeigt.
Da bash diese Dateideskriptornummer eingibt $fd, fügen wir diesen Deskriptor zur fdsspäteren Verwendung zum Array hinzu .
 

Der Rest ist ziemlich einfach. Der Master liest eine Zeile aus STDIN, durchläuft alle Dateideskriptoren in $fdsund sendet die Zeile an diesen Dateideskriptor ( printf ... >&$fd).

 

Das Ergebnis sieht so aus:

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

Wo ich getippt habe hellound world.

Patrick
quelle
@Patrick the is works, aber du hast einen Tippfehler, {fd} und es sollte $ {fd} sein
c4f4t0r
3
@ c4f4t0r Das ist kein Tippfehler
Patrick
@Patrick on suse 11 "bash type.bash type.bash: zeile 10: exec: {fd}: not found" Ich habe zu exec $ {fd} gewechselt und es funktioniert so
c4f4t0r
2
@ c4f4t0r Die Version von bash in open suse 11 ist ziemlich alt (3.2). Diese Funktion wurde in Bash 4.0 implementiert.
Patrick
Vielen Dank für viele gute Informationen! Ein Nitpick: Ich kann verstehen, warum Sie sagen würden echo -- "$line"oder printf "%s\n" "$line"- aber warum Sie verwenden müssten, --wenn das nächste Argument fest codiert ist (und nicht mit beginnt -)?
Scott
0

teeund bash:

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

Wenn die Anzahl der Manager konfigurierbar sein soll oder wenn die Ausgabe von verschiedenen Managern nicht gemischt werden soll:

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