связь между несколькими процессами

13

У меня есть скрипт bash, который запускает функцию manager () как отдельный процесс для x-times. Как можно пересылать сообщения всем процессам manager () из скрипта?

Я читал об анонимных каналах, но понятия не имею, как поделиться с ним сообщениями. Я пытался сделать это с именованными каналами, но, похоже, мне придется создавать отдельный именованный канал для каждого процесса?

Какой самый элегантный способ сделать это?

Вот мой код до сих пор:

#!/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
Альяз
источник

Ответы:

25

Термин для того, что вы пытаетесь достичь, означает мультиплексирование .

Это можно сделать довольно легко в bash, но для этого нужны некоторые более продвинутые функции bash.

Я создал сценарий на основе вашего, который, я думаю, делает то, что вы пытаетесь выполнить. Я объясню это ниже.

#!/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это функция bash, которая просто читает из STDIN и записывает свой идентификатор и строку в STDOUT. Мы используем $BASHPIDвместо $$as $$не обновляется для подоболочек (это то, что мы будем использовать для запуска manager.

fdsявляется массивом, который будет содержать файловые дескрипторы, указывающие на каналы STDIN различных managerпорожденных объектов .
Затем мы проходим цикл и создаем 5 процессов менеджера. Я использую for (( ))синтаксис вместо того, как вы это делали, потому что он чище. Это зависит от bash, но некоторые вещи, которые выполняет этот скрипт, зависят от bash, поэтому вполне могут пройти весь путь.
 

Далее мы доберемся до exec {fd}> >(manager $i). Это делает еще несколько вещей, связанных с bash.
Первый из которых есть {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, и bash теперь имеет файловый дескриптор, указывающий на STDIN этого менеджера.
Затем, поскольку bash вставляет этот номер дескриптора файла $fd, мы добавляем этот дескриптор в массив fdsдля дальнейшего использования.
 

В остальном все довольно просто. Мастер читает строку из STDIN, перебирает все файловые дескрипторы $fdsи отправляет строку в этот дескриптор файла ( 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

Где я напечатал helloи world.

Патрик
источник
@ Патрик работает, но вы получили опечатку, {fd}, и это должно быть $ {fd}
c4f4t0r
3
@ c4f4t0r Это не опечатка
Патрик
@ Патрик на suse 11 "bash type.bash type.bash: строка 10: exec: {fd}: не найдено" я изменил на exec $ {fd}, и он работает так
c4f4t0r
2
@ c4f4t0r Версия bash в open suse 11 довольно древняя (3.2). Эта функция была реализована в Bash 4.0.
Патрик
Спасибо за много полезной информации! Ниппик: Я могу понять, почему вы сказали бы 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}
Оле Танге
источник