Sabes, no estoy convencido de que necesariamente necesites un ciclo de retroalimentación repetitiva como lo muestran tus diagramas, por mucho que tal vez puedas usar una tubería persistente entre coprocesos . Por otra parte, puede ser que no haya demasiada diferencia: una vez que abre una línea en un coproceso, puede implementar bucles de estilo típicos simplemente escribiendo información y leyendo información de ella sin hacer nada fuera de lo común.
En primer lugar, parecería que bc
es un candidato principal para un coproceso para usted. En bc
usted puede define
funciones que pueden hacer más o menos lo que pide en su pseudocódigo. Por ejemplo, algunas funciones muy simples para hacer esto podrían ser:
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
... que imprimiría ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
Pero, por supuesto, no dura . Tan pronto como el subshell a cargo de printf
la tubería se cierra (justo después de printf
escribir a()\n
en la tubería), la tubería se rompe y bc
la entrada se cierra y también se cierra. Eso no es tan útil como podría ser.
@derobert ya ha mencionado FIFO como se puede obtener al crear un archivo de canalización con nombre con la mkfifo
utilidad. Estos son esencialmente solo tuberías, excepto que el núcleo del sistema vincula una entrada del sistema de archivos a ambos extremos. Estos son muy útiles, pero sería mejor si pudieras tener una tubería sin correr el riesgo de que se espíe en el sistema de archivos.
Resulta que tu caparazón lo hace mucho. Si usa un shell que implementa la sustitución de procesos, entonces tiene un medio muy sencillo de obtener una tubería duradera , del tipo que podría asignar a un proceso en segundo plano con el que puede comunicarse.
En bash
, por ejemplo, puede ver cómo funciona la sustitución del proceso:
bash -cx ': <(:)'
+ : /dev/fd/63
Ves que realmente es una sustitución . El shell sustituye un valor durante la expansión que corresponde a la ruta a un enlace a una tubería . Puede aprovechar eso: no necesita limitarse a usar esa tubería solo para comunicarse con cualquier proceso que se ejecute dentro de la ()
sustitución misma ...
bash -c '
eval "exec 3<>"<(:) "4<>"<(:)
cat <&4 >&3 &
echo hey cat >&4
read hiback <&3
echo "$hiback" here'
... que imprime ...
hey cat here
Ahora sé que diferentes shells hacen el coprocesamiento de diferentes maneras, y que hay una sintaxis específica bash
para configurar uno (y probablemente también para uno zsh
) , pero no sé cómo funcionan esas cosas. Solo sé que puede usar la sintaxis anterior para hacer prácticamente lo mismo sin todo el rigmarole en ambos bash
y zsh
, y puede hacer algo muy similar dash
y busybox ash
lograr el mismo propósito con documentos aquí (porque dash
y busybox
hacer aquí) documentos con tuberías en lugar de archivos temporales como lo hacen los otros dos) .
Entonces, cuando se aplica a 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="$!"
... esa es la parte difícil. Y esta es la parte divertida ...
set --
until [ "$#" -eq 10 ]
do printf '%s()\n' b c a >&"$BCIN"
set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"
... que imprime ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965
... y sigue funcionando ...
echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"
... lo que me da el último valor para bc
's en a
lugar de llamar a la a()
función para incrementarlo e imprime ...
.29566669586771958965
Continuará funcionando, de hecho, hasta que lo mate y derribe sus tuberías IPC ...
kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT