comunicação entre vários processos

12

Eu tenho um script bash, que executa a função manager () como um processo separado para x-times. Como é possível encaminhar mensagens para todos os processos manager () dentro do script?

Eu li sobre pipes anônimos, mas não tenho idéia de como compartilhar as mensagens com ele .. Eu tentei fazer isso com pipes nomeados, mas parece que eu teria que criar um pipe nomeado separado para cada processo?

Qual é a maneira mais elegante de fazer isso?

Aqui está meu código até agora:

#!/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
    
por Aljaz 26.05.2014 / 23:29

2 respostas

24

O termo para o que você está tentando realizar é multiplexing .

Isso pode ser feito com bastante facilidade no bash, mas requer alguns recursos bash mais avançados.

Eu criei um script baseado no seu que, na minha opinião, faz o que você está tentando realizar. Eu vou explicar isso abaixo.

#!/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 é uma função bash que simplesmente lê a partir de STDIN e grava seu identificador e a linha para STDOUT. Usamos $BASHPID em vez de $$ , pois $$ não é atualizado para subshells (que é o que estaremos usando para lançar manager .

fds é uma matriz que conterá os descritores de arquivo que apontam para os canais STDIN dos vários manager s gerados.
Então, percorremos e criamos 5 processos gerenciais. Eu uso a sintaxe for (( )) em vez da maneira como você estava fazendo isso como é mais limpo. Isto é bash específico, mas várias das coisas que este script faz são específicas, assim como pode ir até o fim.

Em seguida, chegamos a exec {fd}> >(manager $i) . Isso faz várias outras coisas específicas bash.
A primeira delas é {fd}> . Isso pega o próximo descritor de arquivo disponível em ou após o número 10, abre um canal com o lado de escrita do canal atribuído a esse descritor de arquivo e atribui o número do descritor de arquivo à variável $fd .

O >(manager $i) lança manager $i e basicamente substitui >(manager $i) por um caminho para um STDIN desse processo. Portanto, se manager foi lançado como PID 1234, >(manager $i) pode ser substituído por /proc/1234/fd/0 (isso depende do sistema operacional).

Portanto, supondo que o próximo número de descritor de arquivo disponível seja 10 e o gerenciador seja iniciado com PID 1234, o comando exec {fd}> >(manager $i) basicamente se tornará exec 10>/proc/1234/fd/0 e bash agora tem descritor de arquivo apontando para STDIN desse gerenciador.
Então, como o bash coloca esse número do descritor de arquivo em $fd , adicionamos esse descritor à matriz fds para uso posterior.

O resto é bem simples. O mestre lê uma linha de STDIN, repete todos os descritores de arquivo em $fds e envia a linha para esse desciptor de arquivo ( printf ... >&$fd ).

O resultado é assim:

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

Onde eu digitei hello e world .

    
por 27.05.2014 / 00:30
0

tee e bash :

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

Se o número de gerentes deve ser configurável ou se você quiser que a saída de diferentes gerentes não seja misturada:

export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}
    
por 14.04.2017 / 12:13