Successivo: Reti TCP/IP, Precedente: Ordinamento di vettori, Su: Funzionalità avanzate [Contenuti][Indice]
Spesso è utile poter inviare dati a un programma separato che li elabori e in seguito leggere il risultato. Questo può essere sempre fatto con file temporanei:
# Scrivere i dati per l'elaborazione filetemp = ("mieidati." PROCINFO["pid"]) while (non dipendente dai dati) print dati | ("sottoprogramma > " filetemp) close("sottoprogramma > " filetemp) # Legge il risultato, rimuove filetemp quando ha finito while ((getline nuovidati < filetemp) > 0) elabora nuovidati secondo le esigenze close(filetemp) system("rm " filetemp)
Questo funziona, ma non è elegante. Tra le altre cose, richiede che il programma venga eseguito in una directory che non può essere condivisa tra gli utenti; per esempio, /tmp non può esserlo, poiché potrebbe accadere che un altro utente stia usando un file temporaneo con lo stesso nome.91
Comunque, con gawk
, è possibile aprire una pipe
bidirezionale
verso un altro processo. Il secondo processo è chiamato coprocesso,
poiché viene eseguito in parallelo con gawk
. La connessione
bidirezionale viene creata usando l’operatore ‘|&’ (preso in prestito
dalla shell Korn, ksh
):92
do { print dati |& "sottoprogramma" "sottoprogramma" |& getline risultato } while (ci sono ancora dati da elaborare) close("sottoprogramma")
La prima volta che viene eseguita un’operazione I/O usando l’operatore
‘|&’,
gawk
crea una pipeline bidirezionale verso un processo figlio
che esegue l’altro programma. L’output creato con print
o con
printf
viene scritto nello standard input del programma, e il contenuto
dello standard output del programma può essere letto dal programma
gawk
usando getline
.
Come accade coi processi avviati con ‘|’, il sottoprogramma può
essere un qualsiasi programma, o una pipeline di programmi, che può essere
avviato dalla shell.
Ci sono alcune avvertenze da tenere presenti:
gawk
, lo standard error
dei coprocessi va nello stesso posto dove va lo standard error del
genitore gawk
. Non è possibile leggere lo standard error del
figlio separatamente.
gawk
automaticamente scrive su disco tutto
l’output spedito tramite la pipe al coprocesso.
Tuttavia, se il coprocesso non scrive su disco il suo output,
gawk
potrebbe bloccarsi mentre esegue una getline
per leggere
il risultato del coprocesso. Questo può portare a una situazione
conosciuta come stallo (deadlock, abbraccio mortale), in cui ciascun
processo rimane in attesa
che l’altro processo faccia qualcosa.
È possibile chiudere una pipe bidirezionale con un coprocesso solo in una
direzione, fornendo un secondo argomento, "to"
o "from"
, alla
funzione close()
(vedi la sezione Chiudere ridirezioni in input e in output).
Queste stringhe dicono a gawk
di chiudere la pipe, rispettivamente
nella direzione che invia i dati al coprocesso e nella direzione che legge da
esso.
Questo è particolarmente necessario per usare il programma di utilità
di sistema sort
come parte di un coprocesso;
sort
deve leggere tutti i dati di input
prima di poter produrre un qualsiasi output.
Il programma sort
non riceve un’indicazione di fine-file
(end-of-file) finché gawk
non chiude l’estremità in scrittura della
pipe.
Una volta terminata la scrittura dei dati sul programma sort
,
si può chiudere il lato "to"
della pipe, e quindi iniziare a leggere
i dati ordinati via getline
.
Per esempio:
BEGIN { comando = "LC_ALL=C sort" n = split("abcdefghijklmnopqrstuvwxyz", a, "") for (i = n; i > 0; i--) print a[i] |& comando close(comando, "to") while ((comando |& getline line) > 0) print "ricevuto", line close(comando) }
Questo programma scrive le lettere dell’alfabeto in ordine inverso, uno per
riga, attraverso la pipe bidirezionale verso sort
. Poi chiude la
direzione di scrittura della pipe, in modo che sort
riceva
un’indicazione di fine-file. Questo fa in modo che sort
ordini i
dati e scriva i dati ordinati nel programma gawk
. Una volta che
tutti i dati sono stati letti, gawk
termina il coprocesso ed esce.
Come nota a margine, l’assegnamento ‘LC_ALL=C’ nel comando sort
assicura che sort
usi l’ordinamento tradizionale di Unix (ASCII).
Ciò non è strettamente necessario in questo caso, ma è bene sapere come farlo.
Occorre prestare attenzione quando si chiude il lato "from"
di una
pipe bidirezionale; in tal caso gawk
attende che il
processo-figlio termini, il che può causare lo stallo del programma
awk
in esecuzione. (Per questo motivo, questa particolare
funzionalità è molto meno usata, in pratica, di quella che consente la
possibilità di chiudere il lato "to"
della pipe.)
ATTENZIONE: Normalmente, è un errore fatale (che fa terminare il programma
awk
) scrivere verso il lato"to"
di una pipe bidirezionale che è stata chiusa, e lo stesso vale se si legge dal lato"from"
di una pipe bidirezionale che sia stata chiusa.È possibile impostare
PROCINFO["comando", "NONFATAL"]
per far sì che tali operazioni non provochino la fine del programmaawk
. Se lo si fa, è necessario controllare il valore diERRNO
dopo ogni istruzioneprintf
, ogetline
. Vedi la sezione Abilitare continuazione dopo errori in output per ulteriori informazioni.
Per le comunicazioni bidirezionali si possono anche usare delle pseudo tty
(pty) al posto delle pipe, se il sistema in uso le prevede.
Questo vien fatto, a seconda del comando da usare, impostando un elemento
speciale nel vettore PROCINFO
(vedi la sezione Variabili predefinite con cui awk
fornisce informazioni),
in questo modo:
comando = "sort -nr" # comando, salvato in una variabile PROCINFO[comando, "pty"] = 1 # aggiorna PROCINFO print … |& comando # avvia la pipe bidirezionale …
Se il sistema in uso non ha le pty, o se tutte le pty del sistema
sono in uso, gawk
automaticamente torna a usare le pipe
regolari.
Usare le pty in genere evita i problemi di stallo del buffer descritti
precedentemente, in cambio di un piccolo calo di prestazioni. Ciò dipende
dal fatto che la gestione delle tty è fatta una riga per volta.
Su sistemi che hanno il comando stdbuf
(parte del pacchetto
GNU Coreutils), si può usare tale programma, invece delle pty.
Si noti anche che le pty non sono completamente trasparenti. Alcuni codici di controllo binari, come Ctrl-d per indicare la condizione di file-file, sono interpetati dal gestore di tty e non sono passati all’applicazione.
ATTENZIONE: In ultima analisi, i coprocessi danno adito alla possibilità di uno stallo (deadlock) tra
gawk
e il programma in esecuzione nel coprocesso. Ciò può succedere se si inviano “troppi” dati al coprocesso, prima di leggere dati inviati dallo stesso; entrambi i processi sono bloccati sulla scrittura dei dati, e nessuno dei due è disponibile a leggere quelli che sono già stati scritti dall’altro. Non c’è modo di evitare completamente una tale situazione; occorre una programmazione attenta, insieme alla conoscenza del comportamento del coprocesso.
L’esempio seguente, preparato da Andrew Schorr, dimostra come l’utilizzo delle pty può servire a evitare situazioni di stallo connesse con i buffer
Si supponga che gawk
non sia in grado di sommare dei numeri.
Si potrebbe usare un coprocesso per farlo. Ecco un programma fin troppo
semplice, che può svolgere tale funzione:
$ cat add.c #include <stdio.h> int main(void) { int x, y; while (scanf("%d %d", & x, & y) == 2) printf("%d\n", x + y); return 0; } $ cc -O add.c -o add Compilazione del programma
Si potrebbe poi scrivere un programma gawk
fin troppo semplice,
per sommare dei numeri passandoli al coprocesso:
$ echo 1 2 | > gawk -v cmd=./add '{ print |& cmd; cmd |& getline x; print x }'
E il programma andrebbe in stallo, poiché, add.c non chiama a sua
volta ‘setlinebuf(stdout)’. Il programma add
si blocca.
Ora, si provi invece con:
$ echo 1 2 | > gawk -v cmd=add 'BEGIN { PROCINFO[cmd, "pty"] = 1 } > { print |& cmd; cmd |& getline x; print x }' -| 3
Usando una pty, gawk
fa sì che la libreria di I/O
determini di avere a che fare con una sessione interattiva, e quindi
utilizzi per default una riga alla volta come buffer.
E ora, magicamente, funziona!
Michael Brennan suggerisce l’uso di rand()
per
generare nomi-file unici. Questo è un punto valido; tuttavia, i file
temporanei rimangono più difficili da usare delle pipe bidirezionali.
Questo è molto diverso dallo stesso operatore nella C shell e in Bash.
Successivo: Reti TCP/IP, Precedente: Ordinamento di vettori, Su: Funzionalità avanzate [Contenuti][Indice]