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


13

У мене є скрипт bash, який запускає функцію manager () як окремий процес для x-разів. Як можна пересилати повідомлення всім менеджерам () процесам із сценарію?

Я читав про анонімні труби, але поняття не маю, як поділитися повідомленнями з ним. Я намагався робити це з названими трубами, але, здається, мені доведеться створити окрему назву для кожного процесу?

Який найелегантніший спосіб зробити це?

Ось мій код поки що:

#!/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замість того $$, $$що не оновлюється для передплатежів (що ми будемо використовувати для запуску manager.

fdsявляє собою масив, який буде містити дескриптори файлів, що вказують на STDIN-труби різних managers, що породилися.
Потім ми перебираємо і створюємо 5 процесів менеджера. Я використовую for (( ))синтаксис замість того, як ви робили це, оскільки він чистіший. Це специфічно для bash, але кілька речей, які цей скрипт робить, є специфічними для удару, тому вони можуть пройти весь шлях.
 

Далі ми добираємось exec {fd}> >(manager $i). Це робить ще кілька конкретних речей.
Перший з яких - це {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.


@Patrick the працює, але у вас з'явився помилка друку, {fd}, і це повинно бути $ {fd}
c4f4t0r

3
@ c4f4t0r Це не помилка
Патрік

@Patrick on suse 11 "bash type.bash type.bash: рядок 10: exec: {fd}: не знайдено" я змінив exec $ {fd}, і він працює так
c4f4t0r

2
@ c4f4t0r Версія bash у відкритому корпусі 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}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.