Ви знаєте, я не впевнений, що вам обов'язково потрібен повторюваний цикл зворотного зв’язку, як зображені ваші діаграми, настільки, наскільки, можливо, ви могли б використовувати стійкий конвеєр між копроцесами . Знову ж таки, можливо, це не дуже велика різниця - як тільки ви відкриєте рядок у копроцесі, ви зможете реалізувати типові петлі стилів, просто записуючи інформацію та читаючи з неї інформацію, не роблячи нічого незвичайного.
По-перше, здається, що 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
тут - документи з трубами, а не temp-файлами, як це роблять інші два) .
Отже, при застосуванні до 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