Знаете, я не уверен, что вам обязательно нужен повторяющийся цикл обратной связи, как изображают ваши диаграммы, настолько, насколько возможно вы могли бы использовать постоянный конвейер между сопроцессами . С другой стороны, это может быть не так уж много различий - когда вы открываете строку в сопроцессе, вы можете реализовать типичные циклы стилей, просто записывая информацию и читая информацию из нее, не делая ничего необычного.
Во-первых, может показаться, что bc
для вас это главный кандидат на сопроцесс. В bc
вы можете define
функции , которые могут сделать очень многое , что вы просите в вашем псевдокод. Например, некоторые очень простые функции для этого могут выглядеть так:
printf '%s()\n' b c a |
3<&0 <&- bc -l <<\IN <&3
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
IN
... который будет печатать ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
Но, конечно, это не последний . Как только подоболочка, отвечающая за printf
канал, завершает работу (сразу после printf
записи a()\n
в канал), канал разрушается, и bc
ввод закрывается, и он также завершает работу. Это не так полезно, как могло бы быть.
@derobert уже упомянул FIFO, что можно сделать, создав файл именованного канала с помощью mkfifo
утилиты. По сути, это также просто каналы, за исключением того, что ядро системы связывает запись файловой системы с обоих концов. Они очень полезны, но было бы лучше, если бы вы могли просто иметь канал, не рискуя получить его в файловой системе.
Как это бывает, ваша оболочка делает это много. Если вы используете оболочку, которая реализует подстановку процессов, то у вас есть очень простые способы получения прочного канала - такого типа, который вы можете назначить фоновому процессу, с которым вы можете общаться.
В bash
, например, вы можете увидеть , как заместительная процесс работы:
bash -cx ': <(:)'
+ : /dev/fd/63
Вы видите, что это действительно замена . Оболочка заменяет значение во время расширения, которое соответствует пути к ссылке на канал . Вы можете воспользоваться этим - вам не нужно ограничивать использование этого канала только для связи с любым процессом, выполняемым внутри самой ()
замены ...
bash -c '
eval "exec 3<>"<(:) "4<>"<(:)
cat <&4 >&3 &
echo hey cat >&4
read hiback <&3
echo "$hiback" here'
... который печатает ...
hey cat here
Теперь я знаю, что разные оболочки выполняют процесс сопроцессора по-разному, и что существует специальный синтаксис bash
для его настройки (и, вероятно, zsh
также для), но я не знаю, как эти вещи работают. Я просто знаю , что вы можете использовать приведенные выше синтаксис делать практически то же самое , не все канитель в обоих bash
и zsh
- и вы можете сделать очень похожую вещь в dash
и busybox ash
для достижения той же цели с здесь-документов (потому что dash
и busybox
делать здесь- документы с конвейерами, а не временные файлы, как у двух других) .
Итак, применительно к bc
...
eval "exec 3<>"<(:) "4<>"<(:)
bc -l <<\INIT <&4 >&3 &
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
INIT
export BCOUT=3 BCIN=4 BCPID="$!"
... это сложная часть. И это самое интересное ...
set --
until [ "$#" -eq 10 ]
do printf '%s()\n' b c a >&"$BCIN"
set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"
... который печатает ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965
... и он все еще работает ...
echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"
... который просто возвращает мне последнее значение для bc
s a
вместо вызова a()
функции для его увеличения и печатает ...
.29566669586771958965
Фактически, он будет продолжать работать, пока я не убью его и не разорву его каналы IPC ...
kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT