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 facoltativi 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 Chiusura file e pipe 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 I/O bidirezionale, 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 Opzioni).

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 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 Opzioni).

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 a 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()

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]