communication entre plusieurs processus

13

J'ai un script bash, qui exécute la fonction manager () comme un processus séparé pour x-times. Comment est-il possible de transmettre des messages à tous les processus manager () depuis le script?

J'ai lu des informations sur les canaux anonymes, mais je ne sais pas comment partager les messages avec lui. J'ai essayé de le faire avec des canaux nommés, mais il semble que je devrais créer un canal nommé distinct pour chaque processus?

Quelle est la façon la plus élégante de procéder?

Voici mon code jusqu'à présent:

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

Réponses:

25

Le terme pour ce que vous essayez d'accomplir est le multiplexage .

Cela peut être accompli assez facilement dans bash, mais cela nécessite des fonctionnalités bash plus avancées.

J'ai créé un script basé sur le vôtre qui, je pense, fait ce que vous essayez d'accomplir. Je vais l'expliquer ci-dessous.

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

managerest une fonction bash qui lit simplement depuis STDIN et écrit son identifiant et la ligne dans STDOUT. Nous utilisons $BASHPIDau lieu de $$as qui $$n'est pas mis à jour pour les sous-coquilles (c'est ce que nous allons utiliser pour lancer manager.

fdsest un tableau qui contiendra les descripteurs de fichiers pointant vers les canaux STDIN des différents managers générés.
Ensuite, nous bouclons et créons 5 processus de gestion. J'utilise la for (( ))syntaxe au lieu de la façon dont vous le faisiez car elle est plus propre. Ceci est spécifique à bash, mais plusieurs des choses que ce script fait sont spécifiques à bash, donc aussi bien aller jusqu'au bout.
 

Ensuite, nous y arrivons exec {fd}> >(manager $i). Cela fait plusieurs choses plus spécifiques à bash.
Le premier est {fd}>. Cela saisit le descripteur de fichier suivant disponible sur ou après le numéro 10, ouvre un canal avec le côté écriture du canal affecté à ce descripteur de fichier et attribue le numéro de descripteur de fichier à la variable $fd.

Le >(manager $i)lance manager $iet se substitue essentiellement >(manager $i)avec un chemin vers un STDIN de ce processus. Donc, si a managerété lancé en tant que PID 1234, il >(manager $i)pourrait être remplacé par /proc/1234/fd/0(cela dépend du système d'exploitation).

Donc, en supposant que le prochain numéro de descripteur de fichier disponible est 10 et que le gestionnaire est lancé avec le PID 1234, la commande exec {fd}> >(manager $i)devient essentiellement exec 10>/proc/1234/fd/0et bash a maintenant un descripteur de fichier pointant vers STDIN de ce gestionnaire.
Puis, puisque bash place ce numéro de descripteur de fichier $fd, nous ajoutons ce descripteur au tableau fdspour une utilisation ultérieure.
 

Le reste est assez simple. Le maître lit une ligne à partir de STDIN, parcourt tous les descripteurs de fichiers dans $fdset envoie la ligne à ce fichier desciptor ( printf ... >&$fd).

 

Le résultat ressemble à ceci:

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

Où j'ai tapé helloet world.

Patrick
la source
@Patrick the is works, but you got a typo, {fd} and it should be $ {fd}
c4f4t0r
3
@ c4f4t0r Ce n'est pas une faute de frappe
Patrick
@Patrick on suse 11 "bash type.bash type.bash: ligne 10: exec: {fd}: introuvable" j'ai changé pour exec $ {fd} et ça fonctionne comme ça
c4f4t0r
2
@ c4f4t0r La version de bash dans open suse 11 est assez ancienne (3.2). Cette fonctionnalité a été implémentée dans bash 4.0.
Patrick
Merci pour beaucoup de bonnes informations! Un nitpick: je peux comprendre pourquoi vous diriez echo -- "$line"ou printf "%s\n" "$line"- mais pourquoi devriez-vous utiliser --lorsque le prochain argument est codé en dur (et ne commence pas par -)?
Scott
0

teeet bash:

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

Si le nombre de gestionnaires doit être configurable ou si vous souhaitez que la sortie de différents gestionnaires ne soit pas mélangée:

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