Vous savez, je ne suis pas convaincu que vous ayez nécessairement besoin d'une boucle de rétroaction répétitive comme le montrent vos diagrammes, autant que vous pourriez peut-être utiliser un pipeline persistant entre les coprocessus . Là encore, il se peut qu'il n'y ait pas trop de différence - une fois que vous ouvrez une ligne sur un coprocessus, vous pouvez implémenter des boucles de style typiques en écrivant et en lisant des informations sans rien faire de très inhabituel.
En premier lieu, il semblerait que ce bc
soit un candidat de choix pour un coprocessus pour vous. Dans bc
vous pouvez des define
fonctions qui peuvent faire à peu près ce que vous demandez dans votre pseudocode. Par exemple, certaines fonctions très simples pour ce faire pourraient ressembler à:
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
... qui imprimerait ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
Mais bien sûr, cela ne dure pas . Dès que le sous-shell en charge de printf
la pipe de 's se ferme (juste après avoir printf
écrit a()\n
dans la pipe) la pipe est détruite et bc
l'entrée de' ferme 'et elle se ferme aussi. Ce n'est pas aussi utile qu'il pourrait l'être.
@derobert a déjà mentionné les FIFO comme cela peut être fait en créant un fichier de canal nommé avec l' mkfifo
utilitaire. Ce ne sont essentiellement que des canaux, sauf que le noyau système relie une entrée de système de fichiers aux deux extrémités. Celles-ci sont très utiles, mais ce serait mieux si vous pouviez simplement avoir un tuyau sans risquer qu'il soit espionné dans le système de fichiers.
En fait, votre shell fait beaucoup cela. Si vous utilisez un shell qui implémente la substitution de processus, vous disposez d'un moyen très simple d'obtenir un canal durable - du type que vous pourriez attribuer à un processus en arrière-plan avec lequel vous pouvez communiquer.
Dans bash
, par exemple, vous pouvez voir comment fonctionne la substitution de processus:
bash -cx ': <(:)'
+ : /dev/fd/63
Vous voyez, c'est vraiment une substitution . Le shell substitue une valeur lors de l'expansion qui correspond au chemin vers un lien vers un tuyau . Vous pouvez en profiter - vous n'avez pas besoin d'être contraint d'utiliser ce canal uniquement pour communiquer avec le processus exécuté dans la ()
substitution elle-même ...
bash -c '
eval "exec 3<>"<(:) "4<>"<(:)
cat <&4 >&3 &
echo hey cat >&4
read hiback <&3
echo "$hiback" here'
... qui imprime ...
hey cat here
Maintenant, je sais que différents shells font le coprocessus de différentes manières - et qu'il y a une syntaxe spécifique bash
pour en configurer un (et probablement un pour zsh
aussi) - mais je ne sais pas comment ces choses fonctionnent. Je sais juste que vous pouvez utiliser la syntaxe ci-dessus pour faire pratiquement la même chose sans tout le rigmarole dans les deux bash
et zsh
- et vous pouvez faire une chose très similaire dans dash
et busybox ash
pour atteindre le même but avec ici-documents (parce que dash
et busybox
faites ici- documents avec des tuyaux plutôt que des fichiers temporaires comme les deux autres) .
Donc, lorsqu'il est appliqué à 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="$!"
... c'est la partie difficile. Et c'est la partie amusante ...
set --
until [ "$#" -eq 10 ]
do printf '%s()\n' b c a >&"$BCIN"
set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"
... qui imprime ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965
... et il fonctionne toujours ...
echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"
... ce qui me donne juste la dernière valeur de bc
's a
plutôt que d'appeler la a()
fonction pour l'incrémenter et l'imprimer ...
.29566669586771958965
Il continuera de fonctionner, en fait, jusqu'à ce que je le tue et que j'arrache ses tuyaux IPC ...
kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT