Successivo: , Precedente: , Su: Funzioni predefinite   [Contenuti][Indice]


9.1.4 Funzioni di Input/Output

Le seguenti funzioni riguardano l’input/output (I/O). I parametri opzionali sono racchiusi tra parentesi quadre ([ ]):

close(nome_file [, come])

Chiude il file nome_file in input o in output. Alternativamente, l’argomento può essere un comando della shell usato per creare un coprocesso, o per ridirigere verso o da una pipe; questo coprocesso o pipe viene chiuso. Vedi la sezione Chiudere ridirezioni in input e in output per ulteriori informazioni.

Quando si chiude un coprocesso, può talora essere utile chiudere dapprima un lato della pipe bidirezionale e quindi chiudere l’altro. Questo si può fare fornendo un secondo argomento a close(). Questo secondo argomento (come) dovrebbe essere una delle due stringhe "to" o "from", che indicano quale lato della pipe chiudere. La stringa può essere scritta indifferentemente in maiuscolo o in minuscolo. Vedi la sezione Comunicazioni bidirezionali con un altro processo, che tratta questa funzionalità con maggior dettaglio e mostra un esempio.

Si noti che il secondo argomento di close() è un’estensione gawk; non è disponibile in modalità compatibile (vedi la sezione Opzioni sulla riga di comando).

fflush([nome_file])

Scrive su disco ogni output contenuto in memoria, associato con nome_file, che è o un file aperto in scrittura o un comando della shell che ridirige output a una pipe o a un coprocesso.

Molti programmi di utilità bufferizzano il loro output (cioè, accumulano in memoria record da scrivere in un file su disco o sullo schermo, fin quando non arriva il momento giusto per inviare i dati al dispositivo di output). Questo è spesso più efficiente che scrivere ogni particella di informazione non appena diventa disponibile. Tuttavia, qualche volta è necessario forzare un programma a svuotare i suoi buffer (cioè, inviare l’informazione alla sua destinazione, anche se un buffer non è pieno). Questo è lo scopo della funzione fflush(); anche gawk scrive il suo output in un buffer, e la funzione fflush() forza gawk a svuotare i suoi buffer.

Brian Kernighan ha aggiunto fflush() al suo awk nell’aprile 1992. Per due decenni è rimasta un’estensione comune. A Dicembre 2012 è stata accettata e inclusa nello standard POSIX. Si veda il sito Web dell’Austin Group.

POSIX standardizza fflush() come segue: se non c’è alcun argomento, o se l’argomento è la stringa nulla (""), awk svuota i buffer di tutti i file in output e di tutte le pipe.

NOTA: Prima della versione 4.0.2, gawk avrebbe svuotato solo i buffer dello standard output se non era specificato alcun argomento, e svuotato tutti i buffer dei file in output e delle pipe se l’argomento era la stringa nulla. Questo è stato modificato per essere compatibile con l’awk di Kernighan, nella speranza che standardizzare questa funzionalità in POSIX sarebbe stato più agevole (come poi è effettivamente successo).

Con gawk, si può usare ‘fflush("/dev/stdout")’ se si desidera solo svuotare i buffer dello standard output.

fflush() restituisce zero se il buffer è svuotato con successo; altrimenti, restituisce un valore diverso da zero. (gawk restituisce -1.) Nel caso in cui tutti i buffer vadano svuotati, il valore restituito è zero solo se tutti i buffer sono stati svuotati con successo. Altrimenti, è -1, e gawk avvisa riguardo al nome_file che ha problemi.

gawk invia anche un messaggio di avvertimento se si tenta di svuotare i buffer di un file o pipe che era stato aperto in lettura (p.es. con getline), o se nome_file non è un file, una pipe, o un coprocesso aperto. in tal caso, fflush() restituisce ancora -1.

Bufferizzazione interattiva e non interattiva

A complicare ulteriormente le cose, i problemi di bufferizzazione possono peggiorare se il programma eseguito è interattivo (cioè, se comunica con un utente seduto davanti a una tastiera).52

I programmi interattivi normalmente bufferizzano per riga il loro output (cioè, scrivono in output una riga alla volta). I programmi non-interattivi attendono di aver riempito un buffer, il che può voler dire anche parecchie righe di output. Ecco un esempio della differenza:

$ awk '{ print $1 + $2 }'
1 1
-| 2
2 3
-| 5
Ctrl-d

Ogni riga di output è stampata immediatamente. Si confronti questo comportamente con quello di questo esempio:

$ awk '{ print $1 + $2 }' | cat
1 1
2 3
Ctrl-d
-| 2
-| 5

In questo caso, nessun output viene stampato finché non è stato battuto il Ctrl-d, perché l’output è bufferizzato e inviato tramite pipe al comando cat in un colpo solo.

system(comando)

Esegue il comando del sistema operativo comando e quindi ritorna al programma awk. Restituisce il codice di ritorno di comando.

Per esempio, inserendo il seguente frammento di codice in un programma awk:

END {
     system("date | mail -s 'awk completato' root")
}

all’amministratore di sistema viene inviato un messaggio di posta quando il programma awk termina di elaborare l’input e inizia l’elaborazione da eseguire alla fine dell’input.

Si noti che la ridirezione di print o printf in una pipe è spesso sufficiente per ottenere lo stesso risultato. Se è necessario eseguire parecchi comandi, è più efficiente stamparli verso una pipe diretta alla shell:

while (ancora lavoro da fare)
    print comando | "/bin/sh"
close("/bin/sh")

Tuttavia, nel caso che il programma awk sia interattivo, system() è utile per eseguire grossi programmi autonomi, come ad esempio la shell o un programma di modifica testi. Alcuni sistemi operativi non consentono di implementare la funzione system(). Richiamare system() in sistemi in cui non è disponibile provoca un errore fatale.

NOTA: Quando si specifica l’opzione --sandbox, la funzione system() è disabilitata (vedi la sezione Opzioni sulla riga di comando).

Nei sistemi aderenti allo standard POSIX, il codice di ritorno di un comando è un numero contenuto in 16 bit. Il valore del codice di ritorno passato alla funzione C exit() alla fine del programma è contenuto negli 8 bit di valore più alto dei 16 bit (la metà sinistra) che compongono il numero. I bit di valore più basso (la metà destra) indicano se il processo è stato terminato da un segnale (bit 7), e, se questo è il caso, il numero del segnale che ha provocato la terminazione (bit 0–6).

Tradizionalmente, la funzione system() di awk si è semplicemente limitata a restituire il valore del codice di ritorno diviso per 256 (ossia la metà sinistra del numero di 16 bit, spostata a destra). In una situazione normale questo equivale a utilizzare il codice di ritornodi system(), ma nel caso in cui il programma sia stato terminato da un segnale, il valore diventa un numero frazionale in virgola mobile.53 POSIX stabilisce che la chiamata a system() dall’interno di awk dovrebbe restituire l’intero valore a 16 bit.

gawk si trova in qualche modo a metà strada. I valori del codice di ritorno sono descritti nella Tabella 9.5.

SituazioneValore codice di ritorno da system()
--traditionalValore dalla funzione C system()/256
--posixValore dalla funzione C system()
Uscita normale dal comandoCodice di ritorno del comando
Terminazione da un segnale256 + numero segnale "assassino"
Terminazione da un segnale con dump memoria512 + numero segnale "assassino"
Qualsiasi tipo di errore-1

Tabella 9.5: Valori codici di ritorno da chiamata a system()

A partire dalla versione di agosto 2018, BWK awk si comporta come gawk per il codice di ritorno della chiamata system().

Controllare la bufferizzazione dell’output con system()

La funzione fflush() consente un controllo esplicito sulla bufferizzazione dell’output per singoli file e pipe. Tuttavia, il suo utilizzo non è portabile su molte delle meno recenti implementazioni di awk. Un metodo alternativo per forzare la scrittura dell’output è una chiamata a system() che abbia come argomento la stringa nulla:

system("")   # scrive l'output su disco

gawk tratta questo uso della funzione system() come un caso speciale, e si guarda bene dall’invocare la shell (o un altro interprete di comandi) con un comando nullo. Quindi, con gawk, questa maniera di procedere non è solo utile, ma è anche efficiente. Questo metodo dovrebbe funzionare anche con altre implementazioni di awk, ma non è detto che eviti una invocazione non necessaria della shell. (Altre implementazioni potrebbero limitarsi a forzare la scrittura del buffer associato con lo standard output, e non necessariamente di tutto l’output bufferizzato.)

Avendo in mente le attese di un programmatore, sarebbe sensato che system() forzi la scrittura su disco di tutto l’output disponibile. Il programma seguente:

BEGIN {
     print "prima riga stampata"
     system("echo system echo")
     print "seconda riga stampata"
}

deve stampare:

prima riga stampata
system echo
seconda riga stampata

e non:

system echo
prima riga stampata
seconda riga stampata

Se awk non forzasse la scrittura dei suoi buffer prima di invocare system(), l’output sarebbe quest’ultimo (quello non voluto).


Note a piè di pagina

(52)

Un programma è interattivo se il suo standard output è connesso a un dispositivo terminale. Ai giorni nostri, questo vuol dire davanti a uno schermo e a una tastiera.

(53)

In uno scambio di messaggi privato il Dr. Kernighan mi ha comunicato che questo modo di procedere è probabilmente errato.


Successivo: , Precedente: , Su: Funzioni predefinite   [Contenuti][Indice]