comunicación entre múltiples procesos


13

Tengo un script bash, que ejecuta la función manager () como un proceso separado para x-times. ¿Cómo es posible reenviar mensajes a todos los procesos manager () desde el script?

He leído sobre canalizaciones anónimas, pero no tengo idea de cómo compartir los mensajes con él. Intenté hacerlo con canalizaciones con nombre, pero parece que tendría que crear una canalización con nombre por separado para cada proceso.

¿Cuál es la forma más elegante de hacer esto?

Aquí está mi código hasta ahora:

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

Respuestas:


25

El término para lo que estás tratando de lograr es multiplexación .

Esto se puede lograr con bastante facilidad en bash, pero requiere algunas características de bash más avanzadas.

Creé un script basado en tu que creo que hace lo que intentas lograr. Lo explicaré a continuación.

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

manageres una función bash que simplemente lee desde STDIN y escribe su identificador y la línea en STDOUT. Usamos en $BASHPIDlugar de $$como $$no se actualiza para subcapas (que es lo que usaremos para iniciar manager.

fdses una matriz que contendrá los descriptores de archivo que apuntan a las tuberías STDIN de los diversos managers generados.
Luego recorremos y creamos 5 procesos de administrador. Utilizo la for (( ))sintaxis en lugar de la forma en que lo hacía, ya que es más limpia. Esto es específico de bash, pero varias de las cosas que hace este script son específicas de bash, por lo que bien podría ir hasta el final.
 

A continuación llegamos a exec {fd}> >(manager $i). Esto hace varias cosas más específicas de bash.
El primero de los cuales es {fd}>. Esto toma el siguiente descriptor de archivo disponible en o después del número 10, abre una tubería con el lado de escritura de la tubería asignado a ese descriptor de archivo y asigna el número de descriptor de archivo a la variable $fd.

Se >(manager $i)inicia manager $iy básicamente se sustituye >(manager $i)con una ruta a un STDIN de ese proceso. Entonces, si managerse lanzó como PID 1234, >(manager $i)podría ser sustituido por /proc/1234/fd/0(esto depende del sistema operativo).

Entonces, suponiendo que el siguiente número de descriptor de archivo disponible sea 10, y el administrador se inicie con PID 1234, el comando exec {fd}> >(manager $i)básicamente se convierte exec 10>/proc/1234/fd/0, y bash ahora tiene un descriptor de archivo que apunta a STDIN de ese administrador.
Luego, como bash pone ese número de descriptor de archivo $fd, agregamos ese descriptor a la matriz fdspara su uso posterior.
 

El resto es bastante simple. El maestro lee una línea de STDIN, itera sobre todos los descriptores de archivo $fdsy envía la línea a ese descriptor de archivo ( printf ... >&$fd).

 

El resultado se ve así:

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

Donde escribí helloy world.


@Patrick the is works, pero tienes un error tipográfico, {fd} y debería ser $ {fd}
c4f4t0r

3
@ c4f4t0r Eso no es un error tipográfico
Patrick

@Patrick en suse 11 "bash type.bash type.bash: línea 10: exec: {fd}: no encontrado" cambié a exec $ {fd} y funciona así
c4f4t0r

2
@ c4f4t0r La versión de bash en open suse 11 es bastante antigua (3.2). Esta característica se implementó en bash 4.0.
Patrick

Gracias por mucha buena información! Un punto crítico: puedo entender por qué diría echo -- "$line"o printf "%s\n" "$line", pero ¿por qué necesitaría usarlo --cuando el siguiente argumento esté codificado (y no comience con -)?
Scott

0

teey bash:

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

Si el número de administradores debe ser configurable o si desea que la salida de diferentes administradores no se mezcle:

export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.