Sai, non sono convinto che tu abbia necessariamente bisogno di un ciclo di feedback ripetitivo come mostrano i tuoi diagrammi, per quanto forse potresti usare una pipeline persistente tra i coprocessi . Inoltre, potrebbe non esserci troppa differenza: una volta aperta una riga su un coprocesso è possibile implementare cicli di stile tipici semplicemente scrivendo informazioni e leggendo informazioni da essa senza fare nulla di straordinario.
In primo luogo, sembrerebbe che bc
sia il candidato principale per un coprocesso per te. In bc
te puoi define
funzioni che possono fare praticamente ciò che chiedi nel tuo pseudocodice. Ad esempio, alcune funzioni molto semplici per farlo potrebbero apparire come:
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
... che stamperebbe ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
Ma ovviamente non dura . Non appena la sottostruttura responsabile della printf
pipe si chiude (subito dopo printf
scrive a()\n
sulla pipe) la pipe viene abbattuta e bc
l'input si chiude e si chiude anche. Non è così utile come potrebbe essere.
@derobert ha già menzionato FIFO come si può creare creando un file pipe con l' mkfifo
utilità. Questi sono essenzialmente solo pipe, tranne per il fatto che il kernel di sistema collega una voce del filesystem ad entrambe le estremità. Questi sono molto utili, ma sarebbe meglio se si potesse semplicemente avere una pipe senza rischiare che venisse curiosata nel filesystem.
In effetti, la tua shell lo fa molto. Se usi una shell che implementa la sostituzione dei processi, allora hai un mezzo molto semplice per ottenere una pipe duratura - del tipo che potresti assegnare a un processo in background con cui puoi comunicare.
Ad bash
esempio, puoi vedere come funziona la sostituzione del processo:
bash -cx ': <(:)'
+ : /dev/fd/63
Vedi che è davvero una sostituzione . La shell sostituisce un valore durante l'espansione che corrisponde al percorso di un collegamento a una pipe . Puoi trarne vantaggio - non devi essere costretto a usare quella pipe solo per comunicare con qualunque processo venga eseguito all'interno della ()
sostituzione stessa ...
bash -c '
eval "exec 3<>"<(:) "4<>"<(:)
cat <&4 >&3 &
echo hey cat >&4
read hiback <&3
echo "$hiback" here'
... che stampa ...
hey cat here
Ora so che diverse shell eseguono il processo di coprocesso in modi diversi - e che esiste una sintassi specifica bash
per configurarne uno (e probabilmente anche uno zsh
) - ma non so come funzionano queste cose. So solo che puoi usare la sintassi di cui sopra per fare praticamente la stessa cosa senza tutto il rigmarole in entrambi bash
e zsh
- e puoi fare una cosa molto simile dash
e busybox ash
raggiungere lo stesso scopo con i documenti qui (perché dash
e busybox
fare qui- documenti con pipe anziché file temporanei come fanno gli altri due) .
Quindi, quando applicato 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="$!"
... questa è la parte difficile. E questa è la parte divertente ...
set --
until [ "$#" -eq 10 ]
do printf '%s()\n' b c a >&"$BCIN"
set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"
... che stampa ...
b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965
... e funziona ancora ...
echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"
... che mi dà solo l'ultimo valore per bc
" a
piuttosto che chiamare la a()
funzione per incrementarla e stampare ...
.29566669586771958965
Continuerà a funzionare, infatti, fino a quando non lo ucciderò e abbatterò i suoi tubi IPC ...
kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT