Successivo: Introduzione3, Su: (dir) [Contenuti][Indice]
Questo file documenta awk
, un programma che si può usare per
selezionare dei record determinati in un file ed eseguire azioni su di essi.
Copyright dell’edizione originale © 1989, 1991, 1992, 1993, 1996–2005, 2007, 2009–2017
Free Software Foundation, Inc.
Copyright dell’edizione italiana © 2016 – Free Software Foundation, Inc.
Questa è l’Edizione 4.1 di GAWK: Programmare efficacemente in AWK: Una Guida Utente per GNU Awk, per la versione 4.1.4 (o successiva) dell’implementazione GNU di AWK. (Titolo originale: Gawk: Effective AWK Programming: A User’s Guide for GNU Awk.)
È garantito il permesso di copiare, distribuire e/o modificare questo documento seguendo i termini della Licenza per Documentazione Libera GNU, Versione 1.3 o ogni versione successiva pubblicata dalla Free Software Foundation; con le Sezioni Non Modificabili “GNU General Public License”, con i testi di copertina “Un Manuale GNU”, e con i testi di quarta di copertina come in (a) più avanti. Una copia della licenza è acclusa nella sezione intitolata "Licenza per Documentazione Libera GNU".
• Introduzione3: | Alcune parole gentili riguardo a questo Documento. | |
• Introduzione4: | Ulteriori parole gentili. | |
• Prefazione: | Di cosa tratta questo Documento; breve storia e ringraziamenti. | |
• Per iniziare: | Un’introduzione elementare all’uso di
awk . Come eseguire un programma
awk . Sintassi della riga di
comando.
| |
• Invocare Gawk: | Come eseguire gawk .
| |
• Espressioni regolari: | Tutto quel che c’è da sapere sull’individuazione di stringhe tramite espressioni regolari. | |
• Leggere file: | Come leggere file e manipolare campi. | |
• Stampare: | Come stampare usando awk .
Descrizione delle istruzioni print e
printf . È descritta inoltre la
ridirezione dell’output.
| |
• Espressioni: | Le espressioni sono i componenti elementari delle istruzioni. | |
• Criteri di ricerca e azioni: | Panoramica sui criteri di ricerca e sulle azioni. | |
• Vettori: | La descrizione e l’uso dei vettori. Sono inoltre descritte le istruzioni di controllo relative ai vettori. | |
• Funzioni: | Funzioni predefinite e definite dall’utente. | |
• Funzioni di libreria: | Una libreria di funzioni di awk .
| |
• Programmi di esempio: | Molti programmi awk con
spiegazioni dettagliate.
| |
• Funzionalità avanzate: | Roba per utenti sofisticati, propria di
gawk .
| |
• Internazionalizzazione: | Come far sì che gawk parli la
vostra lingua.
| |
• Debugger: | Il debugger di gawk .
| |
• Calcolo con precisione arbitraria: | Calcolo con precisione arbitraria in
gawk .
| |
• Estensioni dinamiche: | Aggiungere nuove funzioni predefinite di
gawk .
| |
• Storia del linguaggio: | L’evoluzione del linguaggio awk .
| |
• Installazione: | Installare gawk in vari sistemi
operativi.
| |
• Note: | Note riguardo ad aggiunte a gawk
e possibili futuri sviluppi.
| |
• Concetti fondamentali: | Velocissima introduzione alla programmazione. | |
• Glossario: | Spiegazione di alcuni termini poco familiari. | |
• Copia: | Il vostro diritto a copiare e distribuire
gawk .
| |
• Licenza per Documentazione Libera GNU (FDL): | La licenza per questo Documento. | |
• Indice analitico: | Indice dei concetti e delle variabili. | |
• Storia: | La storia di gawk e
awk .
| |
• Nomi: | Che nome usare per trovare
awk .
| |
• Questo manuale: | Uso di questo Documento. Comprende esempi di file in input utilizzabili. | |
• Convenzioni: | Convenzioni tipografiche. | |
• Storia del manuale: | Breve storia del progetto GNU e di questo Documento. | |
• Come contribuire: | Un aiuto per la salvezza del mondo. | |
• Ringraziamenti: | Ringraziamenti. | |
• Eseguire gawk: | Come eseguire programmi
gawk ; comprende la sintassi
della riga di comando.
| |
• Monouso: | Eseguire un breve programma
awk di tipo usa-e-getta.
| |
• Leggere dal terminale: | Senza uso di file in input (input immesso da tastiera). | |
• Lunghi: | Mettere programmi awk
permanenti in file.
| |
• Script eseguibili: | Preparare programmi awk
da eseguire come script.
| |
• Commenti: | Aggiungere documentazione a programmi
gawk .
| |
• Protezione: | Ulteriore discussione di problemi connessi alle protezioni nella shell. | |
• Doppi apici in DOS: | Doppi apici in file .BAT Windows | |
• File dati di esempio: | File di dati di esempio da usare nei
programmi awk illustrati in
questo Documento.
| |
• Molto semplice: | Un esempio molto semplice. | |
• Due regole: | Un esempio meno semplice di programma di una riga, che usa due regole. | |
• Maggiore sofisticazione: | Un esempio più complesso. | |
• Istruzioni/Righe: | Suddividere o riunire istruzioni su [una o più] righe. | |
• Altre funzionalità: | Altre funzionalità di awk .
| |
• Quando: | Quando usare gawk e quando
usare altre cose.
| |
• Sommario dell'introduzione: | Sommario dell’introduzione. | |
• Riga di comando: | Come eseguire awk .
| |
• Opzioni: | Opzioni sulla riga di comando e loro significato. | |
• Altri argomenti: | Nomi dei file in input e assegnamento di valori a variabili. | |
• Specificare lo standard input: | Come specificare lo standard input insieme ad altri file. | |
• Variabili d'ambiente: | Le variabili d’ambiente usate da
gawk .
| |
• AWKPATH (Variabile): | Ricerca di programmi awk
in una lista di directory.
| |
• AWKLIBPATH (Variabile): | Ricerca di librerie condivise
awk in una lista di
directory.
| |
• Altre variabili d'ambiente: | Le variabili d’ambiente. | |
• Codice di ritorno: | Il codice di ritorno all’uscita
da gawk .
| |
• Includere file: | Come includere altri file nel proprio programma. | |
• Caricare librerie condivise: | Caricare librerie condivise nel proprio programma. | |
• Parti obsolete: | Opzioni e/o funzionalità obsolete. | |
• Non documentato: | Opzioni e funzionalità non documentate. | |
• Sommario invocazione: | Sommario di come eseguire
awk .
| |
• Uso di regexp: | Come usare le espressioni regolari. | |
• Sequenze di protezione: | Come scrivere caratteri non stampabili. | |
• Operatori di espressioni regolari: | Operatori di espressioni regolari. | |
• Espressioni tra parentesi quadre: | Cosa possono contenere ‘[...]’. | |
• Più lungo da sinistra: | Quanto è lungo il testo individuato. | |
• Espressioni regolari calcolate: | Usare regexp dinamiche. | |
• Operatori di regexp GNU: | Operatori propri del software GNU. | |
• Maiuscolo-Minuscolo: | Fare confronti ignorando maiuscolo/minuscolo. | |
• Sommario espressioni regolari: | Sommario delle espressioni regolari. | |
• Record: | Controllare come i dati sono suddivisi in record. | |
• awk divisione record: | Divisione dei record con awk
standard.
| |
• gawk divisione record: | Divisione dei record con gawk .
| |
• Campi: | Un’introduzione ai campi. | |
• Campi non costanti: | Numeri di campo variabili. | |
• Cambiare i campi: | Cambiare il contenuto di un campo. | |
• Separatori di campo: | I separatori di campo, e come cambiarli. | |
• Separatori di campo di default: | Come di solito sono separati i campi. | |
• Separare campi con regexp: | Usare regexp come separatori. | |
• Campi di un solo carattere: | Fare di ogni carattere un campo separato. | |
• Separatori campo da riga di comando: | Impostare FS dalla riga di
comando.
| |
• Campo intera riga: | Fare di una riga intera un campo solo. | |
• Sommario sulla separazione campi: | Alcuni punti finali e una tavola di sommario. | |
• Dimensione costante: | Leggere campi di larghezza costante. | |
• Separazione in base al contenuto: | Definire campi dal loro Contenuto. | |
• Righe multiple: | Record su righe multiple | |
• Getline: | Richiedere input usando getline .
| |
• Getline semplice: | Usare getline senza argomenti.
| |
• Getline variabile: | Usare getline in una variabile.
| |
• Getline file: | Usare getline da un file.
| |
• Getline variabile file: | Usare getline in una variabile
da un file.
| |
• Getline pipe: | Usare getline da una pipe.
| |
• Getline variabile pipe: | Usare getline in una variabile
da una pipe.
| |
• Getline coprocesso: | Usare getline da un coprocesso.
| |
• Getline variabile coprocesso: | Usare getline in una variabile
da un coprocesso.
| |
• Note su getline: | Cose importanti da sapere su
getline .
| |
• Sommario di getline: | Sommario delle varianti di
getline .
| |
• Timeout in lettura: | Leggere input entro un tempo limite. | |
• Proseguire dopo errore in input: | Rielaborare input dopo certi errori. | |
• Directory su riga di comando: | Cosa accade se si mette una directory sulla riga di comando. | |
• Sommario di Input: | Sommario di Input. | |
• Esercizi su Input: | Esercizi. | |
• Print: | L’istruzione print .
| |
• Esempi su print: | Semplici esempi di
istruzioni print .
| |
• Separatori di output: | I separatori di output e come modificarli. | |
• OFMT: | Controllare l’output di numeri con
print .
| |
• Printf: | L’istruzione printf .
| |
• Printf Fondamenti: | Sintassi dell’istruzione
printf .
| |
• Lettere di controllo: | Lettere di controllo del formato. | |
• Modificatori di formato: | Modificatori specifiche di formato. | |
• Esempi su printf: | Numerosi esempi. | |
• Ridirezione: | Come ridirigere l’output a diversi file e pipe. | |
• FD speciali: | File speciali per I/O. | |
• File speciali: | Interpretazione nomi-file in
gawk . gawk
permette di accedere a descrittori di
file ereditati.
| |
• Altri file ereditati: | Accedere ad altri file aperti con
gawk .
| |
• Reti speciali: | File speciali per comunicazioni con la rete. | |
• Avvertimenti speciali: | Cose a cui prestare attenzione. | |
• Chiusura file e pipe: | Chiudere file in input e di output e pipe. | |
• Continuazione dopo errori: | Abilitare continuazione dopo errori in output. | |
• Sommario di Output: | Sommario di Output. | |
• Esercizi su Output: | Esercizi. | |
• Valori: | Costanti, variabili ed espressioni regolari. | |
• Costanti: | Costanti di tipo stringa, numeriche ed espressioni regolari. | |
• Costanti scalari: | Costanti numeriche e stringhe. | |
• Numeri non-decimali: | Cosa sono i numeri ottali ed esadecimali. | |
• Costanti come espressioni regolari: | Costanti fornite tramite espressioni regolari. | |
• Usare le costanti regexp: | Quando e come usare una costante specificata tramite espressioni regolari | |
• Costanti regexp normali: | Costanti regexp normali in
awk .
| |
• Costanti regexp forti: | Costanti regexp fortemente tipizzate. | |
• Variabili: | Le variabili permettono di definire valori da usare in seguito. | |
• Usare variabili: | Usare variabili nei propri programmi. | |
• Opzioni di assegnamento: | Impostare variabili dalla riga di comando, e un sommario della sintassi della riga di comando. Questo è un metodo di input avanzato. | |
• Conversione: | La conversione di stringhe in numeri e viceversa. | |
• Stringhe e numeri: | Come awk converte tra
stringhe e numeri.
| |
• Localizzazione e conversioni: | Come la localizzazione può influire sulle conversioni. | |
• Tutti gli operatori: | Gli operatori di gawk .
| |
• Operatori aritmetici: | Operazioni aritmetiche (‘+’, ‘-’, etc.) | |
• Concatenazione: | Concatenazione di stringhe. | |
• Operatori di assegnamento: | Cambiare il valore di una variabile o di un campo. | |
• Operatori di incremento: | Incrementare il valore numerico di una variabile. | |
• Valori e condizioni di verità: | Determinare Vero/Falso. | |
• Valori di verità: | Cosa è “vero” e cosa è “falso”. | |
• Tipi di variabile e confronti: | Come alle variabili si assegna il tipo e l’effetto che questo ha sul confronto di numeri e stringhe con ‘<’, etc. | |
• Tipi di variabile: | Tipo stringa rispetto a tipo numero. | |
• Operatori di confronto: | Gli operatori di confronto. | |
• Confronto POSIX di stringhe: | Confronto tra stringhe usando le regole POSIX. | |
• Operatori booleani: | Combinare espressioni di confronto usando operatori booleani ‘||’ (“or”), ‘&&’ (“and”) e ‘!’ (“not”). | |
• Espressioni condizionali: | Le espressioni condizionali scelgono una tra due sottoespressioni, a seconda del valore di una terza sottoespressione. | |
• Chiamate di funzione: | Una chiamata di funzione è un’espressione. | |
• Precedenza: | Come si nidificano i vari operatori. | |
• Localizzazioni: | Come la localizzazione influenza la gestione dati. | |
• Sommario delle espressioni: | Sommario delle espressioni. | |
• Panoramica sui criteri di ricerca: | Come scrivere un criterio di ricerca. | |
• regexp come criteri di ricerca: | Espressioni regolari come criteri di ricerca. | |
• Espressioni come criteri di ricerca: | Qualsiasi espressione può servire da criterio di ricerca. | |
• Intervalli: | Specificare intervalli di record con i criteri di ricerca. | |
• BEGIN/END: | Specificare regole di inizio e fine programma. | |
• Usare BEGIN/END: | Come e perché usare regole BEGIN/END. | |
• I/O e BEGIN/END: | Problemi di I/O nelle regole BEGIN/END. | |
• BEGINFILE/ENDFILE: | Due condizioni speciali per controlli avanzati. | |
• Vuoto: | Il criterio di ricerca vuoto, che corrisponde a ogni record. | |
• Usare variabili di shell: | Come usare variabili di shell in
awk .
| |
• Panoramica sulle azioni: | Cosa costituisce un’azione. | |
• Istruzioni: | Descrizione dettagliata delle varie istruzioni di controllo. | |
• Istruzione if: | Eseguire in maniera condizionale
istruzioni awk .
| |
• Istruzione while: | Eseguire il ciclo, finché è verificata una condizione. | |
• Istruzione do: | Eseguire l’azione specificata, continuare a eseguire il ciclo finché è verificata una condizione. | |
• Istruzione for: | Un’altra istruzione iterativa, che permette di specificare clausole iniziali e di incremento. | |
• Istruzione switch: | Valutazione di quale insieme di istruzioni eseguire, a seconda del valore assunto da una variabile. | |
• Istruzione break: | Uscire subito dal ciclo più interno in cui ci si trova. | |
• Istruzione continue: | Andare alla fine del ciclo più interno in cui ci si trova. | |
• Istruzione next: | Smettere di elaborare il record corrente. | |
• Istruzione nextfile: | Smettere di elaborare il file corrente. | |
• Istruzione exit: | Interrompere l’esecuzione di awk .
| |
• Variabili predefinite: | Sommario delle variabili predefinite. | |
• Variabili modificabili dall'utente: | Variabili predefinite modificabili per
controllare awk .
| |
• Variabili auto-assegnate: | Variabili predefinite con cui
awk fornisce informazioni.
| |
• ARGC e ARGV: | Modi di usare ARGC e
ARGV .
| |
• Sommario criteri e azioni: | Sommario criteri e azioni. | |
• Fondamenti sui vettori: | Informazioni di base sui vettori. | |
• Introduzione ai vettori: | Introduzione ai vettori. | |
• Visitare elementi: | Come esaminare un elemento di un vettore. | |
• Impostare elementi: | Come cambiare un elemento di un vettore. | |
• Esempio di vettore: | Esempio semplice di vettore | |
• Visitare un intero vettore: | Variazione dell’istruzione
for . Cicla attraverso gli
indici degli elementi contenuti in
un vettore.
| |
• Controllare visita: | Controllare l’ordine in cui i vettori sono visitati. | |
• Indici numerici di vettore: | Come usare numeri come indici in
awk .
| |
• Indici non inizializzati: | Usare variabili non inizializzate come indici. | |
• Cancellazione: | L’istruzione delete toglie un
elemento da un vettore.
| |
• Vettori multidimensionali: | Emulare vettori multidimensionali in
awk .
| |
• Visitare vettori multidimensionali: | Visitare vettori multidimensionali. | |
• Vettori di vettori: | Vettori multidimensionali veri. | |
• Sommario dei vettori: | Sommario dei vettori. | |
• Funzioni predefinite: | Riepilogo delle funzioni predefinite. | |
• Chiamare funzioni predefinite: | Come chiamare funzioni predefinite. | |
• Funzioni numeriche: | Funzioni che trattano numeri, comprese
int() , sin()
e rand() .
| |
• Funzioni per stringhe: | Funzioni di manipolazione di stringhe,
come split() , match()
e sprintf() .
| |
• Dettagli ostici: | Più di quel che si vorrebbe sapere su
‘\’ e ‘&’ con sub() ,
gsub() , e gensub() .
| |
• Funzioni di I/O: | Funzioni per i file e per i comandi della shell. | |
• Funzioni di tempo: | Funzione per gestire marcature temporali. | |
• Funzioni a livello di bit: | Funzioni per operazioni di manipolazione bit. | |
• Funzioni per i tipi: | Funzioni per conoscere il tipo di una variabile. | |
• Funzioni di internazionalizzazione: | Funzioni per tradurre stringhe. | |
• Funzioni definite dall'utente: | Descrizione dettagliata delle funzioni definite dall’utente. | |
• Sintassi delle definizioni: | Come scrivere definizioni e cosa significano. | |
• Esempio di funzione: | Un esempio di definizione di funzione e spiegazione della stessa. | |
• Precisazioni sulle funzioni: | Cose a cui prestare attenzione. | |
• Chiamare una funzione: | Non usare spazi. | |
• Campo di validità variabili: | Variabili locali e globali. | |
• Parametri per valore/riferimento: | Passaggio parametri. | |
• Istruzione return: | Specificare il valore che una funzione restituisce. | |
• Variabili di tipo dinamico: | Come cambiare tipo a una variabile in fase di esecuzione del programma. | |
• Chiamate indirette: | Scegliere la funzione da chiamare in fase di esecuzione del programma. | |
• Sommario delle funzioni: | Sommario delle funzioni. | |
• Nomi di variabili di libreria: | Che nomi è meglio dare alle variabili private globali nelle funzioni di libreria | |
• Funzioni di tipo generale: | Funzioni di uso generale. | |
• Funzione strtonum: | Da usare se non è disponibile la
funzione predefinita
strtonum() .
| |
• Funzione assert: | Una funzione per controllare
affermazioni in programmi
awk .
| |
• Funzione round: | Una funzione per eseguire
arrotondamenti se sprintf()
non lo fa correttamente.
| |
• Funzione random Cliff: | Il generatore Cliff di numeri casuali. | |
• Funzioni ordinali: | Funzioni per usare caratteri come numeri e viceversa. | |
• Funzione join: | Una funzione per raccogliere un vettore in una stringa. | |
• Funzione getlocaltime: | Una funzione per ottenere data e ora nel formato desiderato. | |
• Funzione readfile: | Una funzione per leggere un file intero in un colpo solo. | |
• Apici alla shell: | Una funzione per passare stringhe con apici alla shell. | |
• Gestione File Dati: | Funzioni for gestire file dati specificati sulla riga di comando, | |
• Funzione filetrans: | Una funzione per gestire il passaggio da un file in input al successivo. | |
• Funzione rewind: | Una funzione per rileggere il file di input. | |
• Controllo di file: | Controllare che i file in input siano accessibili. | |
• File vuoti: | Controllare se i file in input sono vuoti. | |
• Ignorare assegnamenti di variabili: | Trattare assegnamenti di variabili come nomi di file. | |
• Funzione getopt: | Una funzione per trattare argomenti presenti sulla riga di comando. | |
• Funzioni Passwd: | Funzioni per ottenete informazioni sull’utente [da /etc/passwd]. | |
• Funzioni Group: | Funzioni per ottenete informazioni sul gruppo [da /etc/group]. | |
• Visitare vettori: | Una funzione per visitare vettori di vettori. | |
• Sommario funzioni di libreria: | Sommario funzioni di libreria. | |
• Esercizi con le librerie: | Esercizi. | |
• Eseguire esempi: | Come eseguire i programmi di esempio. | |
• Cloni: | Cloni di programmi di utilità comuni. | |
• Programma cut: | Il programma di utilità cut .
| |
• Programma egrep: | Il programma di utilità egrep .
| |
• Programma id: | Il programma di utilità id .
| |
• Programma split: | Il programma di utilità split .
| |
• Programma tee: | Il programma di utilità tee .
| |
• Programma uniq: | Il programma di utilità uniq .
| |
• Programma wc: | Il programma di utilità wc .
| |
• Programmi vari: | Alcuni interessanti programmi in
awk
| |
• Programma dupword: | Trovare parole duplicate in un documento. | |
• Programma alarm: | Un programma di sveglia. | |
• Programma translate: | Un programma simile al comando di
utilità tr .
| |
• Programma labels: | Stampare etichette per lettere. | |
• Programma utilizzo parole: | Un programma per produrre un contatore dell’utilizzo di parole in un testo. | |
• Programma riordino diario: | Eliminare righe doppie da un file di cronologia. | |
• Programma extract: | Estrarre programmi da file sorgenti Texinfo. | |
• Programma sed semplice: | Un semplice editor di flusso. | |
• Programma igawk: | Un programma per fornire ad
awk la possibilità di
includere file.
| |
• Programma anagram: | Trovare anagrammi da una lista di parole. | |
• Programma signature: | La gente fa cose stupefacenti se ha troppo tempo libero. | |
• Sommario dei programmi: | Sommario dei programmi. | |
• Esercizi sui programmi: | Esercizi. | |
• Dati non decimali: | Consentire dati di input in base diversa da 10. | |
• Ordinamento di vettori: | Modi per controllare la visita di un vettore e il suo ordinamento. | |
• Controllare visita vettori: | Come usare PROCINFO["sorted_in"]. | |
• Funzioni di ordinamento di vettori: | Come usare asort() e
asorti() .
| |
• I/O bidirezionale: | Comunicazione nei due sensi con un altro processo. | |
• Reti TCP/IP: | Usare gawk per
programmazione di rete.
| |
• Profilare: | Profilare i propri programmi
awk .
| |
• Sommario funzionalità avanzate: | Sommario funzionalità avanzate. | |
• I18N e L10N: | Internazionalizzazione e localiz. | |
• Utilizzare gettext : | Come funziona GNU gettext .
| |
• I18N per programmatore: | Funzionalità per il programmatore. | |
• I18N per traduttore: | Funzionalità per il traduttore. | |
• Estrazione di stringhe: | Estrarre stringhe marcate. | |
• Ordinamento di printf: | Riordinare argomenti printf
[nelle stringhe da tradurre].
| |
• Portabilità nell'I18N: | Problemi di portabilità a livello di
awk .
| |
• Esempio I18N: | Un semplice esempio di internazionalizzazione. | |
• Gawk internazionalizzato: | gawk stesso è
internazionalizzato.
| |
• Sommario I18N: | Sommario sull’internazionalizzazione. | |
• Debugging: | Introduzione al debugger di
gawk .
| |
• Nozioni sul debug: | Generalità sul debug. | |
• Terminologia nel debug: | Concetti fondamentali sul debug. | |
• Debug di Awk: | Il debug di awk .
| |
• Esempio di sessione di debug: | Esempio di sessione di debug di
gawk .
| |
• Invocazione del debugger: | Come avviare il debugger. | |
• Trovare il bug: | Trovare il bug. | |
• Lista dei comandi di debug: | I principali comandi di debug. | |
• Controllo dei breakpoint: | Controllo dei punti d’interruzione. | |
• Controllo esecuzione debugger: | Controllo di esecuzione. | |
• Vedere e modificare dati: | Vedere e modificare dati. | |
• Stack di esecuzione: | Lavorare con lo stack. | |
• Informazioni sul debugger: | Ottenere informazioni sullo stato del programma e del debugger. | |
• Comandi vari del debugger: | Comandi vari del debugger. | |
• Supporto per Readline: | Supporto per Readline. | |
• Limitazioni: | Limitazioni. | |
• Sommario sul debug: | Sommario sul debug. | |
• Aritmetica del computer: | Una rapida introduzione alla matematica del computer. | |
• Definizioni matematiche: | Altre cose da sapere. | |
• Funzionalità MPFR: | Funzionalità per il calcolo a
precisione arbitraria in gawk
| |
• Cautela col calcolo in VM: | Cose da sapere. | |
• Inesattezza nei calcoli: | La matematica in virgola mobile non è esatta. | |
• Rappresentazioni inesatte: | Molti numeri non sono rappresentati esattamente. | |
• Confronti tra valori in VM: | Come confrontare valori in virgola mobile. | |
• Gli errori si sommano: | Gli errori diventano sempre maggiori. | |
• Ottenere la precisione: | Ottenere la precisione voluta. | |
• Tentare di arrotondare: | Tentare di aggiungere bit di precisione e arrotondare. | |
• Impostare la precisione: | Impostare la precisione. | |
• Impostare modi di arrotondare: | Impostare la modalità di arrotondamento. | |
• Interi a precisione arbitraria: | Aritmetica dei numeri interi a precisione
arbitraria con gawk .
| |
• Problemi virgola mobile POSIX: | Confronto tra standard e uso corrente. | |
• Sommario virgola mobile: | Sommario della trattazione della virgola mobile. | |
• Introduzione alle estensioni: | Cos’è un’estensione. | |
• Licenza delle estensioni: | tipo di licenza delle estensioni. | |
• Panoramica sul meccanismo delle estensioni: | Come funziona a grandi linee. | |
• Descrizione dell'API delle estensioni: | Una descrizione completa dell’API. | |
• Intro funzioni API delle estensioni: | Introduzione alle funzioni dell’API. | |
• Tipi di dati generali: | I tipi di dati. | |
• Funzioni di allocazione memoria: | Funzioni per allocare memoria. | |
• Funzioni di costruzione: | Funzioni per creare valori. | |
• Funzioni di registrazione: | Funzioni per registrare cose con
gawk .
| |
• Funzioni di estensione: | Registrare funzioni di estensione. | |
• Funzioni di exit callback: | Registrare una exit di callback. | |
• Stringa di versione Estensioni: | Registrare una stringa di versione. | |
• Analizzatori di input: | Registrare un analizzatore di input. | |
• Processori di output: | Registrare un processore di output. | |
• Processori bidirezionali: | Registrare un processore bidirezionale. | |
• Stampare messaggi: | Stampare messaggi dalle estensioni. | |
• Aggiornare ERRNO : | Funzioni per aggiornare ERRNO .
| |
• Richiedere valori: | Come ottenere un valore. | |
• Accedere ai parametri: | Funzioni per acceder ai parametri. | |
• Accedere alla tabella simboli: | Funzioni per accedere alle variabili globali. | |
• Tabella simboli per nome: | Accedere e aggiornare variabili per nome. | |
• Tabella simboli tramite cookie: | Accedere alle variabili per “cookie”. | |
• Valori nascosti: | Creare e usare valori nascosti. | |
• Manipolazione di vettori: | Funzioni per lavorare coi vettori. | |
• Tipi di dati per i vettori: | Tipi dati per lavorare coi vettori. | |
• Funzioni per i vettori: | Funzioni per lavorare coi vettori. | |
• Appiattimento di vettori: | Come appiattire i vettori. | |
• Creazione di vettori: | Come creare e popolare vettori. | |
• Ridirezione API: | Come accedere alla ridirezioni e modificarle. | |
• Variabili dell'estensione API: | Variabili fornite dall’API. | |
• Versione dell'estensione: | Informazioni sulla versione API. | |
• Variabili informative di estens. API: | Variabili che forniscono informazioni
sull’invocazione di gawk .
| |
• Codice predefinito di un'estensione API: | Codice predefinito di interfaccia API. | |
• Modifiche dalla versione API 1: | Modifiche dalla versione 1 dell’API. | |
• Trovare le estensioni: | Come gawk trova le
estensioni compilate.
| |
• Esempio di estensione: | Esempio di codice C di un’estensione. | |
• Descrizione interna file: | Quello che le nuove funzioni faranno. | |
• Operazioni interne file: | Codice per gestire file all’interno. | |
• Usare operazioni interne file: | Come usare un’estensione esterna. | |
• Esempi di estensione: | Le estensioni di esempio incluse con
gawk .
| |
• Esempio di estensione funzioni file: | Funzioni relative ai file. | |
• Esempio di estensione Fnmatch: | Un’interfaccia a fnmatch() .
| |
• Esempio di estensione Fork: | Un’interfaccia a fork() e
altre funzioni di processo.
| |
• Esempio di estensione Inplace: | Consentire modifica file input nell’estensione. | |
• Esempio di estensione Ord: | Conversioni di caratteri in valori numerici e viceversa. | |
• Esempio di estensione Readdir: | Un’interfaccia a readdir() .
| |
• Esempio di estensione Revout: | Invertire la stringa in output. | |
• Esempio di estensione Rev2way: | Esempio di I/O bidirezionale. | |
• Esempio di estensione Rwarray: | Scaricare e ricaricare un vettore. | |
• Esempio di estensione Readfile: | Leggere un intero file in una stringa. | |
• Esempio di estensione Time: | Un’interfaccia a gettimeofday()
e sleep() .
| |
• Esempio di estensione API Test: | Test per la API. | |
• gawkextlib: | Il progetto gawkextlib .
| |
• Sommario delle estensioni: | Sommario delle estensioni. | |
• Esercizi sulle estensioni: | Esercizi. | |
• V7/SVR3.1: | Le principali differenze tra V7 e System V Release 3.1. | |
• SVR4: | Differenze minori tra System V Release 3.1 e 4. | |
• POSIX: | Nuove funzionalità per lo standard POSIX. | |
• BTL: | Nuove funzionalità dalla versione
di awk di Brian Kernighan.
| |
• POSIX/GNU: | Le estensioni in gawk non
previste in awk POSIX.
| |
• Storia delle funzionalità: | Storia delle funzionalità di
gawk .
| |
• Estensioni comuni: | Sommario Estensioni comuni. | |
• Intervalli e localizzazione: | Come le localizzazioni influiscono sugli intervalli delle espressioni regolari. | |
• Contributori: | I maggiori contributori a
gawk .
| |
• Sommario della storia: | Sommario della storia. | |
• Distribuzione di Gawk: | Contenuto della distribuzione di
gawk .
| |
• Scaricare: | Come ottenere la distribuzione. | |
• Scompattazione: | Come estrarre la distribuzione. | |
• Contenuti della distribuzione: | Cosa c’è nella distribuzione. | |
• Installazione Unix: | Installare gawk su
varie versioni di Unix.
| |
• Installazione veloce: | Compilare gawk sotto Unix.
| |
• File da usare a inizio sessione: | Funzioni di personalizzazione shell. | |
• Ulteriori opzioni di configurazione: | Altre opzioni utilizzabili in fase di compilazione. | |
• Filosofia della configurazione: | Come si suppone che funzioni. | |
• Installazione non-Unix: | Installazioni su altri Sistemi Operativi. | |
• Installazione su PC: | Installare e compilare
gawk su Microsoft Windows.
| |
• Installazione binaria su PC: | Installare una distribuzione pronta all’uso. | |
• Compilazione su PC: | Compilare gawk per
Windows32.
| |
• Uso su PC: | Eseguire gawk su Windows32.
| |
• Cygwin: | Compilare ed eseguire gawk
per Cygwin.
| |
• MSYS: | Usare gawk nell’ambiente
MSYS.
| |
• Installazione su VMS: | Installare gawk su VMS.
| |
• Compilazione su VMS: | Come compilare gawk su
VMS.
| |
• Estensioni dinamiche su VMS: | Compilare estensioni dinamiche
di gawk su VMS.
| |
• Dettagli installazione su VMS: | Come installare gawk su
VMS.
| |
• Esecuzione su VMS: | Come eseguire gawk su VMS.
| |
• GNV su VMS: | Il progetto GNV di VMS. | |
• Vecchio Gawk su VMS: | Una versione non aggiornata arriva con alcune versioni di VMS. | |
• Bug: | Notificare problemi e bug. | |
• Indirizzo Bug: | Dove notificare problemi. | |
• Usenet: | Dove non notificare problemi. | |
• Manutentori: | Manutentori di version non-*nix. | |
• Altre versioni: | Altre implementazioni di
awk liberamente
disponibili.
| |
• Sommario dell'installazione: | Sommario dell’installazione. | |
• Modalità di compatibilità: | Come inibire alcune estensioni di
gawk .
| |
• Aggiunte: | Fare aggiunte a gawk .
| |
• Accedere ai sorgenti: | Accedere al deposito sorgenti Git. | |
• Aggiungere codice: | Aggiungere codice al programma
principale gawk .
| |
• Nuovi sistemi: | Rendere disponibile gawk
a un nuovo sistema operativo.
| |
• File derivati: | Perché i file ancillari sono tenuti
nel deposito git .
| |
• Future estensioni: | Nuove funzionalità che potranno essere implementate in futuro. | |
• Limitazioni dell'implementazione: | Alcune limitazioni dell’implementazione. | |
• Progetto delle estensioni: | Note di progetto sull’estensione API. | |
• Problemi con le vecchie estensioni: | Problemi con la precedente implementazione di estensioni. | |
• Obiettivi delle estensioni: | Obiettivi del nuovo meccanismo. | |
• Altre scelte progettuali per le estensioni: | Qualche altra scelta progettuale. | |
• Futuri sviluppi delle estensioni: | Possibilità di crescita futura. | |
• Meccanismo delle vecchie estensioni: | Problemi con le vecchie estensioni. | |
• Sommario delle note: | Sommario delle note di implementazione. | |
• Fondamenti ad alto livello: | Una visione dall’alto. | |
• Fondamenti sui tipi di dati: | Una velocissima introduzione ai tipi di dati. |
awk
awk
awk
e gawk
awk
gawk
gawk
gawk
getline
getline
senza argomentigetline
in una variabilegetline
da un filegetline
in una variabile da un filegetline
da una pipegetline
in una variabile da una pipegetline
da un coprocessogetline
in una variabile da un coprocessogetline
getline
print
print
print
printf
per stampe sofisticate
print
e printf
gawk
awk
awk
awk
awk
awk
awk
con gawk
gawk
gawk
awk
gawk
gawk
gawk
gawk
ERRNO
gawk
trova le estensioni compilategawk
fnmatch()
fork()
, wait()
, e waitpid()
ord()
e chr()
gawkextlib
awk
awk
awk
di Brian Kernighangawk
non in POSIX awk
gawk
gawk
gawk
gawk
gawk
su sistemi di tipo Unix
awk
liberamente disponibiliSuccessivo: Introduzione4, Precedente: Top, Su: Top [Contenuti][Indice]
Arnold Robbins e io siamo buoni amici. Ci siamo conosciuti
nel 1990 per un insieme di
circostanze—e per il nostro linguaggio di programmazione preferito, AWK.
Tutto era iniziato un paio d’anni prima.
Avevo appena iniziato un nuovo lavoro e avevo notato un computer Unix scollegato
che giaceva in un angolo.
Nessuno sapeva come usarlo, tanto meno io. Comunque,
qualche giorno più tardi, stava funzionando, con
me come root
e solo e unico utente.
Quel giorno, iniziai la transizione da statistico a programmatore Unix.
In uno dei miei giri per biblioteche e librerie alla ricerca di libri sullo Unix, trovai il libro, dalla copertina grigia, su AWK, noto anche come Alfred V. Aho, Brian W. Kernighan e Peter J. Weinberger, The AWK Programming Language, (Addison-Wesley, 1988). Il semplice paradigma di programmazione di AWK —trovare un’espressione di ricerca nell’input e di conseguenza compiere un’azione—riduceva spesso complesse e tediose manipolazioni di dati a poche righe di codice. Ero entusiasta di cimentarmi nella programmazione in AWK.
Ahimè, l’awk
sul mio computer era una versione limitata del
linguaggio descritto nel libro grigio. Scoprii che il mio computer aveva il
“vecchio awk
” mentre il libro descriveva il “nuovo
awk
.”
Imparai che non era un caso isolato; la vecchia versione si rifiutava di farsi da
parte o di cedere il suo nome. Se un sistema aveva un nuovo awk
,
questo era chiamato invariabilmente nawk
, e pochi sistemi lo avevano.
Il miglior modo per ottenere un nuovo awk
era quello di scaricare via
ftp
il codice sorgente di gawk
da prep.ai.mit.edu
.
gawk
era una versione del nuovo awk
scritta da David Trueman
e Arnold, e disponibile sotto la GNU General Public License.
Per inciso, ora non è
più così difficile trovare un nuovo awk
. gawk
viene
fornito con GNU/Linux, e si possono scaricare i binari e il codice sorgente per
quasi tutti i sistemi; mia moglie usa gawk
nella sua stazione di lavoro VMS.
Il mio sistema Unix non era inizialmente collegato a una presa di corrente; a
maggior ragione non era collegato a una rete. Così, ignaro dell’esistenza di
gawk
e in generale della comunità di Unix, e desiderando un nuovo
awk
, ne scrissi uno mio, chiamato mawk
. Prima di aver
finito, scoprii l’esistenza di gawk
,
ma era troppo tardi per fermarmi, così alla fine inviai un messaggio
a un newsgroup comp.sources
.
Qualche giorno dopo ricevetti un cordiale messaggio di posta elettronica
da Arnold che si presentava.
Propose di scambiarci progetti e algoritmi, e
allegò una bozza dello standard POSIX, che mi permise di
aggiornare mawk
per includere le estensioni al linguaggio
aggiunte dopo la pubblicazione di The AWK Programming Language.
Francamente, se i nostri ruoli fossero stati invertiti, io non sarei stato così disponibile e probabilmente non ci saremmo mai incontrati. Sono felice che l’incontro sia avvenuto. Lui è un vero esperto tra gli esperti di AWK e una persona squisita. Arnold mette a disposizione della Free Software Foundation parti significative della sua esperienza e del suo tempo.
Questo libro è il manuale di riferimento di gawk
, ma sostanzialmente
è un libro sulla programmazione in AWK che
interesserà un vasto pubblico.
È un riferimento completo al linguaggio AWK come definito dalla versione del
1987 di Bell Laboratories e codificato nelle POSIX Utilities
standard del 1992.
D’altra parte, un programmatore AWK alle prime armi può studiare
una quantità di programmi pratici che permettono di apprezzare
la potenza dei concetti di base di AWK:
flusso di controllo guidato dai dati, ricerca di corrispondenze tramite
espressioni regolari e vettori associativi.
Chi desidera qualcosa di nuovo può provare l’interfaccia di gawk
verso i protocolli di rete attraverso i file speciali /inet.
I programmi in questo libro evidenziano come un programma AWK sia generalmente molto più piccolo e veloce da sviluppare di uno equivalente scritto in C. Di conseguenza, è spesso conveniente creare un prototipo di un algoritmo o di un progetto in AWK per arrivare a eseguirlo in breve tempo e scoprire prima i problemi che possono presentarsi. Spesso, l’efficienza di questa versione iniziale interpretata è sufficiente e il prototipo AWK diventa il prodotto finale.
Il nuovo comando pgawk
(profiling gawk
) produce
conteggi sull’esecuzione delle istruzioni del programma.
Recentemente ho fatto un tentativo con un algoritmo che, a fronte di
n
righe di input, produceva il risultato in un tempo
~ C n^2,
mentre in teoria
avrebbe dovuto terminare in un tempo
~ C n log n.
Dopo qualche minuto di attenta lettura
del profilo in awkprof.out, ho ricondotto il problema a
una singola riga di codice. pgawk
è una gradita integrazione
ai miei strumenti di programmatore.
Arnold ha condensato in questo libro oltre un decennio di esperienza nell’uso di
programmi AWK e nello sviluppo di gawk
. Se si vuole usare
AWK o imparare ad usarlo, è consigliabile leggere questo libro.
Michael Brennan
Autore di mawk
Marzo 2001
Successivo: Prefazione, Precedente: Introduzione3, Su: Top [Contenuti][Indice]
Ci sono cose che non cambiano. Tredici anni fa scrivevo: “Se si vuole usare AWK o imparare ad usarlo, è consigliabile leggere questo libro.” Era vero allora e rimane vero anche oggi.
Imparare a usare un linguaggio di programmazione richiede qualcosa di più che padroneggiarne la sintassi. Occorre comprendere come usare le funzionalità del linguaggio per risolvere problemi pratici di programmazione. Uno dei punti più importanti di questo libro è che fornisce molti esempi che mostrano come utilizzare AWK.
Altre cose, invece, cambiano. I nostri computer sono diventati molto più veloci e la loro memoria è molto più estesa. Per questa ragione, la velocità di esecuzione e l’uso efficiente della memoria, caratteristiche di un linguaggio di livello elevato, hanno minore rilevanza. Scrivere un programma prototipo in AWK per poi riscriverlo in C per migliorare l’utilizzo delle risorse capita sempre meno, perché sempre più spesso il prototipo è abbastanza veloce anche per essere messo in produzione.
Naturalmente, ci sono tipi di calcoli che sono effettuati più agevolmente
da programmi scritti in C o C++.
Con gawk
4.1 e successive versioni, non è necessario
decidere se scrivere un programma in AWK oppure in C/C++. Si può scrivere
buona parte del programma in AWK e le parti che richiedono
specificamente il C/C++ possono essere scritte in C/C++ e quindi il tutto
può essere eseguito come un programma unico, con il modulo gawk
che carica dinamicamente il modulo C/C++ in fase di esecuzione.
Estensioni dinamiche,
spiega la procedura in gran
dettaglio, e, come prevedibile, riporta molti esempi che sono di aiuto per
approfondire anche gli aspetti più complessi.
È per me un piacere programmare in AWK ed è stato divertente (ri)leggere questo libro. Penso che sarà lo stesso per voi.
Michael Brennan
Autore di mawk
Ottobre 2014
Successivo: Per iniziare, Precedente: Introduzione4, Su: Top [Contenuti][Indice]
Lavorando con file di testo capita di dover eseguire alcuni tipi ripetitivi di
operazioni. Si potrebbe voler estrarre alcune righe e scartare il resto, o fare
modifiche laddove siano verificate certe condizioni, lasciando inalterato il
resto del file. Questi compiti risultano spesso più agevoli usando
awk
. Il programma di utilità awk
interpreta un linguaggio
di programmazione specializzato che rende facile eseguire semplici attività
di riformattazione di dati.
L’implementazione GNU di awk
è chiamata gawk
; se
invocato con le opzioni o con le variabili d’ambiente appropriate,
(vedi Opzioni), è pienamente
compatibile con le specifiche
POSIX1
del linguaggio awk
e con la versione Unix di awk
mantenuta
da Brian Kernighan.
Ciò implica che tutti i programmi
awk
scritti correttamente dovrebbero funzionare con gawk
.
Perciò nella maggior parte dei casi non si distingue tra gawk
e
altre implementazioni di awk
.
Usando awk
potete:
Inoltre,
gawk
fornisce strumenti che rendono facile:
awk
.
Questo Documento spiega il linguaggio awk
e come lo si può
usare efficacemente. È richiesta una familiarità coi comandi di sistema
di base, come cat
e ls
,2 così come con le funzionalità di base della shell, come la
ridirezione, l’input/output (I/O) e le pipe.
Implementazioni del linguaggio awk
sono disponibili per diversi
sistemi operativi di computer. Questo Documento, oltre a descrivere il
linguaggio awk
in generale, descrive anche la specifica
implementazione di awk
chiamata gawk
(che sta per
“GNU awk
”). gawk
funziona su una vasta gamma di sistemi
Unix, dai PC basati su architettura Intel fino
a sistemi di potenza molto maggiore.
gawk
è stato portato anche su Mac OS X,
Microsoft Windows
(tutte le versioni),
e OpenVMS.3
• Storia: | La storia di gawk e
awk .
| |
• Nomi: | Che nome usare per trovare
awk .
| |
• Questo manuale: | Uso di questo Documento. Comprende esempi di file in input utilizzabili. | |
• Convenzioni: | Convenzioni tipografiche. | |
• Storia del manuale: | Breve storia del Progetto GNU e di questo Documento. | |
• Come contribuire: | Un aiuto per la salvezza del mondo. | |
• Ringraziamenti: | Ringraziamenti. |
Successivo: Nomi, Su: Prefazione [Contenuti][Indice]
gawk
e awk
Ricetta per un linguaggio di programmazione
Mescolare bene tutte le parti usando Dopo otto anni, aggiungere un’altra parte di |
Il nome awk
deriva dalle iniziali dei suoi progettisti: Alfred V.
Aho, Peter J. Weinberger e Brian W. Kernighan. La versione originale di
awk
fu scritta nel 1977 negli AT&T Bell Laboratories.
Nel 1985, una nuova versione rese il linguaggio di programmazione
più potente, introducendo le funzioni definite dall’utente, flussi di input
multipli ed espressioni regolari calcolate.
Questa nuova versione ebbe larga diffusione con Unix System V
Release 3.1 (1987).
La versione in System V Release 4 (1989) ha aggiunto alcune nuove funzionalità
e ha fatto pulizia nel comportamento di alcuni degli “punti oscuri” del
linguaggio. Le specifiche per awk
nello standard POSIX Command
Language and Utilities ha in seguito reso più chiaro il linguaggio. Sia i
progettisti di gawk
che quelli dell’originale awk
dei Bell
Laboratories hanno collaborato alla formulazione delle specifiche POSIX.
Paul Rubin ha scritto gawk
, nel 1986.
Jay Fenlason l’ha completata, seguendo i consigli di Richard Stallman.
Anche John Woods ha fornito parti del codice. Nel 1988 e 1989, David Trueman,
col mio aiuto, ha rivisto completamente gawk
per la compatibilità
col più recente awk
.
Intorno al 1994, sono divenuto il manutentore principale.
Lo sviluppo corrente è incentrato sulla correzione degli errori, sul
miglioramento delle prestazioni, sulla conformità agli standard e,
occasionalmente, su nuove funzionalità.
Nel maggio 1997, Jürgen Kahrs avvertì la necessità di un accesso alla
rete da awk
, e con un piccolo aiuto da parte mia, cominciò ad
aggiungere funzionalità a gawk
per fare questo. A quel tempo,
lui scrisse anche il grosso di
TCP/IP Internetworking with gawk
(un documento separato, disponibile come parte della distribuzione
gawk
). Il suo codice alla fine venne integrato nella distribuzione
principale di gawk
con la versione 3.1 di gawk
.
John Haque ha riscritto la parte interna di gawk
, mentre metteva a
punto un debugger a livello di awk
. Questa versione divenne
disponibile come gawk
versione 4.0 nel 2011.
Vedi Contributori
per un elenco completo di quelli che hanno fornito contributi importanti a
gawk
.
Successivo: Questo manuale, Precedente: Storia, Su: Prefazione [Contenuti][Indice]
Il linguaggio awk
si è evoluto nel corso degli anni. Tutti i
dettagli si trovano in Storia del linguaggio.
Il linguaggio descritto in questo Documento
viene spesso citato come “nuovo awk
”.
Per analogia, la versione originale di awk
è citata
come “vecchio awk
.”
Su molti sistemi di uso corrente, eseguendo il programma di utilità
awk
, si invoca qualche versione del nuovo
awk
.4 Se
il comando awk
nel sistema in uso è il vecchio, il
risultato che vedrete per il programma di test che segue è
del tipo:
$ awk 1 /dev/null error→ awk: syntax error near line 1 error→ awk: bailing out near line 1
Se questo è il caso, dovreste cercare una versione del nuovo awk
,
o semplicemente installare gawk
!
All’interno di questo Documento, quando si fa riferimento a
funzionalità del linguaggio che dovrebbe essere disponibile in ogni
implementazione completa di awk
POSIX, viene usato il termine
awk
. Quando si fa riferimento a una funzionalità specifica
dell’implementazione GNU, viene usato i termine gawk
.
Successivo: Convenzioni, Precedente: Nomi, Su: Prefazione [Contenuti][Indice]
Il termine awk
si riferisce sia a uno specifico programma sia al
linguaggio che si usa per dire al programma stesso cosa deve fare. Quando dobbiamo
essere precisi, chiamiamo il linguaggio “il linguaggio awk
,”
e il programma “l’utilità awk
.”
Questo Documento spiega
sia come scrivere programmi nel linguaggio awk
che come
eseguire l’utilità awk
.
Il termine “programma awk
” si riferisce a un programma scritto
dall’utente nel linguaggio di programmazione awk
.
In primo luogo, questo Documento spiega le funzionalità di awk
come definite nello standard POSIX, e lo fa nel contesto dell’implementazione
gawk
. Oltre a questo, cerca anche di descrivere le differenze
significative tra gawk
e altre
implementazioni awk
.5
Infine, vien fatta rilevare ogni funzionalità di gawk
non
inclusa nello standard POSIX per awk
.
Questo Documento ha il difficile compito di essere tanto una guida introduttiva che un manuale di riferimento. I neofiti possono tranquillamente saltare i dettagli che sembrano loro troppo complessi. Possono anche ignorare i molti riferimenti incrociati, preparati avendo in mente gli utenti esperti e per le versioni Info e HTML del Documento.
Ci sono dei riquadri sparsi in tutto il Documento. Aggiungono una spiegazione più completa su punti importanti, ma che probabilmente non sono di interesse in sede di prima lettura. Si trovano tutti nell’indice analitico, alla voce “sidebar.”
La maggior parte delle volte, gli esempi usano programmi awk
completi.
Alcune delle sezioni più avanzate mostrano solo la parte del programma
awk
che illustra il concetto che si sta descrivendo.
Sebbene questo Documento sia destinato soprattutto alle persone che non
hanno una precedente conoscenza di awk
, esso contiene anche tante
informazioni che anche gli esperti di awk
troveranno utili.
In particolare, dovrebbero essere d’interesse la descrizione di POSIX
awk
e i programmi di esempio
in
Funzioni di libreria e
in
Programmi di esempio.
Questo Documento è suddiviso in diverse parti, come segue:
awk
e il programma gawk
nel dettaglio.
Inizia con le nozioni di base, e continua con tutte le caratteristiche di
awk
. Contiene i seguenti capitoli:
awk
.
gawk
, il significato delle sue
opzioni da riga di comando e come trovare i file sorgenti del programma
awk
.
awk
POSIX e gawk
.
awk
legge i dati inseriti dall’utente.
Introduce i concetti di record e campi, e anche il
comando getline
.
Contiene una prima descrizione della ridirezione I/O, e una breve descrizione
dell’I/O di rete.
awk
possono produrre output con
print
e printf
.
awk
e
gawk
.
awk
: il vettore associativo.
Vengono trattati anche l’eliminazione di elementi del vettore e di interi
vettori, e l’ordinamento dei vettori in gawk
.
Il capitolo descrive inoltre come gawk
fornisce vettori di
vettori.
awk
e
gawk
, e spiega come definire funzioni personalizzate. Viene
anche spiegato come gawk
permetta di invocare funzioni in
maniera indiretta.
awk
e gawk
per la
risoluzione di problemi. Qui ci sono molti programmi da leggere e da cui imparare.
Questa parte contiene i seguenti capitoli:
awk
.
awk
di esempio.
La lettura di questi due capitoli permette di capire come
awk
può risolvere problemi pratici.
gawk
.
Contiene i seguenti capitoli:
awk
.
gawk
.
gawk
scrivendo estensioni in C o C++.
gawk
e a questo
Documento. Contiene le seguenti appendici:
awk
dalla sua prima versione
fino a oggi. Descrive anche come gawk
ha acquisito nuove funzionalità col passare del tempo.
gawk
, come compilarlo
sui sistemi compatibili con POSIX,
e come compilarlo e usarlo su diversi sistemi
non conformi allo standard POSIX. Spiega anche come segnalare gli errori
di gawk
e dove si possono ottenere altre implementazioni
di awk
liberamente disponibili.
gawk
,
come contribuire scrivendo del nuovo codice per gawk
,
e alcune possibili direzioni per il futuro sviluppo di gawk
.
Il Glossario, definisce quasi tutti i termini significativi usati all’interno di questo Documento. Se si incontrano termini coi quali non si ha familiarità, questo è il posto dove cercarli.
gawk
e a questo Documento.
Successivo: Storia del manuale, Precedente: Questo manuale, Su: Prefazione [Contenuti][Indice]
Questo Documento è scritto in Texinfo, il linguaggio di formattazione della documentazione GNU. Viene usato un unico file sorgente Texinfo per produrre sia la versione a stampa della documentazione sia quella online. A causa di ciò, le convenzioni tipografiche sono leggermente diverse da quelle presenti in altri libri che potete aver letto.
Gli esempi da immettere sulla riga di comando sono preceduti dai comuni prompt di shell primario e secondario, ‘$’ e ‘>’. L’input che si inserisce viene mostrato in questo modo. L’output del comando è preceduto dal glifo “-|”, che in genere rappresenta lo standard output del comando. Messaggi di errore e altri output sullo standard error del comando sono preceduti dal glifo “error→”. Per esempio:
$ echo ciao su stdout -| ciao su stdout $ echo salve su stderr 1>&2 error→ salve su stderr
Nel testo, quasi tutto ciò che riguarda la programmazione,
per esempio i nomi dei comandi,
appare in questo font
. I frammenti
di codice appaiono nello stesso font e tra apici, ‘in questo modo’.
Ciò che viene sostituito dall’utente o dal programmatore
appare in questo font.
Le opzioni sono stampate così: -f.
I nomi-file sono indicati in questo modo: /percorso/al/file.
Certe cose sono
evidenziate in questo modo, e se un punto dev’essere reso in modo più
marcato, viene evidenziato in questo modo.
La prima occorrenza di un
nuovo termine è usualmente la sua definizione e appare nello stesso
font della precedente occorrenza di “definizione” in questa frase.
I caratteri che si battono sulla tastiera sono scritti come questi. In particolare, ci sono caratteri speciali chiamati “caratteri di controllo”. Questi sono caratteri che vengono battuti tenendo premuti il tasto CONTROL e un altro tasto contemporaneamente. Per esempio, Ctrl-d è battuto premendo e tenendo premuto il tasto CONTROL, poi premendo il tasto d e infine rilasciando entrambi i tasti.
Per amor di brevità, in questo Documento, la versione di Brian
Kernighan di awk
sarà citata come “BWK awk
.”
(Vedi Altre versioni per informazioni su questa e altre versioni.)
Gli angoli bui sono essenzialmente frattali—per quanto vengano illuminati, ce n’è sempre uno più piccolo e più buio.
Fino allo standard POSIX (e GAWK: Programmare efficacemente in AWK),
molte caratteristiche di awk
erano poco documentate o
non documentate affatto. Le descrizioni di queste caratteristiche
(chiamate spesso “angoli bui”) sono segnalate in questo Documento con
“(a.b.)”.
Appaiono anche nell’indice sotto la voce “angolo buio.”
Ma come osservato nella citazione d’apertura, ogni trattazione degli angoli bui è per definizione incompleta.
Estensioni al linguaggio standard di awk
disponibili in più di una
implementazione di awk
sono segnate
“(e.c.),” ed elencate nell’indice sotto “estensioni comuni”
e “comuni, estensioni”.
Successivo: Come contribuire, Precedente: Convenzioni, Su: Prefazione [Contenuti][Indice]
La Free Software Foundation (FSF) è un’organizzazione senza scopo di lucro dedita alla produzione e distribuzione di software liberamente distribuibile. È stata fondata da Richard M. Stallman, l’autore della prima versione dell’editor Emacs. GNU Emacs è oggi la versione di Emacs più largamente usata.
Il Progetto GNU6
è un progetto della Free Software
Foundation in continuo sviluppo per creare un ambiente per computer completo, liberamente
distribuibile, conforme allo standard POSIX.
La FSF usa la GNU General Public License (GPL) per assicurare che
il codice sorgente del loro software sia sempre
disponibile all’utente finale.
Una copia della GPL è inclusa
in questo Documento
per la consultazione
(vedi Copia).
La GPL si applica al codice sorgente in linguaggio C per gawk
.
Per saperne di più sulla FSF e sul Progetto GNU,
si veda la pagina principale del Progetto GNU.
Questo Documento si può leggere anche dal
sito di GNU.
Una shell, un editor (Emacs), compilatori ottimizzanti C, C++ e
Objective-C altamente portabili, un debugger simbolico e dozzine di grandi e
piccoli programmi di utilità (come gawk
), sono stati completati e
sono liberamente disponibili. Il kernel del sistema operativo GNU (noto come
HURD), è stato rilasciato ma è ancora allo stato di sviluppo iniziale.
In attesa che il sistema operativo GNU venga più completatamente sviluppato, si dovrebbe prendere in considerazione l’uso di GNU/Linux, un sistema operativo liberamente distribuibile e basato su Unix disponibile per Intel, Power Architecture, Sun SPARC, IBM S/390, e altri sistemi.7 Molte distribuzioni GNU/Linux sono scaricabili da internet.
Il Documento è realmente libero—almeno, l’informazione che contiene
è libera per chiunque—. Il codice sorgente del Documento, leggibile
elettronicamente, viene fornito con gawk
.
(Dare un’occhiata alla Free Documentation
License in Licenza per Documentazione Libera GNU (FDL).)
Il Documento in sé ha già avuto parecchie edizioni in passato.
Paul Rubin ha scritto la prima bozza di The GAWK Manual;, che era
lunga una quarantina di pagine.
Diane Close e Richard Stallman l’hanno migliorata arrivando alla
versione che era
lunga una novantina di pagine, e descriveva solo la versione originale
“vecchia” di awk
.
Ho iniziato a lavorare con quella versione nell’autunno del 1988.
Mentre ci stavo lavorando,
la FSF ha pubblicato parecchie versioni preliminari, numerate 0.x).
Nel 1996, l’edizione 1.0 fu rilasciata assieme a gawk
3.0.0.
La FSF ha pubblicato le prime due edizioni col
titolo GAWK: The GNU Awk User’s Guide.
Questa edizione mantiene la struttra di base delle edizioni precedenti.
Per l’edizione FSF 4.0, il contenuto era stato accuratamente rivisto
e aggiornato. Tutti i riferimenti a versioni di gawk
anteriori alla
versione 4.0 sono stati eliminati.
Di particolare interesse in quella edizione era l’aggiunta del Debugger.
Per l’edizione FSF 4.1, il contenuto è stato riorganizzato in parti, e le aggiunte più importanti sono Calcolo con precisione arbitraria, e Estensioni dinamiche.
Questo Documento continuerà certamente ad evolversi. Se si trovano errori nel Documento, si prega di segnalarli! Vedi Bug per informazioni su come inviare le segnalazione di problemi elettronicamente.
Successivo: Ringraziamenti, Precedente: Storia del manuale, Su: Prefazione [Contenuti][Indice]
Come manutentore di GNU awk
, un tempo pensai che sarei stato in grado
di gestire una raccolta di programmi awk
pubblicamente disponibili e
avevo anche esortato a collaborare. Rendere disponibili le cose su Internet
aiuta a contenere la distribuzione gawk
entro dimensioni gestibili.
L’iniziale raccolta di materiale, come questo, è tuttora disponibile su ftp://ftp.freefriends.org/arnold/Awkstuff.
Chi fosse seriamente interessato a contribuire nell’implementazione
di un sito Internet dedicato ad argomenti riguardanti il
linguaggio awk
, è pregato di contattarmi.
Precedente: Come contribuire, Su: Prefazione [Contenuti][Indice]
La bozza iniziale di The GAWK Manual riportava i seguenti ringraziamenti:
Molte persone devono essere ringraziate per la loro assistenza nella produzione di questo manuale. Jay Fenlason ha contribuito con molte idee e programmi di esempio. Richard Mlynarik e Robert Chassell hanno fatto utili osservazioni sulle bozze di questo manuale. Lo scritto A Supplemental Document for AWK di John W. Pierce, del Chemistry Department di UC San Diego, fa il punto su diverse questioni rilevanti sia per l’implementazione di
awk
che per questo manuale, che altrimenti ci sarebbero sfuggite.
Vorrei ringraziare Richard M. Stallman, per la sua visione di un mondo migliore e per il suo coraggio nel fondare la FSF e nel dare inizio al Progetto GNU.
Edizioni precedenti di questo Documento riportavano i seguenti ringraziamenti:
Le seguenti persone (in ordine alfabetico) hanno inviato commenti utili riguardo alle diverse versioni di questo libro: Rick Adams, Dr. Nelson H.F. Beebe, Karl Berry, Dr. Michael Brennan, Rich Burridge, Claire Cloutier, Diane Close, Scott Deifik, Christopher (“Topher”) Eliot, Jeffrey Friedl, Dr. Darrel Hankerson, Michal Jaegermann, Dr. Richard J. LeBlanc, Michael Lijewski, Pat Rankin, Miriam Robbins, Mary Sheehan, e Chuck Toporek.
Robert J. Chassell ha dato preziosissimi consigli sull’uso di Texinfo. Merita anche un particolare ringraziamento per avermi convinto a non dare a questo Documento il titolo How to Gawk Politely. [Un gioco di parole in inglese che può significare sia Come usare Gawk educatamente che Come curiosare educatamente]. Karl Berry ha aiutato in modo significativo con la parte TeX di Texinfo.
Vorrei ringraziare Marshall ed Elaine Hartholz di Seattle e il Dr. Bert e Rita Schreiber di Detroit per i lunghi periodi di vacanza trascorsi in tutta tranquillità in casa loro, che mi hanno permesso di fare importanti progressi nella scrittura di questo Documento e con lo stesso
gawk
.Phil Hughes di SSC ha contribuito in modo molto importante prestandomi il suo portatile col sistema GNU/Linux, non una volta, ma due, il che mi ha permesso di fare tantissimo lavoro mentre ero fuori casa.
David Trueman merita un riconoscimento speciale; ha fatto un lavoro da sentinella durante lo sviluppo di
gawk
affinché funzioni bene e senza errori. Sebbene non sia più impegnato congawk
, lavorare con lui a questo progetto è stato un vero piacere.Gli intrepidi membri della lista GNITS, con una particolare menzione per Ulrich Drepper, hanno fornito un aiuto prezioso e commenti per il progetto delle funzionalità di internazionalizzazione.
Chuck Toporek, Mary Sheehan, e Claire Cloutier della O’Reilly & Associates hanno fornito un’assistenza editoriale rilevante per questo Documento per la versione 3.1 di
gawk
.
Dr. Nelson Beebe,
Andreas Buening,
Dr. Manuel Collado,
Antonio Colombo,
Stephen Davies,
Scott Deifik,
Akim Demaille,
Daniel Richard G.,
Darrel Hankerson,
Michal Jaegermann,
Jürgen Kahrs,
Stepan Kasal,
John Malmberg,
Dave Pitts,
Chet Ramey,
Pat Rankin,
Andrew Schorr,
Corinna Vinschen,
ed Eli Zaretskii
(in ordine alfabetico)
costituiscono l’attuale “gruppo di lavoro sulla portabilità” di
gawk
. Senza il loro duro lavoro e il loro aiuto,
gawk
non sarebbe stato neanche lontanamente il buon programma che è
oggi. È stato e continua a essere un piacere lavorare con questo gruppo
di ottimi collaboratori.
Notevoli contributi di codice e documentazione sono arrivati da parecchie persone. Vedi Contributori per l’elenco completo.
Grazie a Michael Brennan per le Prefazioni.
Grazie a Patrice Dumas per il nuovo programma makeinfo
.
Grazie a Karl Berry, che continua a lavorare per tenere
aggiornato il linguaggio di marcatura Texinfo.
Robert P.J. Day, Michael Brennan e Brian Kernighan hanno gentilmente fatto da revisiori per l’edizione 2015 di questo Documento. Le loro osservazioni hanno contribuito a migliorare la stesura finale.
Vorrei ringraziare Brian Kernighan per la sua preziosa assistenza durante
la fase di collaudo e di debug di gawk
e
per l’aiuto in corso d’opera e i consigli nel chiarire diversi punti sul
linguaggio. Non avremmo proprio fatto un così buon lavoro su gawk
e
sulla sua documentazione senza il suo aiuto.
Brian è un fuoriclasse sia come programmatore che come autore di manuali tecnici. È mio dovere ringraziarlo (una volta di più) per la sua costante amicizia e per essere stato per me un modello da seguire ormai da quasi 30 anni! Averlo come revisiore è per me un privilegio eccitante, ma è stata anche un’esperienza che mi ha fatto sentire molto piccolo...
Devo ringraziare la mia meravigliosa moglie, Miriam, per la sua pazienza nel corso delle molte versioni di questo progetto, per la correzione delle bozze e per aver condiviso con me il computer. Vorrei ringraziare i miei genitori per il loro amore, e per la gentilezza con cui mi hanno cresciuto ed educato. Infine, devo riconoscere la mia gratitudine a D-o, per le molte opportunità che mi ha offerto, e anche per i doni che mi ha elargito, con cui trarre vantaggio da quelle opportunità.
Arnold Robbins
Nof Ayalon
Israel
Febbraio 2015
Successivo: Invocare Gawk, Precedente: Prefazione, Su: Top [Contenuti][Indice]
awk
awk
Il compito fondamentale di awk
è quello di ricercare righe (o
altre unità di testo) in file che corrispondano a certi criteri di ricerca.
Quando una riga corrisponde a uno dei criteri, awk
esegue su
quella riga le azioni specificate per quel criterio. awk
continua
a elaborare righe in input in questo modo, finché non raggiunge la fine delle
righe nei file in input.
I programmi scritti in awk
sono differenti dai programmi scritti
nella maggior parte degli altri linguaggi,
poiché i programmi awk
sono guidati dai dati (ovvero,
richiedono di descrivere i dati sui quali si vuole operare, e in seguito
che cosa fare una volta che tali dati siano stati individuati).
La maggior parte degli altri linguaggi sono procedurali; si deve
descrivere, in maniera molto dettagliata, ogni passo che il programma
deve eseguire. Lavorando con linguaggi procedurali, solitamente è
molto più difficile descrivere chiaramente i dati che il programma
deve elaborare.
Per questa ragione i programmi awk
sono spesso piacevolmente
facili da leggere e da scrivere.
Quando si esegue awk
, va specificato un
programma awk
che
dice ad awk
cosa fare. Il programma consiste di una serie di
regole (può anche contenere definizioni di funzioni,
una funzionalità avanzata che per ora ignoreremo;
vedi Funzioni definite dall'utente). Ogni regola specifica un
criterio di ricerca e un’azione da effettuare
una volta che viene trovato un record corrispondente.
Sintatticamente, una regola consiste in un criterio di ricerca, seguito
da una azione.
L’azione è racchiusa tra parentesi graffe per separarla dal criterio di
ricerca.
Per separare regole, basta andare a capo. Quindi un programma
awk
ha una struttura simile a questa:
criterio { azione } criterio { azione } …
• Eseguire gawk: | Come iniziare a eseguire programmi
gawk ; comprende la sintassi
della riga di comando.
| |
• File dati di esempio: | File di dati di esempio da usare nei
programmi awk illustrati in
questo Documento.
| |
• Molto semplice: | Un esempio molto semplice. | |
• Due regole: | Un esempio meno semplice di programma di una riga, che usa due regole. | |
• Maggiore sofisticazione: | Un esempio più complesso. | |
• Istruzioni/Righe: | Suddividere o riunire istruzioni su [una o più] righe. | |
• Altre funzionalità: | Altre funzionalità di awk .
| |
• Quando: | Quando usare gawk e quando
usare altre cose.
| |
• Sommario dell'introduzione: | Sommario dell’introduzione. |
Successivo: File dati di esempio, Su: Per iniziare [Contenuti][Indice]
gawk
Ci sono vari modi di eseguire un programma awk
. Se il programma è
corto, è più facile includerlo nel comando con cui si invoca awk
,
così:
awk 'programma' input-file1 input-file2 …
Quando il programma è lungo, di solito è meglio metterlo in un file ed eseguirlo con un comando come questo:
awk -f file-di-programma input-file1 input-file2 …
Questa sezione si occupa di entrambe queste modalità insieme a parecchie varianti di ciascuna di esse.
• Monouso: | Eseguire un breve programma
awk di tipo usa-e-getta.
| |
• Leggere dal terminale: | Senza uso di file in input (input immesso da tastiera). | |
• Lunghi: | Mettere programmi awk
permanenti in file.
| |
• Script eseguibili: | Preparare programmi awk
da eseguire come script.
| |
• Commenti: | Aggiungere documentazione a programmi
gawk .
| |
• Protezione: | Ulteriore discussione di problemi connessi all’uso di apici nella shell. |
Successivo: Leggere dal terminale, Su: Eseguire gawk [Contenuti][Indice]
awk
usa-e-gettaUna volta acquisita familiarità con awk
, capiterà spesso di
preparare semplici
programmi nel momento in cui servono. In questo caso si può scrivere
il programma come primo argomento del comando awk
, così:
awk 'programma' input-file1 input-file2 …
dove programma consiste in una serie di criteri di ricerca e di azioni, come descritto precedentemente.
Questo formato di comando chiede alla shell, ossia all’interpretatore
dei comandi, di richiamare awk
e di usare il programma per
trattare record nei file in input.
Il programma è incluso tra apici in modo che
la shell non interpreti qualche carattere destinato ad awk
come
carattere speciale
della shell. Gli apici fanno inoltre sì che la shell tratti tutto il
programma come un solo argomento per awk
, e permettono che
programma sia più lungo di una riga.
Questo formato è utile anche per eseguire programmi awk
di
dimensioni piccole o medie da script di shell, perché non richiede
un file separato che contenga il programma awk
. Uno script
di shell è più affidabile, perché non ci sono altri file che possono
venirsi a trovare fuori posto.
Più avanti in questo capitolo, in Molto semplice, si vedranno esempi di parecchi programmi, brevi, scritti sulla riga di comando.
Successivo: Lunghi, Precedente: Monouso, Su: Eseguire gawk [Contenuti][Indice]
Si può anche eseguire awk
senza indicare alcun file in input. Se
si immette la seguente riga di comando:
awk 'programma'
awk
prende come input del programma lo standard input,
che di solito significa qualsiasi cosa venga immesso dalla tastiera.
Ciò prosegue finché non si segnala una fine-file battendo Ctrl-d.
(In sistemi operativi non-POSIX, il carattere di fine-file può essere diverso.)
Per esempio, il seguente programma stampa un consiglio da amico (dalla Guida galattica per gli autostoppisti di Douglas Adams ), per non lasciarsi spaventare dalle complessità della programmazione per computer:
$ awk 'BEGIN { print "Non v\47allarmate!" }' -| Non v'allarmate!
awk
esegue le istruzioni associate a BEGIN
prima di leggere
qualsiasi input. Se non ci sono altre istruzioni nel proprio programma, come
in questo caso, awk
si ferma, invece di tentare di leggere input che
non sa come elaborare.
Il ‘\47’ è un modo straordinario (spiegato più avanti) per inserire un
apice singolo nel programma, senza dover ricorrere a fastidiosi meccanismi
di protezione della shell.
NOTA: Se si usa Bash come shell, si dovrebbe digitare il comando ‘set +H’ prima eseguire questo programma interattivamente, per non avere una cronologia dei comandi nello stile della C shell, che tratta il ‘!’ come un carattere speciale. Si raccomanda di inserire quel comando nel proprio file di personalizzazione della shell.
Il seguente semplice programma awk
emula il comando cat
; ovvero copia qualsiasi cosa si
batta sulla tastiera nel suo standard output (perché succede è spiegato fra
poco):
$ awk '{ print }' Ora è il tempo per tutti gli uomini buoni -| Ora è il tempo per tutti gli uomini buoni di venire in aiuto al loro paese. -| di venire in aiuto al loro paese. Or sono sedici lustri e sette anni, ... -| Or sono sedici lustri e sette anni, ... Cosa, io preoccupato? -| Cosa, io preoccupato? Ctrl-d
Successivo: Script eseguibili, Precedente: Leggere dal terminale, Su: Eseguire gawk [Contenuti][Indice]
Talora i programmi awk
sono molto lunghi. In tali situazioni
conviene mettere il programma in un file separato. Per dire ad
awk
di usare quel file come programma, digitare:
awk -f file-sorgente input-file1 input-file2 …
L’opzione -f dice al comando awk
di ottenere il programma
awk
dal file file-sorgente (vedi Opzioni).
Ogni nome-file può essere usato come file-sorgente. Per esempio, si
potrebbe mettere il programma:
BEGIN { print \"Non v'allarmate!\" }
nel file consiglio. Allora questo comando:
awk -f consiglio
è equivalente al comando:
awk 'BEGIN { print \"Non v\47allarmate!\" }'
Questo è già stato spiegato prima
(vedi Leggere dal terminale).
Si noti che normalmente non serve mettere apici singoli nel nome-file che si
fornisce con -f, perché di solito i nomi-file non contengono
caratteri che sono speciali per la shell. Si noti che in consiglio,
il programma awk
non ha dei doppi apici che lo delimitano. I
doppi apici sono necessari solo per programmi scritti direttamente sulla riga
di comando di awk
.
(Inoltre, se il programma si trova in un file, è possibile usare un apice
singolo all’interno del programma, invece del magico ‘\47’.)
Per identificare chiaramente un file di programma awk
come tale,
si può aggiungere il suffisso .awk al nome-file. Ciò non
cambia l’esecuzione del programma awk
ma semplifica
la “manutenzione”.
Successivo: Commenti, Precedente: Lunghi, Su: Eseguire gawk [Contenuti][Indice]
awk
da eseguire come scriptUna volta familiarizzato con awk
, si potrebbero scrivere
script che richiamano awk
, usando il meccanismo di
script ‘#!’. Ciò è
possibile in molti sistemi operativi.8
Per esempio, si potrebbe modificare il file consiglio e farlo divenire:
#! /bin/awk -f BEGIN { print \"Non v'allarmate!\" }
Dopo aver reso eseguibile questo file (con il comando chmod
),
digitare semplicemente ‘consiglio’
al prompt della shell e il sistema si preparerà a eseguire awk
come se si fosse digitato ‘awk -f consiglio’:
$ chmod +x consiglio $ consiglio -| Non v'allarmate!
(Si suppone che la directory corrente sia tra quelle contenute nella variabile
che indica il "percorso" di ricerca [solitamente $PATH
]. In caso
contrario si potrebbe aver bisogno di digitare ‘./consiglio’ nella
shell.)
Script awk
autocontenuti sono utili se si vuol scrivere un
programma che gli utenti possono richiamare senza dover essere informati che
il programma è scritto in awk
.
Comprendere ‘#!’
La riga che inizia con ‘#!’ lista l’intero nome-file di un
interpretatore
da richiamare, con degli argomenti facoltativi che saranno passati a
quell’interpretatore sulla riga di comando. Il sistema operativo quindi
richiama l’interpretatore con gli argomenti dati e con l’intera lista di
argomenti con cui era stato invocato il programma. Il primo argomento nella
lista è l’intero nome-file del programma Alcuni sistemi limitano la lunghezza del nome del programma interpretarore a 32 caratteri. Spesso, si può rimediare utilizzando un collegamento simbolico. Non si dovrebbero mettere altri argomenti oltre al primo nella riga ‘#!’
dopo il percorso del comando Infine, il valore di |
Successivo: Protezione, Precedente: Script eseguibili, Su: Eseguire gawk [Contenuti][Indice]
gawk
.Un commento è del testo incluso in un programma per aiutare le persone che lo leggeranno; non è parte del programma eseguibile vero e proprio. I commenti possono spiegare cosa fa il programma e come funziona. Quasi tutti i linguaggi di programmazione possono contenere commenti, poiché i programmi sono solitamente difficili da comprendere senza di essi.
Nel linguaggio awk
, un commento inizia con il segno del
cancelletto (‘#’) e continua fino alla fine della riga.
Il ‘#’ non deve necessariamente essere il primo carattere della riga.
Il linguaggio awk
ignora il resto di una riga dopo il carattere
cancelletto.
Per esempio, potremmo mettere quel che segue in consiglio:
# Questo programma stampa uno scherzoso consiglio amichevole. # Aiuta a far passare la paura del computer agli utenti novelli. BEGIN { print "Non v'allarmate!" }
Si possono mettere dei commenti nei programmi awk
usa-e-getta da
digitare direttamente da tastiera, ma ciò solitmanete non serve molto; il
fine di un commento è di aiutare l’utente o qualcun altro a comprendere il
programma, quando lo rilegge in un secondo tempo.
ATTENZIONE: Come detto in Monouso, si possono includere programmi di dimensioni da piccole a medie tra apici singoli, per mantenere compatti i propri script di shell autocontenuti. Nel far questo, non bisogna inserire un apostrofo (ossia un apice singolo) in un commento, (o in qualsiasi altra parte del vostro programma). La shell interpreta gli apici singoli come delimitatori di chiusura dell’intero programma. Di conseguenza, solitamente la shell emette un messaggio riguardo ad apici presenti in numero dispari, e se
awk
viene comunque eseguito, è probabile che stampi strani messaggi di errori di sintassi. Per esempio, nel caso seguente:$ awk 'BEGIN { print "Ciao" } # un'idea brillante' >La shell considera il secondo apice singolo come delimitatore del testo precedente, e trova che un nuovo testo tra apici ha inizio verso la fine della riga di comando. A causa di ciò emette una richiesta secondaria di input, e si mette in attesa di ulteriore input. Con il comando
awk
Unix, se si chiude l’ulteriore stringa tra apici singoli il risultato è il seguente:$ awk '{ print "Ciao" } # un'idea brillante' > ' error→ awk: fatale: non riesco ad aprire file `brillante' error→ in lettura (File o directory non esistente)Mettere una barra inversa prima dell’apice singolo in ‘un'idea’ non risolverebbe, poiché le barre inverse non sono speciali all’interno di apici singoli. La prossima sottosezione descrive le regole di protezione della shell.
Precedente: Commenti, Su: Eseguire gawk [Contenuti][Indice]
• Doppi apici in DOS: | Passaggio di apici in file .BAT Windows. |
Per programmi awk
di lunghezza da corta a media spesso conviene
digitare il programma sulla riga di comando awk
.
La maniera migliore per farlo è racchiudere l’intero programma tra apici
singoli.
Questo vale sia che si digiti il programma interattivamente su
richiesta della shell, sia che lo si scriva come parte di uno script
di shell di maggiori dimensioni:
awk 'testo del programma' input-file1 input-file2 …
Quando si lavora con la shell, non guasta avere una conoscenza di base sulle regole per l’uso di apici nella shell. Le regole seguenti valgono solo per shell in stile Bourne (come Bash, la Bourne-Again shell). Se si usa la C shell, si avranno regole differenti.
Prima di immergerci nelle regole, introduciamo un concetto che ricorre in tutto questo Documento, che è quello della stringa null, o vuota.
La stringa nulla è una variabile, di tipo carattere, che non ha un valore.
In altre parole, è vuota. Nei programmi awk
si scrive così:
""
. Nella shell la si può scrivere usando apici sia singoli
che doppi: ""
oppure ''
. Sebbena la stringa nulla non contenga
alcun carattere, essa esiste lo stesso. Si consideri questo comando:
$ echo ""
Qui, il comando echo
riceve un solo argomento, anche se
quell’argomento non contiene alcun carattere. Nel resto di questo
Documento, usiamo indifferentemente i termini stringa nulla
e stringa vuota. Ora, proseguiamo con le regole relative agli apici:
Poiché alcuni caratteri all’interno di un testo racchiuso tra doppi apici sono interpretati dalla shell, essi devono essere protetti all’interno del testo stesso. Sono da tener presenti i caratteri ‘$’, ‘`’, ‘\’, e ‘"’, tutti i quali devono essere preceduti da una barra inversa quando ricorrono all’interno di un testo racchiuso tra doppi apici, per poter essere passati letteralmente al programma. (La barra inversa viene tolta prima del passaggio al programma.) Quindi, l’esempio visto precedentemente in Leggere dal terminale:
awk 'BEGIN { print "Non v\47allarmate!" }'
si potrebbe scrivere invece così:
$ awk "BEGIN { print \"Non v'allarmate!\" }" -| Non v'allarmate!
Va notato che l’apice singolo non è speciale all’interno di un testo racchiuso tra doppi apici.
FS
sia
impostato alla stringa nulla, digitare:
awk -F "" 'programma' file # corretto
awk -F"" 'programma' file # errato!
Nel secondo caso, awk
tenta di usare il nome del programma come
valore di FS
, e il primo nome-file come testo del programma!
Ciò come minimo genera un errore di sintassi, e un comportamento confuso nel
caso peggiore.
Mischiare apici singoli e doppi è difficile. Occorre utilizzare trucchi della shell per gli apici, come questi:
$ awk 'BEGIN { print "Questo è un apice singolo. <'"'"'>" }' -| Questo è un apice singolo. <'>
Questo programma stampa tre stringhe tra apici concatenate tra loro. La prima e la terza sono rinchiuse tra apici singoli, la seconda tra apici doppi.
Quanto sopra può essere “semplificato” così:
$ awk 'BEGIN { print "Questo è un apice singolo <'\''>" }' -| Questo è un apice singolo <'>
A voi la scelta del più leggibile dei due.
Un’altra opzione è quella di usare doppi apici, proteggendo i doppi apici
inclusi, a livello awk
:
$ awk "BEGIN { print \"Questo è un apice singolo <'>\" }" -| Questo è un apice singolo <'>
Quest’opzione è fastidiosa anche perché il doppio apice, la barra inversa e
il simbolo del dollaro sono molto comuni nei programmi awk
più
avanzati.
Una terza opzione è quella di usare le sequenze ottali equivalenti (vedi Sequenze di protezione) per i caratteri apice singolo e doppio, così:
$ awk 'BEGIN { print "Questo è un apice singolo <\47>" }' -| Questo è un apice singolo <'> $ awk 'BEGIN { print "Questo è un doppio apice <\42>" }' -| Questo è un doppio apice <">
Questo funziona bene, ma sai dovrebbe commentare chiaramente quel che il testo protetto significa.
Una quarta possibilità è di usare assegnamenti di variabili sulla riga di comando, così:
$ awk -v sq="'" 'BEGIN { print "Questo è un apice singolo <" sq ">" }' -| Questo è un apice singolo <'>
(Qui, le due stringhe costanti e il valore di sq
sono concatenati in
un’unica stringa che è stampata da print
.)
Se servono veramente sia gli apici singoli che quelli doppi nel proprio
programma awk
, è probabilmente meglio tenerlo in un file separato,
dove la shell non interferisce, e si potrà scrivere quello che si vuole.
Su: Protezione [Contenuti][Indice]
Sebbene questo Documento in generale si preoccupi solo di sistemi POSIX e della shell POSIX, il problema che stiamo per vedere emerge abbastanza spesso presso parecchi utenti, e per questo ne parliamo.
Le “shell” nei sistemi Microsoft Windows usaso il carattere doppio apice per protezione, e rendono difficile o impossibile inserire un carattere doppio apice in uno script scritto su una riga di comando. l’esempio che segue, per il quale ringraziamo Jeroen Brink, mostra come stampare tutte le righe di un file, racchiudendole tra doppi apici:
gawk "{ print \"\042\" $0 \"\042\" }" file
Successivo: Molto semplice, Precedente: Eseguire gawk, Su: Per iniziare [Contenuti][Indice]
Molti degli esempi in questo Documento hanno come input due file-dati di esempio. Il primo, mail-list, contiene una lista di nomi di persone, insieme ai loro indirizzi email e a informazioni riguardanti le persone stesse. Il secondo file-dati, di nome inventory-shipped, contiene informazioni riguardo a consegne mensili. In entrambi i file, ogni riga è considerata come un record.
Nel mail-list, ogni record contiene il nome di una persona, il suo numero di telefono, il suo indirizzo email, e un codice che indica la sua relazione con l’autore della lista. Le colonne sono allineate usando degli spazi. Una ‘A’ nell’ultima colonna indica che quella persona è un conoscente [Acquaintance]. Una ‘F’ nell’ultima colonna significa che quella persona è un amico [Friend]. Una ‘R’ vuol dire che quella persona è un parente [Relative]:
Amelia 555-5553 amelia.zodiacusque@gmail.com F Anthony 555-3412 anthony.asserturo@hotmail.com A Becky 555-7685 becky.algebrarum@gmail.com A Bill 555-1675 bill.drowning@hotmail.com A Broderick 555-0542 broderick.aliquotiens@yahoo.com R Camilla 555-2912 camilla.infusarum@skynet.be R Fabius 555-1234 fabius.undevicesimus@ucb.edu F Julie 555-6699 julie.perscrutabor@skeeve.com F Martin 555-6480 martin.codicibus@hotmail.com A Samuel 555-3430 samuel.lanceolis@shu.edu A Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R
Il file-dati inventory-shipped contiene informazioni sulle consegne effettuate durante l’anno. Ogni record contiene il mese, il numero di contenitori verdi spediti, il numero di scatole rosse spedite, il numero di borse arancione spedite, e il numero di pacchetti blu spediti, in quest’ordine. Ci sono 16 record, relativi ai dodici mesi dello scorso anno e ai primi quattro mesi dell’anno in corso. Una riga vuota separa i data relativi a ciascun anno:
Jan 13 25 15 115 Feb 15 32 24 226 Mar 15 24 34 228 Apr 31 52 63 420 May 16 34 29 208 Jun 31 42 75 492 Jul 24 34 67 436 Aug 15 34 47 316 Sep 13 55 37 277 Oct 29 54 68 525 Nov 20 87 82 577 Dec 17 35 61 401 Jan 21 36 64 620 Feb 26 58 80 652 Mar 24 75 70 495 Apr 21 70 74 514
Questi file di esempio sono inclusi nella distribuzione gawk
,
nella directory awklib/eg/data.
Successivo: Due regole, Precedente: File dati di esempio, Su: Per iniziare [Contenuti][Indice]
I seguenti comandi eseguono un semplice programma awk
che cerca
nel file in input mail-list la stringa di caratteri ‘li’ (una
sequenza di caratteri è solitamente chiamato una stringa;
il termine stringa è basato su un uso linguistico, del tipo
“una stringa di perle” o “una stringa di luci decorative”):
awk '/li/ { print $0 }' mail-list
Quando si incontra una riga che contiene ‘li’, la si stampa, perché ‘print $0’ significa "stampa la riga corrente". (Lo scrivere solo ‘print’ ha lo stesso significato, quindi avremmo anche potuto limitarci a fare così).
Si sarà notato che delle barre (‘/’) delimitano la stringa ‘li’
nel programma awk
. Le barre indicano che ‘li’ è il
modello da ricercare. Questo tipo di notazione è definita come
espressione regolare, e sarà trattata più avanti in maggior dettaglio
(vedi Espressioni regolari).
Il modello può corrispondere anche solo a una parte di una parola.
Ci sono
apici singoli che racchiudono il programma awk
in modo che la
shell non interpreti alcuna parte di esso come un carattere speciale della
shell.
Questo è quello che il programma stampa:
$ awk '/li/ { print $0 }' mail-list -| Amelia 555-5553 amelia.zodiacusque@gmail.com F -| Broderick 555-0542 broderick.aliquotiens@yahoo.com R -| Julie 555-6699 julie.perscrutabor@skeeve.com F -| Samuel 555-3430 samuel.lanceolis@shu.edu A
In una regola awk
, il criterio di selezione o l’azione possono
essere omessi, ma non entrambi. Se il criterio è omesso, l’azione viene
applicata a ogni riga dell’input.
Se l’azione viene omessa, per default si stampano tutte le righe che
sono individuate dal criterio di selezione.
Quindi, si potrebbe omettere l’azione (l’istruzione print
e le
graffe) nell’esempio precedente e il risultato sarebbe lo stesso:
awk
stampa tutte le righe che corrispondono al criterio di
ricerca ‘li’. Per confronto, omettendo l’istruzione print
ma
lasciando le graffe si richiede un’azione nulla, che non fa nulla (cioè non
stampa alcuna riga).
Molti programmi awk
pratici sono lunghi solo una o due righe.
Qui sotto troviamo una collezione di programmi utili e corti, per iniziare.
Alcuni di questi programmi contengono elementi del linguaggio che non sono
ancora stati spiegati. (La descrizione del programma fornisce una buona
idea di quel che si vuole ottenere, ma occorre leggere il resto del
Documento per divenire esperti in awk
!)
Molti degli esempi usano un file-dati di nome data. Questo serve solo
a indicare la posizione del nome; se questi programmi devono venir usati per
se stessi, sostituire i propri nomi-file al posto di data.
Per futura memoria, si noti che spesso c’è più di un modo per fare qualcosa
in awk
. In un altro momento, si potrebbe tornare a guardare questi
esempi per vedere se si riescono a trovare modi differenti per fare le stesse
cose mostrate qui appresso:
awk 'length($0) > 80' data
L’unica regola presente ha un’espressione di relazione come modello e non ha azione—quindi applica l’azione di default, stampando il record.
awk '{ if (length($0) > max) max = length($0) } END { print max }' data
Il codice associato a END
viene eseguito dopo che tutto
l’input è stato letto; è l’altra faccia della medaglia di BEGIN
.
expand data | awk '{ if (x < length($0)) x = length($0) } END { print "la lunghezza massima di una riga è" x }'
Questo esempio è leggermente diverso da quello precedente:
l’input è l’output del comando expand
, che cambia i TAB
in spazi, in modo che le larghezze confrontate siano quelle che sarebbero
qualora le si stampasse, e non il numero dei caratteri di input su ogni
riga. [il carattere TAB occupa un byte nel file, ma può generare fino a
otto spazi bianchi in fase di stampa.]
awk 'NF > 0' data
Questa è una maniera facile per eliminare le righe vuote dal file (o piuttosto, per creare un nuovo file, simile al vecchio, ma nel quale le linee vuote sono state tolte).
awk 'BEGIN { for (i = 1; i <= 7; i++) print int(101 * rand()) }'
ls -l elenco-file | awk '{ x += $5 } END { print "byte totali: " x }'
ls -l elenco-file | awk '{ x += $5 } END { print "K-byte totali:", x / 1024 }'
awk -F: '{ print $1 }' /etc/passwd | sort
awk 'END { print NR }' data
awk 'NR % 2 == 0' data
Se aveste usato invece l’espressione ‘NR % 2 == 1’, il programma avrebbe stampato le righe dispari.
Successivo: Maggiore sofisticazione, Precedente: Molto semplice, Su: Per iniziare [Contenuti][Indice]
Il programma awk
legge il file in input una riga alla volta.
Per ogni riga awk
controlla la corrispondenza con ogni regola.
Se viene trovata più di una corrispondenza, vengono eseguite altrettante
azioni, nell’ordine in cui appaiono nel programma awk
.
Se non viene trovata nessuna corrispondenza, non viene eseguita alcuna azione.
Dopo aver elaborato tutte le regole che hanno corrispondenza con la riga (e
puó darsi che nessuna corrisponda), awk
legge la riga successiva. Comunque
vedi Istruzione next,
e anche vedi Istruzione nextfile.)
Si prosegue così finché il programma raggiunge la fine del file.
Per esempio, il seguente programma awk
contiene due regole:
/12/ { print $0 } /21/ { print $0 }
La prima regola ha la stringa ‘12’ da cercare e ‘print $0’ come azione. La seconda regola ha la stringa ‘21’ da cercare e ha ancora ‘print $0’ come azione. L’azione di ciascuna regola è racchiusa in una coppia di parentesi graffe.
Questo programma stampa ogni riga che contiene la stringa ‘12’ oppure la stringa ‘21’. Se una riga contiene entrambe le stringhe, è stampata due volte, una volta per ogni regola.
Questo è ciò che capita se eseguiamo questo programma sui nostri file-dati, mail-list e inventory-shipped:
$ awk '/12/ { print $0 } > /21/ { print $0 }' mail-list inventory-shipped -| Anthony 555-3412 anthony.asserturo@hotmail.com A -| Camilla 555-2912 camilla.infusarum@skynet.be R -| Fabius 555-1234 fabius.undevicesimus@ucb.edu F -| Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R -| Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R -| Jan 21 36 64 620 -| Apr 21 70 74 514
Si noti che la riga che inizia con ‘Jean-Paul’ nel file mail-list è stata stampata due volte, una volta per ogni regola.
Successivo: Istruzioni/Righe, Precedente: Due regole, Su: Per iniziare [Contenuti][Indice]
Dopo aver imparato a eseguire alcuni semplici compiti, vediamo cosa possono
fare i tipici programmi awk
.
Questo esempio mostra come awk
può essere usato per riassumere,
selezionare e riordinare l’output di un altro comando. Sono usate
funzionalità di cui non si è ancora parlato, quindi non ci si deve preoccupare
se alcuni dettagli risulteranno oscuri:
ls -l | awk '$6 == "Nov" { somma += $5 } END { print somma }'
Questo comando stampa il numero totale di byte in tutti i file contenuti nella directory corrente, la cui data di modifica è novembre (di qualsiasi anno). La parte ‘ls -l’ dell’esempio è un comando di sistema che fornisce un elenco dei file in una directory, con anche la dimensione di ogni file e la data di ultima modifica. Il suo output è del tipo:
-rw-r--r-- 1 arnold user 1933 Nov 7 13:05 Makefile -rw-r--r-- 1 arnold user 10809 Nov 7 13:03 awk.h -rw-r--r-- 1 arnold user 983 Apr 13 12:14 awk.tab.h -rw-r--r-- 1 arnold user 31869 Jun 15 12:20 awkgram.y -rw-r--r-- 1 arnold user 22414 Nov 7 13:03 awk1.c -rw-r--r-- 1 arnold user 37455 Nov 7 13:03 awk2.c -rw-r--r-- 1 arnold user 27511 Dec 9 13:07 awk3.c -rw-r--r-- 1 arnold user 7989 Nov 7 13:03 awk4.c
Il primo campo contiene le autorizzazioni di lettura/scrittura [r/w], il secondo il numero dei collegamenti al file [cioè il numero di nomi con cui il file è conosciuto], e il terzo campo identifica il proprietario del file. Il quarto campo identifica il gruppo a cui appartiene il file. Il quinto campo contiene la dimensione del file, in byte. Il sesto, settimo e ottavo campo contengono il mese, il giorno e l’ora, rispettivamente, in cui il file è stato modificato. Infine, il nono campo contiene il nome-file.
L’espressione ‘$6 == "Nov"’ nel nostro programma awk
controlla
se il sesto campo dell’output di ‘ls -l’ corrisponda alla stringa
‘Nov’. Ogni volta che una riga ha la stringa
‘Nov’ come suo sesto campo, awk
esegue l’azione
‘somma += $5’. Questo aggiunge il quinto campo (la dimensione del file)
alla variabile somma
. Come risultato, quando awk
ha finito
di leggere tutte le righe in input, somma
contiene la somma totale
delle dimensioni dei file che corrispondono al criterio di ricerca.
(Ciò funziona contando sul fatto che le variabili awk
sono
automaticamente inizializzate a zero.)
Dopo che l’ultima riga dell’output di ls
è stata elaborata, la
regola END
viene eseguita e viene stampato il valore di somma
.
In questo esempio, il valore di somma
è 80600.
Queste tecniche più avanzate di awk
sono trattate in
sezioni
successive (vedi Panoramica sulle azioni). Prima di poter passare a una
programmazione più avanzata con awk
, è necessario sapere come
awk
interpreta i file in input e visualizza quelli in output.
Modificando campi e usando l’istruzione print
è possibile produrre
dei rapporti molto utili ed esteticamente gradevoli.
Successivo: Altre funzionalità, Precedente: Maggiore sofisticazione, Su: Per iniziare [Contenuti][Indice]
awk
Molto spesso, ogni riga di un programma awk
è un’istruzione a sé
stante o una regola isolata, come:
awk '/12/ { print $0 } /21/ { print $0 }' mail-list inventory-shipped
Comunque, gawk
ignora i ritorni a capo dopo ognuno di questi
simboli e istruzioni:
, { ? : || && do else
Un ritorno a capo in ogni altro punto del programma è considerato come la fine di un’istruzione.9
Volendo dividere una sola istruzione su due righe in un punto in cui andando a capo sarebbe considerata conclusa, è possibile continuare nella riga successiva terminando la prima riga con un carattere di barra inversa (‘\’). La barra inversa dev’essere l’ultimo carattere sulla riga, per essere riconosciuto come un carattere di continuazione. Una barra inversa è consentita in ogni parte dell’istruzione, anche in mezzo a una stringa o a un’espressione regolare. Per esempio:
awk '/Questa espressione regolare è troppo lunga, quindi\ la continuiamo sulla riga seguente/ { print $1 }'
Non abbiamo quasi mai usato la continuazione tramite barra inversa nei nostri
programmi di esempio. gawk
non pone limiti alla lunghezza di
una riga, quindi la continuazione tramite barra inversa non è mai strettamente
necessaria; serve soltanto a migliorare la leggibilità del programma.
Per la stessa ragione, ma anche per amore di chiarezza, abbiamo tenuto
concise molte istruzioni nei programmi presentati in questo Documento.
La continuazione tramite barra inversa è molto utile quando il proprio
programma awk
si trova in un file sorgente separato, invece di
essere immesso nella riga di comando. Si noti anche che molte implementazioni
di awk
presentano delle differenze su dove è possibile usare
la continuazione tramite barra inversa. Per esempio, potrebbero non
consentire di spezzare una costante di tipo stringa usando la continuazione
tramite barra inversa. Quindi, per ottenere la massima portabilità dei
propri programmi awk
, è meglio non spezzare le righe nel
mezzo di un’espressione regolare o di una stringa.
ATTENZIONE: la continuazione tramite barra inversa non funziona come sopra descritto nella C shell. Funziona per programmi
awk
contenuti in file e per programmi sulla riga di comando, ammesso che si stia usando una shell conforme a POSIX, come la Unix Bourne shell o Bash. Ma la C shell si comporta in maniera diversa! In quel caso, occorre usare due barre inverse consecutive, in fondo alla riga. Si noti anche che quando si usa la C shell ogni andata a capo nel vostro programmaawk
deve essere indicata con una barra inversa. Per esempio:% awk 'BEGIN { \ ? print \\ ? "ciao, mondo" \ ? }' -| ciao, mondoQui, il ‘%’ e il ‘?’ sono i prompt primario e secondario della C shell, analogamente a quelli usati nella shell standard ‘$’ e ‘>’.
Si confronti l’esempio precedente, come viene scritto in una shell conforme a POSIX:
$ awk 'BEGIN { > print \ > "ciao, mondo" > }' -| ciao, mondo
awk
è un linguaggio orientato alla riga. L’azione relativa a ogni
regola deve iniziare sulla stessa riga del criterio di selezione. Per avere
criterio di selezione e azione su righe separate, si
deve usare la continuazione tramite barra inversa; non si può fare
diversamente.
Un’altra cosa da tener presente è che la continuazione tramite barra inversa e
i commenti non possono essere frammisti. Non appena awk
incontra
un ‘#’ che inizia un commento, ignora tutto il resto della riga.
Per esempio:
$ gawk 'BEGIN { print "Non allarmarti" # una amichevole \ > regola BEGIN > }' error→ gawk: riga com.:2: regola BEGIN error→ gawk: riga com.:2: ^ syntax error
In questo caso, parrebbe che la barra inversa continui il commento sulla riga
successiva. Invece, la combinazione barra inversa-ritorno a capo non viene
per nulla notata, in quanto “nascosta” all’interno del commento. Quindi,
il BEGIN
è marcato come errore di sintassi.
Quando le istruzioni awk
all’interno di una regola sono brevi, si
potrebbe metterne più d’una su una riga sola. Ciò è possibile separando le
istruzioni con un punto e virgola (‘;’).
Questo vale anche per le regole stesse.
Quindi, il programma visto all’inizio di
questa
sezione
poteva essere scritto anche così:
/12/ { print $0 } ; /21/ { print $0 }
NOTA BENE: La possibilità che più regole coesistano sulla stessa riga, se sono separate da un punto e virgola, non esisteva nel linguaggio
awk
originale; è stata aggiunta per congruenza con quanto è consentito per le istruzioni all’interno di un’azione.
Successivo: Quando, Precedente: Istruzioni/Righe, Su: Per iniziare [Contenuti][Indice]
awk
Il linguaggio awk
mette a disposizione un numero di variabili
built-in, o predefinite, che il programma dell’utente può usare
per ottenere informazioni da awk
. Ci sono pure altre variabili
che il programma può impostare, per definire come awk
deve
gestire i dati.
Inoltre, awk
mette a disposizione parecchie funzioni predefinite
[built-in] per effettuare calcoli di tipo comune e operazioni che
agiscono sulle stringhe di caratteri.
gawk
mette a disposizione funzioni predefinite per gestire le
marcature temporali, per effettuare manipolazioni a livello di bit, per
tradurre stringhe al momento dell’esecuzione del programma
(internazionalizzazione), per determinare qual è il tipo di una variabile,
e per ordinare dei vettori.
Nel seguito della presentazione del linguaggio awk
, saranno
introdotte molte delle variabili e parecchie funzioni. Esse sono
descritte sistematicamente in Variabili predefinite e in
Funzioni.
Successivo: Sommario dell'introduzione, Precedente: Altre funzionalità, Su: Per iniziare [Contenuti][Indice]
gawk
Ora che abbiamo visto qualcosa di quel che awk
è in grado di fare,
ci si potrà chiedere come awk
potrebbe tornare utile. Usando
programmi di utilità, criteri di ricerca sofisticati, separatori
di campo, istruzioni aritmetiche, e altri criteri di selezione, è possibile
produrre degli output molto più complessi. Il linguaggio awk
è
molto utile per fornire dei tabulati partendo da grandi quantità di dati
grezzi, per esempio riassumendo informazioni dall’output di altri
programmi di utilità come ls
.
(Vedi Maggiore sofisticazione.)
I programmi scritti con awk
sono normalmente molto più
corti dei loro equivalenti in altri linguaggi. Ciò rende i programmi
awk
facili da comporre e da utilizzare. Spesso i programmi
awk
possono essere scritti al volo a terminale, usati una volta sola
e buttati via. Poiché i programmi awk
sono interpretati, si può
evitare la (normalmente laboriosa) parte di compilazione nel ciclo tipico
dello sviluppo software, ossia edita-compila-prova-correggi.
In awk
sono stati scritti programmi complessi, compreso un assembler
completo, pluri-piattaforma per
microprocessori a 8-bit (vedi Glossario, per maggiori informazioni),
e un assembler di microcodice per un computer dedicato esclusivamente
al linguaggio Prolog.
Le possibilità dell’originale awk
erano messe a dura prova
da programmi di questa complessità, ma le versioni moderne sono più robuste.
Se capita di scrivere programmi awk
più lunghi di, diciamo,
qualche centinaio di righe, si potrebbe considerare la possibilità di usare
un linguaggio di programmazione differente da awk
.
La shell consente di ricercare stringhe ed espressioni regolari; inoltre
consente di usare in maniera efficace i comandi di utilità del sistema.
Python offre un piacevole equilibrio tra la facilità di una programmazione
ad alto livello, e la possibilità di interagire a livello di sistema
operativo.10
Precedente: Quando, Su: Per iniziare [Contenuti][Indice]
awk
consistono di coppie di
criterio di ricerca–azione.
awk
.
awk
che siano eseguibili direttamente.
awk
iniziano con ‘#’ e continuano
fino alla fine della stessa riga.
awk
che facciano parte di uno script della shell (o di un file .BAT di
MS-Windows).
do
ed else
.
Successivo: Espressioni regolari, Precedente: Per iniziare, Su: Top [Contenuti][Indice]
awk
e gawk
Questo capitolo tratta di come eseguire awk
, delle opzioni da
riga di comando, sia quelle dello standard POSIX che quelle specifiche di
gawk
, e di cosa fanno awk
e gawk
con gli
argomenti che non sono opzioni.
Prosegue poi spiegando come gawk
cerca i file sorgenti,
leggendo lo standard input assieme ad altri file, le variabili d’ambiente di
gawk
, lo stato di ritorno di gawk
, l’uso dei file inclusi,
e opzioni e/o funzionalità obsolete e non documentate.
Molte delle opzioni e funzionalità qui descritte sono trattate con maggior dettaglio nei capitoli successivi del Documento; gli argomenti presenti in questo capitolo che al momento non interessano si possono tranquillamente saltare.
• Riga di comando: | Come eseguire awk .
| |
• Opzioni: | Opzioni sulla riga di comando e loro significato. | |
• Altri argomenti: | Nomi dei file in input e assegnamento di valori a variabili. | |
• Specificare lo standard input: | Come specificare lo standard input insieme ad altri file. | |
• Variabili d'ambiente: | Le variabili d’ambiente usate da
gawk .
| |
• Codice di ritorno: | Il codice di ritorno all’uscita da
gawk .
| |
• Includere file: | Come includere altri file nel proprio programma. | |
• Caricare librerie condivise: | Caricare librerie condivise nel proprio programma. | |
• Parti obsolete: | Opzioni e/o funzionalità obsolete. | |
• Non documentato: | Opzioni e funzionalità non documentate. | |
• Sommario invocazione: | Sommario invocazione. |
Successivo: Opzioni, Su: Invocare Gawk [Contenuti][Indice]
awk
Ci sono due modi di eseguire awk
: con un programma esplicito o con
uno o più file di programma. Qui è mostrata la sintassi di entrambi; le voci
racchiuse tra […] sono opzionali:
awk
[opzioni] -f file_di _programma [--] file …awk
[opzioni] [--]'programma'
file …
In aggiunta alle tradizionali opzioni di una sola lettera in stile POSIX,
gawk
consente anche le opzioni estese GNU.
È possibile invocare awk
con un programma vuoto:
awk '' file_dati_1 file_dati_2
Fare così ha comunque poco senso; awk
termina
silenziosamente quando viene fornito un programma vuoto.
(a.b.)
Se è stato specificato --lint sulla riga di comando,
gawk
emette un avviso che avverte
che il programma è vuoto.
Successivo: Altri argomenti, Precedente: Riga di comando, Su: Invocare Gawk [Contenuti][Indice]
Le opzioni sono precedute da un trattino e consistono in un unico carattere. Le opzioni estese in stile GNU sono precedute da un doppio trattino e consistono in una parola chiave. La parola chiave può essere abbreviata, a condizione che l’abbreviazione identifichi univocamente l’opzione. Se l’opzione prevede un argomento, la parola chiave è immediatamente seguita da un segno di uguale (‘=’) e dal valore dell’argomento, oppure la parola chiave e il valore dell’argomento sono separati da spazi. Se un’opzione con un valore viene immessa più di una volta, l’ultimo valore è quello che conta.
Ogni opzione estesa di gawk
ha una corrispondente opzione
breve in stile POSIX.
Le opzioni estese e brevi sono
intercambiabili in tutti i contesti.
L’elenco seguente descrive le opzioni richieste dallo standard POSIX:
-F fs
--field-separator fs
Imposta la variabile FS
a fs
(vedi Separatori di campo).
-f file-sorgente
--file file-sorgente
Legge il sorgente del programma awk
da file-sorgente
anziché prenderlo dal primo argomento che non è un’opzione.
Quest’opzione può essere data più volte; il programma awk
è formato dalla concatenazione del contenuto di ogni
file-sorgente specificato.
-v var=val
--assign var=val
Imposta la variabile var al valore val prima che inizi
l’esecuzione del programma. Tali valori di variabile sono disponibili
all’interno della regola BEGIN
(vedi Altri argomenti).
L’opzione -v può impostare una sola variabile per volta, ma può essere usata più di una volta, impostando ogni volta una variabile differente, in questo modo: ‘awk -v pippo=1 -v pluto=2 …’.
ATTENZIONE: Usare -v per impostare valori di variabili predefinite può condurre a risultati sorprendenti.
awk
reimposterà i valori di quelle variabili secondo le sue necessità, anche ignorando eventuali valori iniziali che possono essere stati assegnati.
-W gawk-opt
Fornisce un’opzione specifica dell’implementazione. Questa è la convenzione
POSIX per fornire opzioni specifiche dell’implementazione.
Queste opzioni
hanno anche una corrispondente opzione estesa scritta in stile GNU.
Si noti che le opzioni estese possono essere abbreviate, sempre che
le abbreviazioni siano univoche.
L’elenco completo delle opzioni specifiche di gawk
è riportato di
seguito.
--
Segnale della fine delle opzioni da riga di comando. I seguenti argomenti non sono trattati come opzioni anche se iniziano con ‘-’. Questa interpretazione di -- segue le convenzioni POSIX per l’analisi degli argomenti.
È utile se si hanno nomi-file che iniziano con ‘-’,
o negli script di shell, se si hanno nomi-file che devono essere
specificati dall’utente che potrebbero iniziare con ‘-’.
È utile anche per passare opzioni al programma awk
;
si veda Funzione getopt.
L’elenco che segue descrive le opzioni specifiche di gawk
:
Fa sì che gawk
tratti tutti i dati in input come caratteri di un solo
byte. In aggiunta, tutto l’output scritto con print
o printf
viene trattato come composto da caratteri contenuti in un solo byte.
Normalmente, gawk
segue lo standard POSIX e cerca di elaborare i suoi
dati di input in accordo con la localizzazione corrente
(vedi Localizzazioni).
Questo spesso può comportare la conversione di caratteri multibyte in
caratteri estesi (internamente), e può
creare problemi o confusione se i dati di input non contengono caratteri
multibyte validi. Quest’opzione è una maniera facile di dire a gawk
:
“Giù le mani dai miei dati!”.
Specifica la modalità di compatibilità, nella quale le estensioni GNU al
linguaggio awk
sono disabilitate; in questo modo gawk
si
comporta proprio come la versione di BWK awk
.
Vedi POSIX/GNU, che riassume le estensioni. Si veda anche Modalità di compatibilità.
Stampa la versione ridotta della General Public License ed esce.
=
file]Stampa una lista ordinata di variabili globali, i loro tipi, e i valori finali in file. Se non viene fornito alcun file, stampa questa lista in un file chiamato awkvars.out nella directory corrente. Non sono consentiti spazi tra -d e file, se file viene specificato.
Avere una lista di tutte le variabili globali è un buon modo per cercare
refusi nei propri programmi.
Si può usare quest’opzione anche se si ha un grosso programma con tantissime
funzioni, e si vuol essere sicuri che le funzioni non usino
inavvertitamente variabili globali che sarebbero dovute essere locali
(questo è un errore particolarmente facile da fare con nomi di variabile
semplici come i
, j
, etc.).
=
file]Abilita l’esecuzione del debug di programmi awk
(vedi Debugging).
Per default, il debugger legge i comandi interattivamente dalla tastiera
(standard input).
L’argomento opzionale file consente di specificare un file con una lista
di comandi per il debugger da eseguire in maniera non interattiva.
Non sono consentiti spazi tra -D e file, se
file viene indicato.
Fornisce del codice sorgente nel testo-del-programma. Quest’opzione consente di combinare il codice sorgente contenuto in file col codice sorgente immesso sulla riga di comando. Questo è particolarmente utile quando si hanno funzioni di libreria che si vogliono usare dai programmi da riga di comando (vedi AWKPATH (Variabile)).
Simile a -f, legge il testo del programma awk
da
file. Ci sono due differenze rispetto a -f:
awk
.
Quest’opzione è particolarmente necessaria per le applicazioni World Wide Web
CGI che passano argomenti attraverso le URL; l’uso di quest’opzione impedisce
a un utente malintenzionato (o ad altri) di passare opzioni, assegnamenti o
codice sorgente awk
(con -e) all’applicazione
CGI.11
Quest’opzione dovrebbe essere usata
con script ‘#!’
(vedi Script eseguibili), in questo modo:
#! /usr/local/bin/gawk -E il programma awk è qui …
Analizza il programma sorgente e
genera un file GNU gettext
portable object template sullo
standard output per tutte le costanti di tipo stringa che sono state marcate
come da tradurre.
Vedi Internazionalizzazione,
per informazioni su quest’opzione.
Stampa un messaggio sull’“uso” riassumendo le opzioni brevi ed estese
accettate da gawk
ed esce.
Legge una libreria di sorgenti awk
da file-sorgente.
Quest’opzione è del tutto equivalente a usare la direttiva @include
all’interno del proprio programma. È molto simile all’opzione
-f, ma ci sono due differenze importanti. Primo, quando viene usata
l’opzione -i, il sorgente del programma non viene caricato se è
stato caricato in precedenza, mentre con -f, gawk
carica
sempre il file. Secondo, poiché quest’opzione è pensata per essere usata
con librerie di codice, gawk
non riconosce tali file come
costituenti l’input del programma principale. Così, dopo l’elaborazione di
un argomento -i, gawk
si aspetta di trovare il codice
sorgente principale attraverso l’opzione -f o sulla riga di comando.
Carica un’estensione dinamica denominata ext. Le estensioni sono
memorizzate come librerie condivise di sistema.
Quest’opzione ricerca la libreria usando la variabile d’ambiente
AWKLIBPATH
. Il suffisso corretto per la piattaforma in uso verrà
fornito per default, perciò non è necessario specificarlo nel nome
dell’estensione. La routine di inizializzazione dell’estensione dovrebbe
essere denominata dl_load()
. Un’alternativa è quella di usare la
direttiva @load
all’interno del programma per caricare una libreria
condivisa. Questa funzionalità avanzata è descritta in dettaglio in
Estensioni dinamiche.
=
valore]Emette messaggi d’avvertimento relativi a costrutti dubbi o non portabili ad
altre implementazioni di awk
.
Non sono consentiti spazi tra -L e valore, se
viene indicato il valore.
Alcuni avvertimenti vengono emessi quando gawk
legge preliminarmente
il programma. Altri vengono emessi quando il programma viene eseguito.
Con l’argomento opzionale ‘fatal’, gli avvertimenti lint sono considerati
come errori gravi. Potrebbe essere una misura drastica, però il suo uso
incoraggerà certamente lo sviluppo di programmi awk
più corretti.
Con l’argomento opzionale ‘invalid’, vengono emessi solo gli avvertimenti
relativi a quello che è effettivamente non valido (funzionalità non ancora
completamente implementata).
Alcuni avvertimenti vengono stampati solo una volta, anche se i costrutti dubbi
per i quali vengono emessi avvisi ricorrono diverse volte nel programma
awk
. Perciò, nell’eliminazione dei problemi rilevati da
--lint, bisogna porre attenzione a cercare tutte le occorrenze di ogni
costrutto inappropriato. Siccome i programmi awk
generalmente sono
brevi, questa non è un’operazione gravosa.
Chiede il calcolo con precisione arbitraria sui numeri. Quest’opzione non ha
alcun effetto se gawk
non è compilato per l’uso delle librerie GNU
MPFR e MP
(vedi Calcolo con precisione arbitraria).
Abilita l’interpretazione automatica di valori ottali ed esadecimali nei dati di input (vedi Dati non decimali).
ATTENZIONE: Quest’opzione può generare gravi malfunzionamenti nei vecchi programmi. Usare con cautela. Si noti anche che quest’opzione potrebbe non essere più disponibile in una futura versione di
gawk
.
Forza l’uso del carattere di separazione decimale della localizzazione quando analizza i dati in input (vedi Localizzazioni).
=
file]Consente la stampa di una versione formattata elegantemente dei programmi
awk
. Implica l’opzione --no-optimize.
Per default il programma di output viene creato in un file
chiamato awkprof.out (vedi Profilare).
L’argomento opzionale file consente di specificare un
nome-file differente per l’output.
Non sono consentiti spazi tra -o e file, se
file viene indicato.
NOTA: Nel passato, quest’opzione eseguiva anche il programma. Ora non è più così.
Abilita le ottimizzazioni di default nella rappresentazione interna del programma. Attualmente, questo comprende delle semplificazioni nell’uso di costanti e l’eliminazione delle code di chiamata nelle funzioni ricorsive [sostituzione della chiamata di funzione con dei salti diretti alla funzione].
Queste ottimizzazioni sono abilitate per default. Quest’opzione rimane disponibile per compatibilità all’indietro. Tuttavia può essere usata per annullare l’effetto di una precedente opzione -s (si veda più sotto in questa lista).
=
file]Abilita la creazione del profilo di esecuzione di programmi awk
(vedi Profilare).
Implicitamente viene forzata l’opzione --no-optimize.
Per default, i profili vengono creati in un file chiamato awkprof.out.
L’argomento opzionale file consente di specificare un altro
nome-file per il file del profilo.
Non sono consentiti spazi tra -p e file, se
viene indicato un file.
Il profilo contiene il numero di esecuzioni di ogni istruzione sul margine sinistro e il conteggio delle chiamate di funzione per ogni funzione.
Opera in modalità POSIX rigorosa. Disabilita tutte le estensioni di
gawk
(proprio come --traditional) e
disabilita tutte le estensioni non consentite da POSIX.
Vedi Estensioni comuni, per un sommario delle estensioni
di gawk
che sono disabilitate da quest’opzione.
Inoltre,
vengono applicate le seguenti
restrizioni:
FS
a un singolo carattere TAB
(vedi Separatori di campo).
Se si forniscono entrambe le opzioni --traditional e --posix
sulla riga di comando, --posix ha la precedenza. Se vengono fornite
entrambe le opzioni gawk
emette un avviso.
Consente le espressioni di intervallo
(vedi Operatori di espressioni regolari)
nelle espressioni regolari.
Questo è ora il comportamento di default di gawk
.
Tuttavia, quest’opzione rimane (sia per retrocompatibilità
che per l’uso in combinazione con --traditional).
Disabilita le opzioni di ottimizzazione di default di gawk
effettuate sulla rappresentazione interna del programma.
Disabilita la funzione system()
,
la ridirezione dell’input con getline
,
la ridirezione dell’output con print
e printf
,
e le estensioni dinamiche.
È particolarmente utile quando si vogliono eseguire script awk
da sorgenti dubbie e si vuol essere ricuri che gli script non abbiano
accesso al sistema (oltre al file-dati di input specificato).
Avvisa su costrutti che non sono disponibili nella versione originale di
awk
dalla versione 7 di Unix
(vedi V7/SVR3.1).
Stampa informazioni sulla versione di questa specifica copia di gawk
.
Consente di determinare se la copia di gawk
in uso è aggiornata
rispetto a quello che è attualmente in distribuzione da parte della Free
Software Foundation.
È utile anche per la segnalazione di bug
(vedi Bug).
Ogni altra opzione, se è stato specificato il testo di un programma è contrassegnata come non valida con un messaggio di avvertimento, altrimenti è ignorata.
In modalità di compatibilità, come caso particolare, se il valore di fs
fornito all’opzione -F è ‘t’, FS
è impostata al carattere
TAB ("\t"
). Questo è vero solo per --traditional e non
per --posix
(vedi Separatori di campo).
L’opzione -f può essere usata più di una volta nella riga di comando.
In questo caso, awk
legge il sorgente del suo programma da tutti i
file indicati, come se fossere concatenati assieme a formare un unico grande
file.
Questo è utile per creare librerie di funzioni di awk
. Queste
funzioni possono venir scritte una volta e in seguito recuperate da una
posizione standard, invece di doverle includere in ogni singolo programma.
L’opzione -i è simile in questo senso.
(Come indicato in
Sintassi delle definizioni,
i nomi di funzione devono essere univoci).
Con awk
standard, le funzioni di libreria si possono ancora usare,
anche se il programma è immesso dalla tastiera,
specificando ‘-f /dev/tty’. Dopo aver scritto il programma,
premere Ctrl-d (il carattere di fine file) per terminarlo.
(Si potrebbe anche usare ‘-f -’ per leggere il sorgente del programma
dallo standard input, ma poi non si potrà usare lo standard input come sorgente
di dati).
Siccome è scomodo usare il meccanismo di awk
standard per combinare
file sorgenti e programmi awk
da riga di comando, gawk
fornisce l’opzione -e. Questo non richiede di evitare l’uso dello
standard input per immettere codice sorgente; consente di combinare
facilmente codice sorgente da riga di comando e da libreria
(vedi AWKPATH (Variabile)).
Come per -f, le opzioni -e e -i
si possono usare più volte nella riga di comando.
Se non sono specificate opzioni -f o -e, gawk
usa il primo argomento che non è un’opzione come testo del
codice sorgente del programma.
Se la variabile d’ambiente POSIXLY_CORRECT
esiste,
gawk
si comporta in modalità POSIX rigorosa, esattamente come se
fosse stata fornita l’opzione --posix.
Molti programi GNU cercano questa variabile d’ambiente per eliminare
estensioni che confliggono con POSIX, ma gawk
si comporta in modo
diverso: sopprime tutte le estensioni, anche quelle che non confliggono con
POSIX, e funziona rigorosamente in modalità POSIX.
Se viene fornita l’opzione --lint sulla riga di comando e
gawk
passa alla modalità POSIX a causa di POSIXLY_CORRECT
,
viene emesso un messaggio di avvertimento indicando che è attiva la
modalità POSIX. Normalmente questa variabile si imposta nel file di avvio
della shell a livello utente.
Per una shell compatibile con Bourne (come Bash), queste righe andranno
aggiunte nel file .profile della directory "home" dell’utente:
POSIXLY_CORRECT=true export POSIXLY_CORRECT
Per una shell compatibile con C,12 questa riga andrà aggiunta nel file .login nella directory "home" dell’utente:
setenv POSIXLY_CORRECT true
Avere POSIXLY_CORRECT
impostata non è raccomandato per l’uso quotidiano,
ma è utile per provare la portabilità dei programmi su altri
ambienti.
Successivo: Specificare lo standard input, Precedente: Opzioni, Su: Invocare Gawk [Contenuti][Indice]
Qualsiasi altro argomento sulla riga di comando è trattato normalmente come
file in input da elaborare nell’ordine con cui è specificato. Comunque, un
argomento che ha la forma var=valore
, assegna
il valore valore alla variabile var—non specifica affatto
un file. (Si veda Opzioni di assegnamento.) Nel seguente esempio,
count=1 è un assegnamento di variabile, non un nome-file:
awk -f programma.awk file1 count=1 file2
Tutti gli argomenti da riga di comando sono resi disponibili al programma
awk
nel vettore ARGV
(vedi Variabili predefinite). Opzioni da
riga di comando e il testo del programma (se presente) sono esclusi da
ARGV
. Tutti gli altri argomenti, compresi gli assegnamenti di
variabile, sono inclusi. Come ogni elemento di ARGV
viene elaborato,
gawk
imposta ARGIND
all’indice in ARGV
dell’elemento corrente.
La modifica di ARGC
e ARGV
nel proprio programma awk
consente di controllare come awk
elabora i file in input; questo è
descritto più dettagliatamente in ARGC e ARGV.
La distinzione tra argomenti che sono nome-file e argomenti di assegnamento
di variabili vien fatta quando awk
deve aprire il successivo file di
input.
A quel punto dell’esecuzione, controlla la variabile nome-file per vedere se
è piuttosto un assegnamento di variabile; se così è, awk
imposta la
variabile invece di leggere un file.
Dunque, le variabili ricevono effettivamente i valori loro assegnati dopo che
tutti i file precedentemente specificati sono stati letti. In particolare, i
valori delle variabili assegnati in questo modo non sono disponibili
all’interno di una regola BEGIN
(vedi BEGIN/END),
poiché tali regole vengono eseguite prima che awk
cominci a
esaminare la lista degli argomenti.
I valori delle variabili dati sulla riga di comando sono elaborati per rimuovere sequenze di protezione (vedi Sequenze di protezione). (a.b.)
In alcune implementazioni di awk
molto vecchie, quando un
assegnamento di variabile capitava prima di un qualsiasi nome-file,
l’assegnamento avveniva prima che fosse stata eseguita la regola
BEGIN
. Il comportamento di awk
era in questo modo
ambiguo; alcuni assegnamenti da riga di comando erano disponibili
all’interno della regola BEGIN
, mentre altri no. Sfortunatamente,
alcune applicazioni finivano per essere dipendenti da questa
“funzionalità”. Quando awk
fu modificato per essere più
coerente, fu aggiunta l’opzione -v a beneficio delle
applicazioni che dipendevano dal vecchio comportamento.
La funzionalità dell’assegnamento di variabile è molto utile per assegnare
valori a variabili come RS
, OFS
, e ORS
, che controllano i
formati di input e di output, prima di effettuare la scansione dei file-dati.
È utile anche per effettuare passaggi multipli su un o stesso
file-dati. Per esempio:
awk 'pass == 1 { pass 1 stuff } pass == 2 { pass 2 stuff }' pass=1 mydata pass=2 mydata
Una volta disponibile la funzionalità per assegnare una variabile, l’opzione
-F per impostare il valore di FS
non è più strettamente
necessaria. Rimane per compatibilità all’indietro.
Successivo: Variabili d'ambiente, Precedente: Altri argomenti, Su: Invocare Gawk [Contenuti][Indice]
Capita spesso di voler leggere lo standard input assieme ad altri file. Per esempio, leggere un file, leggere lo standard input derivante da una pipe, e poi leggere un altro file.
Il modo di indicare lo standard input, con tutte le versioni di awk
,
è quello di usare un segno meno o trattino da solo, ‘-’. Per esempio:
qualche_comando | awk -f ilmioprogramma.awk file1 - file2
In questo caso, awk
legge prima file1, poi legge
l’output di qualche_comando, e infile legge
file2.
Si può anche usare "-"
per indicare lo standard input quando si leggono
i file con getline
(vedi Getline file).
In aggiunta, gawk
consente di specificare il
nome-file speciale /dev/stdin, sia sulla riga di comando che
quando si usa getline
.
Anche qualche altra versione di awk
include questa funzionalità,
ma non è standard.
(Alcuni sistemi operativi prevedono un file /dev/stdin
nel filesystem; comunque, gawk
elabora sempre
questo nome-file per conto suo [ossia non importa se il sistema
operativo rende disponibile il file o no].)
Successivo: Codice di ritorno, Precedente: Specificare lo standard input, Su: Invocare Gawk [Contenuti][Indice]
gawk
Diverse variabili d’ambiente influiscono sul comportamento
di gawk
.
• AWKPATH (Variabile): | Ricerca di programmi awk
in una lista di directory.
| |
• AWKLIBPATH (Variabile): | Ricerca di librerie condivise
awk su varie directory.
| |
• Altre variabili d'ambiente: | Le variabili d’ambiente. |
Successivo: AWKLIBPATH (Variabile), Su: Variabili d'ambiente [Contenuti][Indice]
awk
in una lista di directory.Nella maggior parte delle implementazioni di awk
si deve indicare il
percorso completo di ogni file di programma, a meno che il file non
sia nella directory corrente. Con gawk
, invece, se la
variabile nome-file impostata con le opzioni -f o -i non
contiene un separatore di directory ‘/’, gawk
cerca un file con
quel nome in un elenco di directory (chiamato percorso di ricerca),
scorrendole una per una.
Il percorso di ricerca è una stringa di nomi di directory separati da due
punti13. gawk
prende
il percorso di ricerca dalla variabile d’ambiente AWKPATH
. Se questa
variabile non esiste, o se ha un come valore la stringa nulla,
gawk
usa un percorso di default (descritto tra poco).
La funzionalità del percorso di ricerca è particolarmente utile per costruire
librerie di funzioni di awk
. I file di libreria possono essere messi
in una directory standard inclusa nel percorso di ricerca
e richiamati sulla riga di comando con un
nome-file breve. Altrimenti, si dovrebbe scrivere l’intero nome-file per
ciascun file.
Usando l’opzione -i, o l’opzione -f, i programmi di
awk
scritti sulla riga di comando possono usare le funzionalità
contenute nei file di libreria di awk
(vedi Funzioni di libreria).
La ricerca del percorso non viene eseguita se gawk
è in modalità di
compatibilità, sia con l’opzione --traditional che con l’opzione
--posix.
Vedi Opzioni.
Se il file del codice sorgente non viene trovato con una prima ricerca, il percorso viene cercato di nuovo dopo aver aggiunto il suffisso ‘.awk’ al nome-file.
Il meccanismo di ricerca del percorso di gawk
è simile a quello
della shell.
(Si veda The Bourne-Again SHell manual.)
Un elemento nullo nel percorso indica la directory corrente.
(Un elemento nullo è indicato iniziando o terminando il percorso con un segno
di ‘:’ oppure mettendo due ‘:’ consecutivi [‘::’].)
NOTA: Per includere la directory corrente nel percorso di ricerca, si può aggiungere . come un elemento del percorso di ricerca, oppure inserire un elemento nullo.
Diverse passate versioni di
gawk
avrebbero effettuato anche una ricerca esplicita nella directory corrente, prima o dopo aver esaminato il percorso di ricerca. A partire dalla versione 4.1.2, questo non vale più; se si desidera una ricerca nella directory corrente, è necessario aggiungere . esplicitamente, oppure aggiungendo un elemento nullo al percorso di ricerca.
Il valore di default di AWKPATH
è
‘.:/usr/local/share/awk’.14
Poiché . è incluso all’inizio, gawk
cerca dapprima nella
directory corrente, e poi in /usr/local/share/awk.
In pratica, questo vuol dire che solo raramente ci sarà bisogno di cambiare
il valore di AWKPATH
.
Vedi File da usare a inizio sessione, per informazioni su funzioni che possono
essere di aiuto per gestire la variabile AWKPATH
.
gawk
memorizza il valore del percorso di ricerca in uso in
ENVIRON["AWKPATH"]
. Questo consente di aver accesso al valore del
percorso di ricerca in uso all’interno di un programma awk
.
Sebbene la variabile ENVIRON["AWKPATH"]
possa
essere cambiata anche all’interno di
un programma awk
, questo non modifica il comportamento del
programma in esecuzione. Questo comportamento ha una sua logica: la variabile
d’ambiente AWKPATH
è usata per trovare i file sorgenti del programma; una
volta che il programma è in esecuzione, tutti i file sono stati trovati,
e gawk
non ha più bisogno di usare AWKPATH
.
Successivo: Altre variabili d'ambiente, Precedente: AWKPATH (Variabile), Su: Variabili d'ambiente [Contenuti][Indice]
awk
su varie directory.La variabile d’ambiente AWKLIBPATH
è simile alla variabile AWKPATH
,
ma è usata per ricercare estensioni caricabili (memorizzate come
librerie condivise di sistema) specificate con l’opzione -l,
anziché file sorgenti. Se l’estensione non viene trovata, il percorso viene
cercato nuovamente dopo aver aggiunto il suffisso per la libreria condivisa
appropriato per la piattaforma. Per esempio, sui sistemi GNU/Linux viene usato
il suffisso ‘.so’. Il percorso di ricerca specificato è usato anche
attraverso la direttiva @load
(vedi Caricare librerie condivise).
Se la variabile d’ambiente AWKLIBPATH
non esiste, o se ha come valore
la stringa nulla, gawk
usa un percorso di ricerca di default;
questo normalmente vale ‘/usr/local/lib/gawk’, anche se il suo valore
può essere diverso, a seconda di come è stato installato gawk
.
Vedi File da usare a inizio sessione, per informazioni su funzioni che possono
essere di aiuto per gestire la variabile AWKPATH
.
gawk
memorizza il valore del percorso di ricerca in uso in
ENVIRON["AWKLIBPATH"]
. Questo consente di aver accesso al valore del
percorso di ricerca in uso all’interno di un programma awk
.
Precedente: AWKLIBPATH (Variabile), Su: Variabili d'ambiente [Contenuti][Indice]
Molte altre variabili d’ambiente influenzano il comportamento di
gawk
, ma esse sono più specializzate. Quelle dell’elenco seguente
sono quelle più utili agli utenti normali:
GAWK_MSEC_SLEEP
Specifica l’intervallo tra due tentativi di riconnessione,
in millisecondi. Sui sistemi che non prevedono
la chiamata di sistema usleep()
,
il valore è arrotondato a un numero intero di secondi .
GAWK_READ_TIMEOUT
Specifica per quanto tempo, in millisecondi, gawk
aspetta l’input prima di emettere un messaggio di errore.
GAWK_SOCK_RETRIES
Controlla il numero di volte che gawk
cerca di
ristabilire una connessione bidirezionale TCP/IP (socket) prima di
rinunciare a farlo.
Vedi Reti TCP/IP.
Si noti che quando è attiva l’opzione di continuazione dopo errori di I/O
(vedi Continuazione dopo errori),
gawk
tenta di aprire un socket TCP/IP soltanto una volta.
POSIXLY_CORRECT
Provoca il passaggio di gawk
alla modalità di compatibilità POSIX,
disabilitando tutte le estensioni tradizionali e GNU.
Vedi Opzioni.
Le variabili d’ambiente nell’elenco che segue sono utili
soprattutto agli sviluppatori di gawk
per il collaudo e la messa
a punto del programma. Sono soggette a cambiamenti. Le variabili sono:
AWKBUFSIZE
Questa variabile riguarda solo gawk
installato su sistemi
conformi a POSIX.
Col valore di ‘exact’, gawk
usa la dimensione di ogni file di
input come dimensione del buffer di memoria da allocare per I/O. Altrimenti,
il valore dovrebbe essere un numero, e gawk
usa questo numero come
dimensione del buffer da allocare. (Quando questa variabile non è impostata,
gawk
usa la più piccola tra le dimensioni del file e la dimensione
del blocco di “default”, che normalmente è la dimensione del blocco I/O
del filesystem).
AWK_HASH
Se questa variabile è impostata con un valore di ‘gst’, gawk
usa la funzione hash di GNU Smalltalk per gestire i vettori.
Questa funzione può essere leggermente più veloce della funzione standard.
AWKREADFUNC
Se questa variabile esiste, gawk
legge i file sorgenti una riga per
volta, anziché a blocchi. Questa variabile è presente
per problemi di debug su filesystem di sistemi operativi non POSIX,
dove l’I/O è elaborato a record, non a blocchi.
GAWK_MSG_SRC
Se questa variabile esiste, gawk
include il nome-file e il
numero di riga all’interno del codice sorgente gawk
dal quale sono stati generati i messaggi di avvertimento o
i messaggi di errore grave. Il suo intento è quello di aiutare a isolare
l’origine di un messaggio, poiché ci possono essere più righe di codice che
producono lo stesso messaggio di avvertimento o di errore.
GAWK_LOCALE_DIR
Specifica la posizione dei file oggetto compilati contenenti la traduzione dei
messaggi emessi da gawk
stesso. Questa variabile è passata alla
funzione bindtextdomain()
nella fase di partenza di gawk
.
GAWK_NO_DFA
Se questa variabile esiste, gawk
non usa il riconoscitore di
espressioni regolari ASFD [automa a stati finiti deterministico] per i tipi di
test di corrispondenza. Questo può causare un rallentamento di gawk
.
Il suo intento è quello di aiutare a isolare le differenze tra i due
riconoscitori di espressioni regolari che gawk
usa internamente (non
dovrebbero esserci differenze, ma a volte la teoria non coincide con la
pratica).
GAWK_STACKSIZE
Specifica di quanto gawk
dovrebbe accrescere il suo stack di
valutazione interno, all’occorrenza.
INT_CHAIN_MAX
Specifica il numero massimo previsto di elementi che gawk
mantiene
su una catena hash per gestire i vettori indicizzati da numeri interi.
STR_CHAIN_MAX
Specifica il numero massimo previsto di elementi che gawk
mantiene
su una catena hash per gestire i vettori indicizzati da stringhe.
TIDYMEM
Se questa variabile esiste, gawk
usa le chiamate di libreria
mtrace()
della GNU C library per aiutare a scoprire
possibili sprechi di memoria.
Successivo: Includere file, Precedente: Variabili d'ambiente, Su: Invocare Gawk [Contenuti][Indice]
gawk
Se l’istruzione exit
viene usata con un valore
(vedi Istruzione exit), gawk
termina l’esecuzione con il valore
numerico specificato.
Altrimenti, se non ci sono stati problemi durante l’esecuzione,
gawk
esce col valore della costante C
EXIT_SUCCESS
, che normalmente è zero.
Se si verifica un errore, gawk
esce col valore della
costante C EXIT_FAILURE
, che normalmente è uguale a uno.
Se gawk
esce a causa di un errore grave, il codice di ritorno
è due. Sui sistemi non POSIX questo valore può essere mappato
a EXIT_FAILURE
.
Successivo: Caricare librerie condivise, Precedente: Codice di ritorno, Su: Invocare Gawk [Contenuti][Indice]
Questa
sezione descrive una funzionalità disponibile solo in
gawk
.
La direttiva @include
può essere usata per leggere file sorgenti
di awk
esterni. Questo dà la possibilità di suddividere file
sorgenti di awk
di grandi dimensioni in porzioni più piccole e più
maneggevoli, e anche di riutilizzare codice awk
di uso comune
da diversi script awk
. In altre parole, si possono
raggruppare funzioni di awk
usate per eseguire determinati compiti
all’interno di file esterni. Questi file possono essere usati proprio come
librerie di funzioni, usando la direttiva @include
assieme alla
variabile d’ambiente AWKPATH
. Si noti che i file sorgenti possono
venire inclusi anche usando l’opzione -i.
Vediamolo con un esempio.
Iniziamo con due script awk
(banali), che chiameremo
test1 e test2. Questo è lo script test1:
BEGIN { print "Questo è lo script test1." }
e questo è test2:
@include "test1" BEGIN { print "Questo è lo script test2." }
L’esecuzione di gawk
con test2
produce il seguente risultato:
$ gawk -f test2 -| Questo è lo script test1. -| Questo è lo script test2.
gawk
esegue lo script test2, il quale include
test1, usando la direttiva @include
.
Così, per includere file sorgenti di awk
esterni, basta usare
@include
seguito dal nome del file da includere,
racchiuso tra doppi apici.
NOTA: Si tenga presente che questo è un costrutto del linguaggio e che nome-file non può essere una variabile di tipo stringa, ma solo una costante di tipo letterale racchiusa tra doppi apici.
I file da includere possono essere nidificati; p.es., dato un terzo script, che chiameremo test3:
@include "test2" BEGIN { print "Questo è lo script test3." }
L’esecuzione di gawk
con lo script test3 produce i
seguenti risultati:
$ gawk -f test3 -| Questo è lo script test1. -| Questo è lo script test2. -| Questo è lo script test3.
Il nome-file, naturalmente, può essere un nome di percorso. Per esempio:
@include "../funzioni_di_i_o"
e:
@include "/usr/awklib/network"
sono entrambi percorsi validi. La variabile d’ambiente AWKPATH
può
rivestire grande importanza quando si usa @include
. Le stesse
regole per l’uso della variabile d’ambiente AWKPATH
nelle ricerche
da riga di comando
(vedi AWKPATH (Variabile)) si applicano anche a
@include
.
Questo è di grande aiuto nella costruzione di librerie di funzioni di
gawk
. Se si ha uno script di grandi dimensioni contenente
utili funzioni awk
di uso comune, lo si può suddividere in file
di libreria e mettere questi file in una directory dedicata. In seguito si
possono includere queste “librerie” usando il percorso completo dei
file, o impostando opportunamente la variabile d’ambiente AWKPATH
e
quindi usando @include
con la sola parte del percorso completo che
designa il file. Naturalmente,
si possono tenere i file di libreria in più di una directory;
più è complesso l’ambiente di lavoro, più
directory possono essere necessarie per organizzare i file da includere.
Vista la possibilità di specificare opzioni -f multiple, il
meccanismo @include
non è strettamente necessario.
Comunque, la direttiva @include
può essere d’aiuto nel costruire
programmi gawk
autosufficienti, riducendo così la necessità
di scrivere righe di comando complesse e tediose.
In particolare, @include
è molto utile per scrivere script CGI
eseguibili da pagine web.
Come è stato detto in AWKPATH (Variabile), i file sorgenti vengono
sempre cercati nella directory corrente, prima di eseguire la ricerca in
AWKPATH
; questo si applica anche ai file indicati con
@include
.
Successivo: Parti obsolete, Precedente: Includere file, Su: Invocare Gawk [Contenuti][Indice]
Questa
sezione descrive una funzionalità disponibile solo in
gawk
.
La direttiva @load
può essere usata per leggere estensioni di
awk
esterne (memorizzate come librerie condivise di sistema).
Questo consente di collegare del codice compilato che può offrire prestazioni
migliori o dare l’accesso a funzionalità estese non incluse nel linguaggio
awk
. La variabile AWKLIBPATH
viene usata per ricercare
l’estensione. Usare @load
é del tutto equivalente a usare l’opzione da
riga di comando -l.
Se l’estensione non viene trovata in AWKLIBPATH
, viene effettuata
un’altra ricerca dopo aver aggiunto al nome-file il suffisso della
libreria condivisa comunemente in uso per la piattaforma corrente. Per
esempio, sui sistemi GNU/Linux viene usato il suffisso ‘.so’:
$ gawk '@load "ordchr"; BEGIN {print chr(65)}' -| A
Questo è equivalente all’esempio seguente:
$ gawk -lordchr 'BEGIN {print chr(65)}' -| A
Per l’uso da riga di comando è più conveniente l’opzione -l,
ma @load
è utile da inserire all’interno di un file sorgente di
awk
che richieda l’accesso a un’estensione.
Estensioni dinamiche, descrive come scrivere estensioni (in C or C++)
che possono essere caricate sia con @load
che con l’opzione
-l. È anche descritta l’estensione ordchr
.
Successivo: Non documentato, Precedente: Caricare librerie condivise, Su: Invocare Gawk [Contenuti][Indice]
Questa
sezione descrive funzionalità o opzioni da riga di comando
provenienti da precedenti versioni di gawk
che non sono più
disponibili nella versione corrente, o che sono ancora utilizzabili ma sono
deprecate (ciò significa che non saranno presenti nella prossima
versione).
I file speciali relativi ai processi /dev/pid, /dev/ppid,
/dev/pgrpid e /dev/user erano deprecati, ma ancora disponibili,
in gawk
3.1. A partire dalla versione 4.0, non sono
più interpretati da gawk
in modo speciale (al loro posto usare
invece PROCINFO
; si veda Variabili auto-assegnate).
Successivo: Sommario invocazione, Precedente: Parti obsolete, Su: Invocare Gawk [Contenuti][Indice]
Usa il codice sorgente, Luke!
Questa sezione è stata lasciata intenzionalmente vuota.
Precedente: Non documentato, Su: Invocare Gawk [Contenuti][Indice]
awk
usare, o
‘awk 'programma' file’
o
‘awk -f file-del-programma file’.
awk
sono
-f, -F e -v. gawk
fornisce queste e
molte altre, come pure le opzioni estese corrispondenti scritte in stile GNU.
ARGV
. Modifiche a ARGC
e ARGV
influiscono su come awk
elabora l’input.
gawk
consente anche di usare il
nome-file speciale /dev/stdin.
gawk
tiene conto di diverse variabili d’ambiente;
AWKPATH
, AWKLIBPATH
e POSIXLY_CORRECT
sono le
più importanti.
gawk
invia informazioni al programma che lo
ha invocato. Usare l’istruzione exit
dall’interno di un programma
awk
per impostare il codice di ritorno.
gawk
consente di includere nel proprio programma file sorgenti di
awk
con la direttiva @include
o con le opzioni da riga di
comando -i e -f.
gawk
consente di caricare funzioni aggiuntive scritte in C
o C++ con la direttiva @load
e/o con l’opzione -l
(questa funzionalità avanzata è descritta più avanti, in
Estensioni dinamiche).
Successivo: Leggere file, Precedente: Invocare Gawk, Su: Top [Contenuti][Indice]
Una espressione regolare, o regexp, è un modo per descrivere un
insieme di stringhe.
Poiché le espressioni regolari sono una parte fondamentale della
programmazione in awk
, il loro formato e il loro uso meritano un
capitolo a sé stante.
Un’espressione regolare racchiusa tra barre (‘/’)
è un modello di ricerca awk
che individua tutti i record in input
il cui testo corrisponde al modello stesso.
L’espressione regolare più semplice è una sequenza di lettere o di numeri, o
di entrambi. Una tale regexp individua ogni stringa che contenga quella
particolare sequenza.
Quindi, la regexp ‘pippo’ individua ogni stringa che contenga
‘pippo’. In altre parole, al modello di ricerca /pippo/
corrisponde
ogni record in input che contiene i cinque caratteri consecutivi ‘pippo’
in qualsiasi parte del record. Altri tipi di regexp permettono
di specificare classi di stringhe molto più complesse.
All’inizio, gli esempi in questo capitolo sono semplici. Man mano che entriamo nei dettagli su come funzionano le espressioni regolari utilizzeremo formulazioni più complesse.
• Uso di regexp: | Come usare le espressioni regolari. | |
• Sequenze di protezione: | Come scrivere caratteri non stampabili. | |
• Operatori di espressioni regolari: | Operatori di espressioni regolari. | |
• Espressioni tra parentesi quadre: | Cosa possono contenere ‘[...]’. | |
• Più lungo da sinistra: | Quanto è lungo il testo individuato. | |
• Espressioni regolari calcolate: | Usare regexp dinamiche. | |
• Operatori di regexp GNU: | Operatori propri del software GNU. | |
• Maiuscolo-Minuscolo: | Fare confronti ignorando maiuscolo/minuscolo. | |
• Sommario espressioni regolari: | Sommario delle espressioni regolari. |
Successivo: Sequenze di protezione, Su: Espressioni regolari [Contenuti][Indice]
Un’espressione regolare può essere usata come modello di ricerca racchiudendola tra barre. L’espressione regolare è quindi confrontata con tutto il testo di ogni record (normalmente, basta che corrisponda a una parte qualsiasi del testo per risultare soddisfatta). Per esempio, il seguente programma stampa il secondo campo di ogni record in cui compaia la stringa ‘li’, in qualsiasi parte del record:
$ awk '/li/ { print $2 }' mail-list -| 555-5553 -| 555-0542 -| 555-6699 -| 555-3430
Espressioni regolari possono anche essere usate in espressioni di confronto.
Queste espressioni consentono di specificare le stringhe da riconoscere;
non devono necessariamente comprendere l’intero record corrente. I due
operatori ‘~’ e ‘!~’ confrontano espressioni regolari. Le
espressioni che usano questi operatori possono essere usate come modelli di
ricerca, o nelle istruzioni if
, while
, for
, e do
.
(Vedi Istruzioni.)
Per esempio:
exp ~ /regexp/
è verificata se l’espressione exp (intesa come stringa) corrisponde a regexp. L’esempio che segue individua, o sceglie, tutti i record in input in cui la lettera maiuscola ‘J’ è presente da qualche parte nel primo campo:
$ awk '$1 ~ /J/' inventory-shipped -| Jan 13 25 15 115 -| Jun 31 42 75 492 -| Jul 24 34 67 436 -| Jan 21 36 64 620
Lo stesso risultato si può ottenere anche così:
awk '{ if ($1 ~ /J/) print }' inventory-shipped
Il prossimo esempio chiede che l’espressione exp (intesa come stringa) NON corrisponda a regexp:
exp !~ /regexp/
L’esempio che segue individua o sceglie tutti i record in input il cui primo campo NON contiene la lettera maiuscola ‘J’:
$ awk '$1 !~ /J/' inventory-shipped -| Feb 15 32 24 226 -| Mar 15 24 34 228 -| Apr 31 52 63 420 -| May 16 34 29 208 …
Quando una regexp è racchiusa tra barre, come /pippo/
, la chiamiamo
una costante regexp, proprio come 5.27
è una costante
numerica e "pippo"
è una costante [di tipo] stringa.
Successivo: Operatori di espressioni regolari, Precedente: Uso di regexp, Su: Espressioni regolari [Contenuti][Indice]
Alcuni caratteri non possono essere inclusi letteralmente in costanti
stringa ("pippo"
) o in costanti regexp (/pippo/
).
Vanno invece rappresentati usando sequenze di protezione,
ossia sequenze di caratteri preceduti da una barra inversa (‘\’).
Una sequenza di protezione può essere usata per includere un carattere di
"doppio apice" in una costante stringa. Poiché un semplice doppio apice
termina la stringa, va usato ‘\"’ per richiedere che un doppio apice sia
presente all’interno di una stringa. Per esempio:
$ awk 'BEGIN { print "Egli le disse \"ciao!\"." }' -| Egli le disse "ciao!".
Lo stesso carattere di barra inversa è un altro carattere che non può essere
incluso normalmente; occorre scrivere ‘\\’ per inserire una barra
inversa nella stringa o regexp. Quindi, la stringa costituita dai due
caratteri ‘"’ e ‘\’ deve essere scritta come "\"\\"
.
Altre sequenze di protezione rappresentano caratteri non stampabili come TAB o il ritorno a capo. Anche se è possibile immettere la maggior parte dei caratteri non stampabili direttamente in una costante stringa o regexp, essi possono non essere di facile comprensione.
La seguente lista elenca
tutte le sequenze di protezione usate in awk
e
cosa rappresentano. Se non è detto altrimenti, tutte queste sequenze di
protezione valgono sia per costanti stringa che per costanti regexp:
\\
Barra inversa letterale, ‘\’.
\a
Il carattere “campanello”, Ctrl-g, codice ASCII 7 (BEL). (Spesso genera qualche tipo di segnale sonoro udibile.)
\b
Barra inversa, Ctrl-h, codice ASCII 8 (BS).
\f
Nuova pagina, Ctrl-l, codice ASCII 12 (FF).
\n
A capo, Ctrl-j, codice ASCII 10 (LF).
\r
Ritorno del carrello, Ctrl-m, codice ASCII 13 (CR).
\t
Tabulazione orizzontale, Ctrl-i, codice ASCII 9 (HT).
\v
Tabulazione verticale, Ctrl-k, codice ASCII 11 (VT).
\nnn
Il valore ottale nnn, dove nnn può essere da 1 a 3 cifre ottali, tra ‘0’ e ‘7’. Per esempio, il codice per il carattere ASCII ESC (escape) è ‘\033’.
\xhh…
Il valore esadecimale hh, dove hh indica una sequenza di cifre esadecimali (‘0’–‘9’, e ‘A’–‘F’ o ‘a’–‘f’). Dopo ‘\x’ è consentito un massimo di due cifre. Ogni ulteriore cifra esadecimale è considerata come una semplice lettera o numero. (e.c.) (La sequenza di protezione ‘\x’ non è permessa in POSIX awk.)
ATTENZIONE: In ISO C, la sequenza di protezione continua fino a raggiungere il primo carattere che non sia una cifra esadecimale. In passato,
gawk
avrebbe continuato ad aggiungere cifre esadecimali al valore finché non trovava una cifra non esadecimale oppure fino a raggiungere la fine della stringa. Comunque usare più di due cifre esadecimali produceva risultati indefiniti. Dalla versione 4.2, vengono elaborate solo due cifre.
\/
Una barra (necessario solo per costanti regexp).
Questa sequenza si usa per inserire una costante regexp
che contiene una barra
(come /.*:\/home\/[[:alnum:]]+:.*/
; la notazione ‘[[:alnum:]]’
verrà spiegata più avanti,
in
Espressioni tra parentesi quadre).
Poiché una regexp è racchiusa tra
barre, si deve proteggere ogni barra che sia parte dell’espressione, per dire
ad awk
di andare avanti a scandire il resto della regexp.
\"
Un doppio apice (necessario solo per costanti stringa).
Questa sequenza si usa per inserire in una costante stringa il carattere
doppio apice
(come "Egli le disse \"ciao!\"."
).
Poiché la stringa è racchiusa tra
doppi apici, si deve proteggere ogni doppio apice che sia parte della stringa
per dire ad awk
di andare avanti a elaborare il resto della stringa.
In gawk
, parecchie altre sequenze di due caratteri inizianti con
con una barra inversa hanno un significato speciale nelle regexp.
Operatori di regexp GNU.
In una regexp, una barra inversa che preceda un carattere non presente
nella lista precedente, e non elencato in
Operatori di regexp GNU,
significa che il carattere seguente dovrebbe essere preso letteralmente,
anche se normalmente sarebbe un operatore di regexp. Per esempio,
/a\+b/
individua i tre caratteri ‘a+b’.
Per una completa portabilità, non usare una barra inversa prima di qualsiasi carattere non incluso nella lista precedente, o che non sia un operatore.
Ricapitolando:
awk
legge il programma.
gawk
elabora sia costanti regexp che regexp dinamiche
(vedi Espressioni regolari calcolate),
per gli operatori speciali elencati in
Operatori di regexp GNU.
Sequenze di protezione per metacaratteri
Supponiamo che si usi una protezione ottale o esadecimale
per rappresentare un metacarattere di regexp
(si veda Operatori di espressioni regolari).
Storicamente, tali caratteri erano considerati letteralmente.
(a.b.)
Invece, lo standard POSIX richiede che siano considerati
come metacaratteri veri e propri, e questo è ciò che |
Successivo: Espressioni tra parentesi quadre, Precedente: Sequenze di protezione, Su: Espressioni regolari [Contenuti][Indice]
È possibile inserire in espressioni regolari dei caratteri speciali, detti operatori di espressioni regolari o metacaratteri, per aumentarne il potere e la versatilità.
Le sequenze di protezione descritte prima in Sequenze di protezione sono valide all’interno di una regexp. Sono precedute da una ‘\’ e sono riconosciute e convertite nei caratteri reali corrispondenti nella primissima fase dell’elaborazione delle regexp.
Ecco una lista dei metacaratteri. Tutti i caratteri che non sono sequenze di protezione e che non sono elencati qui rappresentano se stessi:
\
Si usa per togliere il significato speciale a un carattere quando si effettuano confronti. Per esempio, ‘\$’ individua il carattere ‘$’.
^
Si usa per indicare l’inizio di una stringa. Per esempio, ‘^@chapter’ individua ‘@chapter’ all’inizio di una stringa e si può usare per identificare inizi di capitoli in file sorgenti Texinfo. Il simbolo ‘^’ è conosciuto come àncora, perché àncora la ricerca solo all’inizio della stringa.
È importante notare che ‘^’ non individua un inizio di riga (il punto subito dopo un ritorno a capo ‘\n’) che si trovi all’interno di una stringa. La condizione non è verificata nell’esempio seguente:
if ("riga1\nRIGA 2" ~ /^R/) …
$
Simile a ‘^’, ma serve a indicare la fine di una stringa. Per esempio, ‘p$’ individua un record che termina con la lettera ‘p’. Il ‘$’ è un’àncora e non individua una fine di riga (il punto immediatamente prima di un carattere di ritorno a capo ‘\n’) contenuta in una stringa. La condizione nell’esempio seguente non è verificata:
if ("riga1\nRIGA 2" ~ /1$/) …
.
(punto)Individua un qualsiasi carattere, incluso il carattere di ritorno a capo. Per esempio, ‘.P’ individua ogni carattere in una stringa che sia seguito da una ‘P’. Usando la concatenazione, si può formare un’espressione regolare come ‘U.A’, che individua qualsiasi sequenza di tre caratteri che inizia con ‘U’ e finisce con ‘A’.
In modalità POSIX stretta (vedi Opzioni),
‘.’ non individua il carattere NUL,
ossia il carattere con tutti i bit uguali a zero.
In altri contesti, NUL è solo un carattere qualsiasi. Altre versioni
di awk
possono non essere in grado di individuare il carattere
NUL.
[
…]
Questa è chiamata una espressione tra parentesi quadre.15 Individua uno qualsiasi dei caratteri racchiusi tra parentesi quadre. Per esempio, ‘[MVX]’ individua uno qualsiasi dei caratteri ‘M’, ‘V’, o ‘X’ in una stringa. Una spiegazione esauriente di quel che si può mettere all’interno di un’espressione tra parentesi quadre è data in Espressioni tra parentesi quadre.
[^
…]
Questa è una espressione tra parentesi quadre complementata. Il primo carattere dopo la ‘[’ deve essere un ‘^’. Individua qualsiasi carattere tranne quelli tra parentesi quadre. Per esempio, ‘[^awk]’ individua qualsiasi carattere che non sia una ‘a’, ‘w’, o ‘k’.
|
Questo è un operatore alternativa ed è usato per specificare delle alternative. La ‘|’ ha la precedenza più bassa tra tutti gli operatori di espressioni regolari. Per esempio, ‘^P|[aeiouy]’ individua tutte le stringhe corrispondenti a ‘^P’ oppure a ‘[aeiouy]’. Ciò significa che individua qualsiasi stringa che inizi con ‘P’ o contenga (in qualsiasi posizione al suo interno) una vocale inglese minuscola.
L’alternativa si applica alle regexp più ampie individuabili in ogni lato.
(
…)
Le parentesi sono usate per raggruppare, sia nelle espressioni regolari sia in quelle aritmetiche. Si possono usare per concatenare espressioni regolari che contengono l’operatore alternativa, ‘|’. Per esempio, ‘@(samp|code)\{[^}]+\}’ individua sia ‘@code{pippo}’ sia ‘@samp{pluto}’. (Queste sono sequenze in linguaggio Texinfo per controllare la formattazione. Il significato di ‘+’ è spiegato più avanti in questa lista.)
*
Questo simbolo richiede che la precedente espressione regolare sia ripetuta tante volte quanto serve per trovare una corrispondenza. Per esempio, ‘ph*’ applica il simbolo ‘*’ al carattere ‘h’ che lo precede immediatamente e ricerca corrispondenze costituite da una ‘p’ seguita da un numero qualsiasi di ‘h’. Viene individuata anche solo la ‘p’, se non ci sono ‘h’.
Ci sono due sfumature da capire sul funzionamento di ‘*’. Primo, ‘*’ tiene conto solo del singolo componente dell’espressione regolare che lo precede (p.es., in ‘ph*’ vale solo per ‘h’). Per fare sì che ‘*’ si applichi a una sottoespressione più estesa, occorre metterla tra parentesi: ‘(ph)*’ individua ‘ph’, ‘phph’, ‘phphph’ e così via.
Secondo, ‘*’ trova quante più ripetizioni siano possibili. Se il testo da ricercare è ‘phhhhhhhhhhhhhhooey’, ‘ph*’ individua tutte le ‘h’.
+
Questo simbolo è simile a ‘*’, tranne per il fatto che l’espressione precedente deve essere trovata almeno una volta. Questo significa che ‘wh+y’ individuerebbe ‘why’ e ‘whhy’, ma non ‘wy’, mentre ‘wh*y’ li troverebbe tutti e tre.
?
Questo simbolo è simile a ‘*’, tranne per il fatto che l’espressione che precede può essere trovata una volta sola oppure non trovata affatto. Per esempio, ‘fe?d’ individua ‘fed’ e ‘fd’, ma nient’altro.
{
n}
{
n,}
{
n,
m}
Uno o due numeri tra parentesi graffe rappresentano una espressione di intervallo. Se c’è un numero tra graffe, la regexp precedente è ripetuta n volte. Se ci sono due numeri separati da una virgola, la regexp precedente è ripetuta da n a m volte. Se c’è un numero seguito da una virgola, allora la regexp precedente è ripetuta almeno n volte:
wh{3}y
Riconosce ‘whhhy’, ma non ‘why’ o ‘whhhhy’.
wh{3,5}y
Riconosce soltanto ‘whhhy’, ‘whhhhy’, o ‘whhhhhy’.
wh{2,}y
Riconosce ‘whhy’, ‘whhhy’ e così via.
Le espressioni di intervallo non erano tradizionalmente disponibili in
awk
. Sono state aggiunte come parte dello standard POSIX per
rendere awk
ed egrep
coerenti tra di loro.
In passato, poiché vecchi programmi possono usare ‘{’ e ‘}’ in
costanti regexp,
gawk
non riconosceva espressioni di intervallo
nelle regexp.
Comunque, a partire dalla versione 4.0,
gawk
riconosce espressioni di intervallo per default.
Ciò accade perché la compatibilità con POSIX è ritenuta più
importante da molti utenti gawk
rispetto alla compatibilità con
dei vecchi programmi.
Per programmi che usano ‘{’ e ‘}’ in costanti regexp,
è buona pratica proteggerli sempre con una barra inversa. Allora le
costanti regexp sono valide e si comportano come desiderato, usando
qualsiasi versione di awk
.16
Infine, quando ‘{’ e ‘}’ appaiono in costanti regexp
in un modo non interpretabile come espressione di intervallo
(come in /q{a}/
), allora sono prese letteralmente.
Nelle espressioni regolari, gli operatori ‘*’, ‘+’, e ‘?’,
come pure le parentesi graffe ‘{’ e ‘}’,
hanno
la precedenza più alta, seguiti dalla concatenazione, e infine da ‘|’.
Come nell’algebra, le parentesi possono cambiare il raggruppamento degli
operatori.
In POSIX awk
e in gawk
, gli operatori ‘*’,
‘+’, e ‘?’ rappresentano se stessi quando non c’è nulla
nella regexp che li precede. Per esempio, /+/
individua un
semplice segno più. Comunque, molte altre versioni di awk
trattano una simile notazione come un errore di sintassi.
Se gawk
è in modalità compatibile (vedi Opzioni), le espressioni
di intervallo non si possono usare nelle espressioni regolari.
Successivo: Più lungo da sinistra, Precedente: Operatori di espressioni regolari, Su: Espressioni regolari [Contenuti][Indice]
Come detto sopra, un’espressione tra parentesi quadre individua qualsiasi carattere incluso tra le parentesi quadre aperta e chiusa.
All’interno di un’espressione tra parentesi quadre, una
espressione di intervallo è formata da due caratteri separati da un
trattino. Individua ogni singolo carattere compreso tra i due caratteri,
ordinati secondo l’insieme di caratteri in uso nel sistema. Per esempio,
‘[0-9]’ è equivalente a ‘[0123456789]’.
(Si veda Intervalli e localizzazione per una spiegazione di come
lo standard POSIX e gawk
sono cambiati nel corso degli anni.
La cosa ha un interesse principalmente storico.)
Con la crescente popolarità dello standard di caratteri Unicode, c’è un’ulteriore dettaglio da tenere in conto. Le sequenze di protezione ottali ed esadecimali utilizzabili per inserire valori all’interno di espressioni tra parentesi quadre sono considerate contenere solo caratteri che occupano un unico byte (caratteri il cui valore stia nell’intervallo 0–256). Per individuare un intervallo di caratteri in cui i punti di inizio e fine dell’intervello abbiano valori maggiori di 256, occorre immettere direttamente le codifiche multi-byte dei caratteri in questione.
Per includere uno dei caratteri ‘\’, ‘]’, ‘-’, o ‘^’ in un’espressione tra parentesi quadre, occorre inserire un ‘\’ prima del carattere stesso. Per esempio:
[d\]]
individua sia ‘d’ che ‘]’. Inoltre, se si mette una ‘]’ subito dopo la ‘[’ aperta, la parentesi quadra chiusa è considerata come uno dei caratteri da individuare.
L’utilizzo di ‘\’ nelle espressioni tra parentesi quadre
è compatibile con altre implementazioni di awk
ed è anche richiesto
da POSIX.
Le espressioni regolari in awk
sono un insieme più esteso delle
specificazioni POSIX per le espressioni regolari estese (ERE).
Le ERE POSIX sono basate sulle espressioni regolari accettate dal
tradizionale programma di utilità egrep
.
Le classi di caratteri sono una funzionalità introdotta nello standard POSIX. Una classe di caratteri è una particolare notazione per descrivere liste di caratteri cha hanno un attributo specifico, ma i caratteri veri e propri possono variare da paese a paese e/o da insieme di caratteri a insieme di caratteri. Per esempio, la nozione di cosa sia un carattere alfabetico è diversa tra gli Stati Uniti e la Francia.
Una classe di caratteri è valida solo in una regexp contenuta tra le parentesi quadre di un’espressione tra parentesi quadre. Le classi di caratteri consistono di ‘[:’, una parola chiave che segnala la classe, e ‘:]’. La Tabella 3.1 elenca le classi di caratteri definite dallo standard POSIX.
Classe | Significato |
---|---|
[:alnum:] | Caratteri alfanumerici. |
[:alpha:] | Caratteri alfabetici. |
[:blank:] | Caratteri spazio e TAB. |
[:cntrl:] | Caratteri di controllo. |
[:digit:] | Caratteri numerici. |
[:graph:] | Caratteri che sono stampabili e visibili. (Uno spazio è stampabile ma non visibile, mentre una ‘a’ è l’uno e l’altro.) |
[:lower:] | Caratteri alfabetici minuscoli. |
[:print:] | Caratteri stampabili (caratteri che non sono caratteri di controllo). |
[:punct:] | Caratteri di punteggiatura (caratteri che non sono lettere, cifre, caratteri di controllo, o caratteri di spazio). |
[:space:] | Caratteri di spazio (come spazio, TAB, e formfeed, per citarne alcuni). |
[:upper:] | Caratteri alfabetici maiuscoli. |
[:xdigit:] | Caratteri che sono cifre esadecimali. |
Tabella 3.1: classi di caratteri POSIX
Per esempio, prima dello standard POSIX, si doveva scrivere
/[A-Za-z0-9]/
per individuare i
caratteri alfanumerici. Se l’insieme
di caratteri in uso comprendeva altri caratteri alfabetici, l’espressione
non li avrebbe individuati.
Con le classi di caratteri POSIX si può scrivere
/[[:alnum:]]/
per designare i caratteri alfabetici e numerici
dell’insieme di caratteri in uso.
Alcuni programmi di utilità che cercano espressioni regolari prevedono
una classe di caratteri, non standard,
‘[:ascii:]’; awk
non la prevede. Tuttavia, è possibile ottenere
lo stesso risultato utilizzando ‘[\x00-\x7F]’. Quest’espressione
individua tutti i valori numerici tra zero e 127, che è l’intervallo definito
dell’insieme di caratteri ASCII. Usando una lista di caratteri che esclude
(‘[^\x00-\x7F]’) si individuano tutti i caratteri mono-byte che non
sono nell’intervallo ASCII.
In espressioni tra parentesi quadre possono apparire due ulteriori sequenze speciali. Riguardano insiemi di caratteri non-ASCII, che possono avere simboli singoli (chiamati elementi di collazione) che sono rappresentati con più di un carattere. Possono designare anche parecchi caratteri che sono equivalenti tra loro ai fini della collazione, o dell’ordinamento. (Per esempio, in francese, la semplice “e” e la sua versione con accento grave “è” sono equivalenti). Queste sequenze sono:
Elementi di collazione multi-byte racchiusi fra ‘[.’ e ‘.]’. Per esempio, se ‘ch’ è un elemento di collazione, ‘[[.ch.]]’ è una regexp che individua questo elemento di collazione, mentre ‘[ch]’ è una regexp che individua le lettere ‘c’ o ‘h’.
Sono nomi, specifici a una particolare localizzazione, per una lista di caratteri equivalenti tra loro. Il nome è racchiuso fra ‘[=’ e ‘=]’. Per esempio, il nome ‘e’ potrebbe essere usato per designare “e”, “ê”, “è”, e “é”. In questo caso, ‘[[=e=]]’ è una regexp che corrisponde a ‘e’, ‘ê’, ‘è’ e ‘é’.
Queste funzionalità sono molto utili in localizzazioni non inglesi.
ATTENZIONE: Le funzioni di libreria che
gawk
usa per individuare le espressioni regolari per ora riconoscono solo le classi di caratteri POSIX; non riconoscono simboli di collazione o classi di equivalenza.
In un’espressione tra parentesi quadre, una parentesi aperta (‘[’) che non costituisca l’inizio della specificazione di una classe di caratteri, di simboli di collazione o di una classe di equivalenza è interpretata letteralmente. Questo vale anche per ‘.’ e ‘*’.
Successivo: Espressioni regolari calcolate, Precedente: Espressioni tra parentesi quadre, Su: Espressioni regolari [Contenuti][Indice]
Si consideri il caso seguente:
echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
Questo esempio usa la funzione sub()
per modificare il record in input.
(sub()
sostituisce la prima ricorrenza in ogni testo individuato dal
primo argomento con la stringa fornita come secondo argomento;
vedi Funzioni per stringhe.) Qui, la regexp /a+/
richiede
“uno o più caratteri ‘a’,” e il testo da sostituire è ‘<A>’.
L’input contiene quattro caratteri ‘a’.
Le espressioni regolari awk
(e POSIX) individuano sempre
la sequenza più lunga, partendo da sinistra, di caratteri in input che
corrispondono. Quindi, tutti e quattro i caratteri ‘a’ sono
rimpiazzati con ‘<A>’ in questo esempio:
$ echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }' -| <A>bcd
Per semplici test corrisponde/non corrisponde, la cosa ha poca importanza.
Ma se si sta controllando un testo o si fanno sostituzioni usando le funzioni
match()
, sub()
, gsub()
e gensub()
,
è invece molto importante.
Tenere in conto questo principio è importante anche quando si suddividono
record e campi usando delle regexp (vedi Record,
e anche vedi Separatori di campo).
Successivo: Operatori di regexp GNU, Precedente: Più lungo da sinistra, Su: Espressioni regolari [Contenuti][Indice]
L’espressione a destra di un operatore ‘~’ o ‘!~’ non deve necessariamente essere una costante regexp (cioè, una stringa di caratteri tra barre). Può essere una qualsiasi espressione. L’espressione è valutata e convertita in una stringa se necessario; il contenuto della stringa è poi usato come una regexp. Una regexp calcolata in questo modo è detta una regexp dinamica o una regexp calcolata:
BEGIN { regexp_numerica = "[[:digit:]]+" } $0 ~ regexp_numerica { print }
Questo script imposta regexp_numerica
come una regexp che
descrive una o più cifre, e poi controlla se un record in input corrisponde a
questa regexp.
NOTA: Usando gli operatori ‘~’ e ‘!~’, si tenga presente che c’è una differenza tra una costante regexp racchiusa tra barre e una costante stringa racchiusa tra doppi apici. Se si intende utilizzare una costante stringa, occorre comprendere che la stringa è, in sostanza, scandita due volte: la prima volta quando
awk
legge il programma, e la seconda volta quando va a confrontare la stringa a sinistra dell’operatore con il modello che sta alla sua destra. Questo vale per ogni espressione (come laregexp_numerica
, vista nel precedente esempio), non solo per le costanti stringa.
Che differenza fa la doppia scansione di una stringa? La risposta ha a che vedere con le sequenze di protezione e particolarmente con le barre inverse. Per inserire una barra inversa in un’espressione regolare all’interno di una stringa, occorre inserire due barre inverse.
Per esempio, /\*/
è una costante regexp per designare un ‘*’
letterale.
È richiesta una sola barra inversa. Per fare lo stesso con una stringa,
occorre immettere "\\*"
. La prima barra inversa protegge la
seconda in modo che la stringa in realtà contenga i
due caratteri ‘\’ e ‘*’.
Dato che si possono usare sia costanti regexp che costanti stringa per descrivere espressioni regolari, qual è da preferire? La risposta è “costanti regexp”, per molti motivi:
awk
può accorgersi
che è stata fornita una regexp e memorizzarla internamente in una forma
che rende la ricerca di corrispondenze più efficiente. Se si usa una costante
stringa, awk
deve prima convertire la stringa nel suo formato
interno e quindi eseguire la ricerca di corrispondenze.
Successivo: Maiuscolo-Minuscolo, Precedente: Espressioni regolari calcolate, Su: Espressioni regolari [Contenuti][Indice]
gawk
Il software GNU che ha a che fare con espressioni regolari comprende alcuni
operatori regexp aggiuntivi. Questi
operatori sono descritti in
questa
sezione e sono specificamente
per gawk
; non sono disponibili in altre implementazioni di
awk
.
La maggior parte degli operatori aggiuntivi riguarda l’identificazione di
parole. Ai nostri fini, una parola è una sequenza di uno o più lettere,
cifre, o trattini bassi (‘_’):
\s
Corrisponde a ogni carattere bianco. Lo si può pensare come un’abbreviazione di ‘[[:space:]]’.
\S
Corrisponde a ogni carattere che non è uno spazio bianco. Lo si può pensare come un’abbreviazione di ‘[^[:space:]]’.
\w
Corrisponde a ogni carattere che componga una parola; ovvero, corrisponde a ogni lettera, cifra, o trattino basso. Lo si può pensare come un’abbreviazione di ‘[[:alnum:]_]’.
\W
Corrisponde a ogni carattere che non è parte di una parola. Lo si può pensare come un’abbreviazione di ‘[^[:alnum:]_]’.
\<
Individua la stringa nulla all’inizio di una parola.
Per esempio, /\<via/
individua ‘via’ ma non
‘funivia’.
\>
Individua la stringa nulla alla fine di una parola.
Per esempio, /via\>/
individua ‘via’ ma non ‘viadotto’.
\y
Individua la stringa nulla o alla fine o all’inizio di una parola. (cioè, il limite di una parola - boundary in inglese). Per esempio, ‘\yradar?\y’ individua sia ‘rada’ che ‘radar’, come parole separate.
\B
Individua la stringa nulla che ricorre all’interno di una parola.
Per esempio,
/\Bora\B/
individua ‘Colorado’, ma non individua ‘che ora è’.
‘\B’ è essenzialmente l’opposto di ‘\y’.
Ci sono due altri operatori che operano sui buffer. In Emacs un
buffer è, naturalmente, un buffer di Emacs. In altri programmi GNU,
fra cui gawk
, le routine di libreria delle regexp considerano
come buffer l’intera stringa su cui effettuare il confronto.
Gli operatori sono:
\`
Individua la stringa nulla che occorre all’inizio di un buffer (di una stringa)
\'
Individua la stringa nulla che occorre alla fine di un buffer (di una stringa)
Poiché ‘^’ e ‘$’ si riferiscono sempre all’inizio e alla
fine di stringhe, questi operatori non aggiungono nuove funzionalità
ad awk
. Sono inclusi per compatibilità con altro
software GNU.
In altro software GNU, l’operatore di limite-di-parola è ‘\b’. Questo,
comunque, è in conflitto con la definizione, nel linguaggio awk
,
di ‘\b’ come
backspace, quindi gawk
usa una lettera differente.
Un metodo alternativo sarebbe stato di richiedere due barre inverse negli
operatori GNU, ma questo è stato ritenuto troppo arzigogolato. Il metodo
corrente di usare ‘\y’ al posto del ‘\b’ di GNU sembra essere
il male minore.
Le varie opzioni sulla riga di comando
(vedi Opzioni)
controllano come gawk
interpreta i caratteri nelle regexp:
Per default, gawk
fornisce tutte le funzionalità delle
regexp POSIX e gli
operatori regexp GNU
predecentemente descritti.
Sono descritti
in Operatori di espressioni regolari.
--posix
Sono ammesse solo le regexp POSIX; gli operatori GNU non sono speciali (p.es., ‘\w’ individua una semplice lettera ‘w’). Le espressioni di intervallo sono ammesse.
--traditional
Le regexp Unix tradizionali di awk
sono ammesse. Gli
operatori GNU non sono speciali, e le espressioni
di intervallo non sono ammesse.
Le classi di caratteri POSIX (‘[[:alnum:]]’, etc.) sono ammesse,
poiché BWK awk
le prevede.
I caratteri descritti usando sequenze di protezione ottali ed esadecimali sono
trattati letteralmente, anche se rappresentano metacaratteri di regexp.
--re-interval
Sono consentite espressioni di intervallo in regexp, se --traditional è stata specificata. Altrimenti, le espressioni di intervallo sono disponibili per default.
Successivo: Sommario espressioni regolari, Precedente: Operatori di regexp GNU, Su: Espressioni regolari [Contenuti][Indice]
Il tipo di carattere (maiuscolo/minuscolo) è normalmente rilevante nelle espressioni regolari, sia nella ricerca di caratteri normali (cioè, non metacaratteri), sia all’interno di espressioni fra parentesi. Quindi, una ‘w’ in un’espressione regolare individua solo una ‘w’ e non la corrispondente maiuscola ‘W’.
Il modo più semplice per richiedere una ricerca non sensibile al maiuscolo/minuscolo è di usare un’espressione tra parentesi quadre, per esempio ‘[Ww]’. Comunque, questo può essere pesante se si usa spesso, e può rendere le espressioni regolari di difficile lettura. Ci sono due alternative che potrebbero essere preferibili.
Un modo per fare un confronto non sensibile a maiuscolo/minuscolo in un
particolare punto del programma
è di convertire i dati in un solo tipo (o minuscole o maiuscole),
usando le funzioni di stringa
predefinite tolower()
o toupper()
(che non
abbiamo ancora introdotto;
vedi Funzioni per stringhe).
Per esempio:
tolower($1) ~ /foo/ { … }
converte il primo campo in minuscole, prima di fare un confronto.
Questo funziona in ogni awk
conforme allo standard POSIX.
Un altro metodo, proprio di gawk
, è di impostare la variabile
IGNORECASE
a un valore diverso da zero (vedi Variabili predefinite).
Quando IGNORECASE
è diverso da zero, tutte le operazioni con
regexp e stringhe ignorano la distinzione maiuscolo/minuscolo.
Il cambio del valore di IGNORECASE
controlla dinamicamente la
sensibilità a maiuscolo/minuscolo del programma quando è in esecuzione.
Il tipo di carattere (maiuscolo/minuscolo) è rilevante per default,
poiché IGNORECASE
(come la maggior parte delle variabili) è
inizializzata a zero:
x = "aB" if (x ~ /ab/) … # questo test non risulterà verificato IGNORECASE = 1 if (x ~ /ab/) … # adesso sarà verificato
In generale, non è possibile usare IGNORECASE
per rendere certe regole
non sensibili a maiuscolo/minuscolo e altre regole invece sì, perché non c’è
una maniera diretta per impostare
IGNORECASE
solo per l’espressione di
una particolare regola.17
Per fare questo, si usino espressioni tra parentesi quadre oppure
tolower()
. Comunque, una cosa che si può fare con IGNORECASE
soltanto è di utilizzare o di ignorare la sensibilità a maiuscolo/minuscolo
per tutte le regole contemporaneamente.
IGNORECASE
è impostabile dalla riga di comando o in una regola
BEGIN
(vedi Altri argomenti; e
vedi Usare BEGIN/END).
Impostare IGNORECASE
dalla riga di comando è un modo per rendere
un programma insensibile a maiuscolo/minuscolo senza doverlo modificare.
In localizzazioni multibyte, le equivalenze tra caratteri maiuscoli e minuscoli sono controllate usando i valori in formato esteso dell’insieme di caratteri della localizzazione. Per il resto, i caratteri sono controllati usando l’insieme di caratteri ISO-8859-1 (ISO Latin-1). Questo insieme di caratteri è un’estensione del tradizionale insieme con 128 caratteri ASCII, che include anche molti caratteri adatti per le lingue europee.18
Il valore di IGNORECASE
non ha effetto se gawk
è in
modalità compatibile (vedi Opzioni).
Il tipo di carattere (maiuscolo o minuscolo) è sempre rilevante in modalità
compatibile.
Precedente: Maiuscolo-Minuscolo, Su: Espressioni regolari [Contenuti][Indice]
awk
, le costanti regexp sono scritte racchiuse
fra barre: /
…/
.
gawk
IGNORECASE
consente di controllare la
differenza maiuscolo/minuscolo nel confronto mediante regexp. In altre
versioni di awk
, vanno usate invece le funzioni tolower()
o
toupper()
.
Successivo: Stampare, Precedente: Espressioni regolari, Su: Top [Contenuti][Indice]
Nel tipico programma awk
,
awk
legge tutto l’input sia dallo standard input
(per default è la tastiera, ma spesso è una pipe da un altro comando)
o da file i cui nomi vengono specificati sulla riga di comando di
awk
. Se si specificano file in input, awk
li legge
nell’ordine, elaborando tutti i dati di uno prima di passare al successivo.
Il nome del file in input corrente si trova nella variabile predefinita
FILENAME
(vedi Variabili predefinite).
L’input è letto in unità chiamate record, e viene elaborato, secondo le regole del programma, un record alla volta. Per default, ogni record è una riga. Ogni record è suddiviso automaticamente in "pezzi" chiamati campi. Questo rende più pratico far lavorare i programmi sulle parti di un record.
In rare occasioni, si potrebbe aver bisogno di usare il comando
getline
. Il comando getline
è utile sia perché può procurare
un input esplicito da un numero indeterminato di file, sia perché non vanno
specificati sulla riga di comando di awk
i nomi dei file usati con
getline (vedi Getline).
• Record: | Controllare come i dati sono suddivisi in record. | |
• Campi: | Un’introduzione ai campi. | |
• Campi non costanti: | Numeri di campo variabili. | |
• Cambiare i campi: | Cambiare il contenuto di un campo. | |
• Separatori di campo: | I separatori di campo, e come cambiarli. | |
• Dimensione costante: | Leggere campi di larghezza costante. | |
• Separazione in base al contenuto: | Definire campi dal loro contenuto. | |
• Righe multiple: | Leggere record che sono su più righe. | |
• Getline: | Leggere file sotto il controllo del
programma, usando la funzione
getline .
| |
• Timeout in lettura: | Leggere input entro un tempo limite. | |
• Proseguire dopo errore in input: | Elaborare ulteriore input dopo certi errori di I/O. | |
• Directory su riga di comando: | Cosa succede mettendo una directory sulla riga di comando. | |
• Sommario di Input: | Sommario di Input. | |
• Esercizi su Input: | Esercizi. |
Successivo: Campi, Su: Leggere file [Contenuti][Indice]
awk
suddivide l’input per il programma in record e campi.
Tiene traccia del numero di record già letti dal
file in input corrente. Questo valore è memorizzato in una variabile
predefinita chiamata FNR
che è reimpostata a zero ogni volta che si
inizia un nuovo file. Un’altra variabile predefinita, NR
, registra il
numero totale di record in input già letti da tutti i file-dati.
Il suo valore iniziale è zero ma non viene mai reimpostata a zero
automaticamente.
• awk divisione record: | Come awk standard divide i record.
| |
• gawk divisione record: | Come gawk divide i record.
|
Successivo: gawk divisione record, Su: Record [Contenuti][Indice]
awk
standard divide i record.I record sono separati da un carattere chiamato separatore di record.
Per default, il separatore di record è il carattere di ritorno a capo.
Questo è il motivo per cui i record sono, per default, righe singole.
Per usare un diverso carattere come separatore di record
basta assegnare quel carattere alla variabile predefinita RS
.
Come per ogni altra variabile,
il valore di RS
può essere cambiato nel programma awk
con l’operatore di assegnamento, ‘=’
(vedi Operatori di assegnamento).
Il nuovo separatore di record dovrebbe essere racchiuso tra doppi apici,
per indicare una costante di stringa. Spesso il momento giusto per far questo
è all’inizio dell’esecuzione, prima che sia elaborato qualsiasi input,
in modo che il primo record sia letto col separatore appropriato.
Per far ciò, si usa il criterio speciale BEGIN
(vedi BEGIN/END).
Per esempio:
awk 'BEGIN { RS = "u" } { print $0 }' mail-list
cambia il valore di RS
in ‘u’, prima di leggere qualsiasi input.
Il nuovo valore è una stringa il cui primo carattere è la lettera “u”; come
risultato, i record sono separati dalla lettera “u”. Poi viene letto il
file in input, e la seconda regola nel programma awk
(l’azione
eseguita se non si specifica un criterio)
stampa ogni record. Poiché ogni istruzione print
aggiunge
un ritorno a capo alla fine del suo output, questo programma
awk
copia l’input con ogni ‘u’ trasformato in un ritorno
a capo. Qui vediamo il risultato dell’esecuzione del programma sul file
mail-list:
$ awk 'BEGIN { RS = "u" } > { print $0 }' mail-list -| Amelia 555-5553 amelia.zodiac -| sq -| e@gmail.com F -| Anthony 555-3412 anthony.assert -| ro@hotmail.com A -| Becky 555-7685 becky.algebrar -| m@gmail.com A -| Bill 555-1675 bill.drowning@hotmail.com A -| Broderick 555-0542 broderick.aliq -| otiens@yahoo.com R -| Camilla 555-2912 camilla.inf -| sar -| m@skynet.be R -| Fabi -| s 555-1234 fabi -| s. -| ndevicesim -| s@ -| cb.ed -| F -| J -| lie 555-6699 j -| lie.perscr -| tabor@skeeve.com F -| Martin 555-6480 martin.codicib -| s@hotmail.com A -| Sam -| el 555-3430 sam -| el.lanceolis@sh -| .ed -| A -| Jean-Pa -| l 555-2127 jeanpa -| l.campanor -| m@ny -| .ed -| R -|
Si noti che la voce relativa al nome ‘Bill’ non è divisa. Nel file-dati originale (vedi File dati di esempio), la riga appare in questo modo:
Bill 555-1675 bill.drowning@hotmail.com A
Essa non contiene nessuna ‘u’, per cui non c’è alcun motivo di dividere
il record, diversamente dalle altre, che hanno una o più ricorrenze della
‘u’. Infatti, questo record è trattato come parte del record precedente;
il ritorno a capo che li separa nell’output è l’originale ritorno a capo nel
file-dati, non quella aggiunta da awk
quando ha stampato il record!
Un altro modo per cambiare il separatore di record è sulla riga di comando, usando la funzionalità dell’assegnamento di variabile (vedi Altri argomenti):
awk '{ print $0 }' RS="u" mail-list
Questo imposta RS
a ‘u’ prima di elaborare mail-list.
Usando un carattere alfabetico come ‘u’ come separatore di record è molto probabile che si ottengano risultati strani. Usando un carattere insolito come ‘/’ è più probabile che si ottenga un comportamento corretto nella maggioranza dei casi, ma non c’è nessuna garanzia. La morale è: conosci i tuoi dati!
Quando si usano caratteri normali come separatore di record,
c’è un caso insolito che capita quando gawk
è reso completamente conforme a POSIX (vedi Opzioni).
In quel caso, la seguente (estrema) pipeline stampa un sorprendente
‘1’:
$ echo | gawk --posix 'BEGIN { RS = "a" } ; { print NF }' -| 1
C’è un solo campo, consistente in un ritorno a capo. Il valore della
variabile predefinita NF
è il numero di campi nel record corrente.
(Normalmente gawk
tratta il ritorno a capo come uno spazio
vuoto, stampando ‘0’ come risultato. Anche molte altre versioni di
awk
agiscono in questo modo.)
Il raggiungimento della fine di un file in input fa terminare il record di
input corrente, anche se l’ultimo carattere nel file non è il carattere in
RS
. (a.b.)
La stringa nulla ""
(una stringa che non contiene alcun carattere)
ha un significato particolare come
valore di RS
. Significa che i record sono separati
soltanto da una o più righe vuote.
Vedi Righe multiple per maggiori dettagli.
Se si cambia il valore di RS
nel mezzo di un’esecuzione di
awk
, il nuovo valore è usato per delimitare i record successivi, ma
non riguarda il record in corso di elaborazione e neppure quelli già
elaborati.
Dopo che è stata determinata la fine di un record, gawk
imposta la variabile RT
al testo nell’input che corrisponde a
RS
.
Precedente: awk divisione record, Su: Record [Contenuti][Indice]
gawk
Quando si usa gawk
,
il valore di RS
non è limitato a una stringa costituita da un solo
carattere, ma può essere qualsiasi espressione regolare
(vedi Espressioni regolari). (e.c.)
In generale, ogni record termina alla stringa più vicina che corrisponde
all’espressione regolare; il record successivo inizia alla fine della stringa
che corrisponde. Questa regola generale è in realtà applicata anche nel caso
normale, in cui RS
contiene solo un ritorno a capo: un record
termina all’inizio della prossima stringa che corrisponde (il prossimo
ritorno a capo nell’input), e il record seguente inizia subito dopo la
fine di questa stringa (al primo carattere della riga seguente).
Il ritorno a capo, poiché corrisponde a RS
, non appartiene a
nessuno dei due record.
Quando RS
è un singolo carattere, RT
contiene lo stesso singolo carattere. Peraltro, quando RS
è
un’espressione regolare, RT
contiene l’effettivo testo in input
corrispondente all’espressione regolare.
Se il file in input termina senza che vi sia un testo che corrisponda a
RS
, gawk
imposta RT
alla stringa nulla.
Il seguente esempio illustra entrambe queste caratteristiche.
In quest’esempio RS
è impostato a un’espressione regolare che
cerca sia un ritorno a capo che una serie di una o più lettere
maiuscole con uno spazio vuoto opzionale iniziale e/o finale:
$ echo record 1 AAAA record 2 BBBB record 3 | > gawk 'BEGIN { RS = "\n|( *[[:upper:]]+ *)" } > { print "Record =", $0,"e RT = [" RT "]" }' -| Record = record 1 e RT = [ AAAA ] -| Record = record 2 e RT = [ BBBB ] -| Record = record 3 e RT = [ -| ]
Le parentesi quadre racchiudono il contenuto di RT
, rendendo visibile
lo spazio vuoto iniziale e quello finale. L’ultimo valore di
RT
è un ritorno a capo.
Vedi Programma sed semplice per un esempio più utile
su RS
come espressione regolare e su RT
.
Se si imposta RS
a un’espressione regolare che consente del testo
finale opzionale, come ‘RS = "abc(XYZ)?"’ è possibile, per via di
limitazioni dell’implementazione, che gawk
possa trovare la parte
iniziale dell’espressione regolare, ma non la parte finale, in modo
particolare se il testo di input che potrebbe avere una corrispondenza con la
parte finale è piuttosto lungo. gawk
cerca di evitare questo
problema, ma al momento non ci sono garanzie che questo funzioni sempre.
NOTA: Si ricordi che in
awk
, i metacaratteri di ancoraggio ‘^’ e ‘$’ trovano l’inizio e la fine di una stringa, e non l’inizio e la fine di una riga. Come risultato, qualcosa come ‘RS = "^[[:upper:]]"’ può solo corrispondere all’inizio di un file. Questo perchégawk
vede il file in input come un’unica lunga stringa in cui possono essere presenti dei caratteri di ritorno a capo. È meglio perciò evitare metacaratteri di ancoraggio nel valore diRS
.
L’uso di RS
come espressione regolare e la variabile RT
sono
estensioni gawk
; non sono disponibili in
modalità compatibile
(vedi Opzioni).
In modalità compatibile, solo il primo carattere del valore di
RS
determina la fine del record.
RS = "\0" non è portabile
Ci sono casi in cui capita di dover trattare un intero file-dati come
un record unico. L’unico modo di far questo è quello di dare a Si potrebbe pensare che per i file di testo il carattere NUL, che
consiste di un carattere con tutti i bit uguali a zero, sia un buon valore da
usare per BEGIN { RS = "\0" } # l'intero file diventa un record?
Quasi tutte le altre implementazioni di Capita che recenti versioni di Vedi Funzione readfile per un modo interessante di leggere
file interi. Se si usa |
Successivo: Campi non costanti, Precedente: Record, Su: Leggere file [Contenuti][Indice]
Quando awk
legge un record in input, il record è
automaticamente analizzato o separato da awk
in "pezzi"
chiamati campi. Per default, i campi sono separati da
spazi vuoti, come le parole in una riga stampata.
Uno spazio vuoto in awk
è qualsiasi stringa composta da uno o più
spazi, segni di tabulazione o ritorni a capo;
altri caratteri, come interruzione di pagina, tabulazione verticale, etc., che
sono considerati spazi vuoti in altri linguaggi, non sono considerati
tali da awk
.
Lo scopo dei campi è quello di rendere più conveniente per l’utente far
riferimento a questi frammenti dei record. Non è necessario usarli—si può
operare sull’intero record, se si vuole—ma i campi sono ciò che rende
così potenti dei semplici programmi awk
.
Si usa il simbolo del dollaro (‘$’)
per far riferimento a un campo in un programma awk
,
seguito dal numero del campo desiderato. Quindi, $1
si riferisce al primo campo, $2
al secondo, e così via.
(Diversamente che nelle shell Unix, i numeri di campo non sono limitati a una
sola cifra; $127
è il centoventisettesimo campo nel record.)
Per esempio, supponiamo che la seguente sia una riga in input:
Questo pare essere un esempio proprio carino.
Qui il primo campo, o $1
, è ‘Questo’, il secondo campo, o
$2
, è ‘pare’, e via dicendo. Si noti che l’ultimo campo,
$7
, è ‘carino.’. Poiché non ci sono spazi tra la
‘o’ e il ‘.’, il punto è considerato parte del settimo
campo.
NF
è una variabile predefinita il cui valore è il numero di campi nel
record corrente. awk
aggiorna automaticamente il valore di
NF
ogni volta che legge un record. Indipendentemente da quanti campi
ci possano essere, l’ultimo campo in un record può essere rappresentato da
$NF
. Così, $NF
è lo stesso di $7
, che è ‘carino.’.
Se si cerca di far riferimento a un campo oltre l’ultimo
(come $8
quando il record ha solo sette campi), si ottiene
la stringa nulla. (Se usato in un’operazione numerica si ottiene zero.)
L’uso di $0
, che sarebbe come un riferimento al campo “numero zero”,
è un caso particolare: rappresenta l’intero record in input. Si usa quando
non si è interessati a un campo specifico. Vediamo qualche altro esempio:
$ awk '$1 ~ /li/ { print $0 }' mail-list -| Amelia 555-5553 amelia.zodiacusque@gmail.com F -| Julie 555-6699 julie.perscrutabor@skeeve.com F
Questo esempio stampa ogni record del file mail-list il cui primo campo contiene la stringa ‘li’.
Per converso, il seguente esempio cerca ‘li’ nell’intero record e stampa il primo e l’ultimo campo di ogni record in input per cui è stata trovata una corrispondenza:
$ awk '/li/ { print $1, $NF }' mail-list -| Amelia F -| Broderick R -| Julie F -| Samuel A
Successivo: Cambiare i campi, Precedente: Campi, Su: Leggere file [Contenuti][Indice]
Un numero di campo non è necessario che sia una costante. Nel linguaggio
awk
si può usare qualsiasi espressione dopo ‘$’ per far
riferimento a un campo. Il valore dell’espressione specifica il numero di
campo. Se il valore è una stringa, piuttosto che un numero, viene convertito
in un numero. Consideriamo questo esempio:
awk '{ print $NR }'
Ricordiamo che NR
è il numero dei record letti fino a questo punto: uno
nel primo record, due nel secondo, etc. Così quest’esempio stampa il primo
campo del primo record, il secondo campo del secondo record, e così via.
Per il ventesimo record, è stampato il campo numero 20; molto probabilmente il
record ha meno di 20 campi, perciò stampa una riga vuota.
Questo è un altro esempio sull’uso di espressioni come numeri di campo:
awk '{ print $(2*2) }' mail-list
awk
calcola l’espressione ‘(2*2)’ e usa il suo valore come
numero del campo da stampare. Qui ‘*’ rappresenta la
moltiplicazione, quindi l’espressione ‘2*2’ ha il valore quattro. Le
parentesi vengono usate affinché la moltiplicazione sia eseguita prima
dell’operazione ‘$’; sono necessarie ogni volta che c’è un operatore
binario20
nell’espressione del numero di campo. Questo esempio, dunque, stampa il
tipo di relazione (il quarto campo) per ogni riga del file
mail-list. (Tutti gli operatori di awk
sono elencati, in
ordine decrescente di precedenza, in
Precedenza.)
Se il numero di campo calcolato è zero, si ottiene l’intero record.
Quindi, ‘$(2-2)’ ha lo stesso valore di $0
. Numeri di campo
negativi non sono consentiti; tentare di far riferimento a uno di essi
normalmente fa terminare il programma. (Lo standard POSIX non chiarisce
cosa succede quando si fa riferimento a un numero di campo negativo.
gawk
avvisa di questo e fa terminare il programma. Altre
implementazioni di awk
possono comportarsi in modo diverso.)
Come accennato in Campi,
awk
memorizza il numero di campi del record corrente nella variabile
predefinita NF
(vedi Variabili predefinite). Quindi,
l’espressione $NF
non è una funzionalità speciale—è la diretta
conseguenza della valutazione di NF
e dell’uso di questo valore come
numero di campo.
Successivo: Separatori di campo, Precedente: Campi non costanti, Su: Leggere file [Contenuti][Indice]
Il contenuto di un campo, così come è visto da awk
, può essere
cambiato all’interno di un programma awk
; questo cambia quello che
awk
percepisce come record in input corrente. (Il reale file in
input non viene toccato; awk
non modifica mai il file in
input).
Si consideri il seguente esempio e il suo output:
$ awk '{ numero_pacchi = $3 ; $3 = $3 - 10 > print numero_pacchi, $3 }' inventory-shipped -| 25 15 -| 32 22 -| 24 14 …
Il programma per prima cosa salva il valore originale del campo tre nella
variabile numero_pacchi
.
Il segno ‘-’ rappresenta la sottrazione, così questo programma riassegna
il campo tre, $3
, come il valore originale del campo meno dieci:
‘$3 - 10’. (Vedi Operatori aritmetici.)
Poi stampa il valore originale e quello nuovo del campo tre.
(Qualcuno nel magazzino ha fatto un errore ricorrente nell’inventariare le
scatole rosse.)
Perché questo funzioni, il testo in $3
deve poter essere riconosciuto
come un numero; la stringa di caratteri dev’essere convertita in un numero
affiché il computer possa eseguire operazioni aritmetiche su di essa. Il
numero che risulta dalla sottrazione viene nuovamente convertito in
una stringa di caratteri che quindi diventa il campo tre.
Vedi Conversione.
Quando il valore di un campo è cambiato (come percepito da awk
), il
testo del record in input viene ricalcolato per contenere il nuovo campo al
posto di quello vecchio. In altre parole, $0
cambia per riflettere il
campo modificato. Questo programma
stampa una copia del file in input, con 10 sottratto dal secondo campo di ogni
riga:
$ awk '{ $2 = $2 - 10; print $0 }' inventory-shipped -| Jan 3 25 15 115 -| Feb 5 32 24 226 -| Mar 5 24 34 228 …
È possibile inoltre assegnare contenuti a campi che sono fuori intervallo. Per esempio:
$ awk '{ $6 = ($5 + $4 + $3 + $2) > print $6 }' inventory-shipped -| 168 -| 297 -| 301 …
Abbiamo appena creato $6
, il cui valore è la somma dei campi
$2
, $3
, $4
e $5
. Il segno ‘+’
rappresenta l’addizione. Per il file inventory-shipped, $6
rappresenta il numero totale di pacchi spediti in un determinato mese.
La creazione di un nuovo campo cambia la copia interna di awk
nel
record in input corrente, che è il valore di $0
. Così, se si scrive
‘print $0’ dopo aver aggiunto un campo, il record stampato include il
nuovo campo, col numero di separatori di campo appropriati tra esso e i
campi originariamente presenti.
Questa ridefinizione influenza ed è influenzata da
NF
(il numero dei campi; vedi Campi).
Per esempio, il valore di NF
è impostato al numero del campo più
elevato che è stato creato.
Il formato preciso di $0
è influenzato anche da una funzionalità che
non è ancora stata trattata: il separatore di campo di output,
OFS
, usato per separare i campi (vedi Separatori di output).
Si noti, comunque, che il mero riferimento a un campo fuori
intervallo non cambia il valore di $0
o di NF
.
Far riferimento a un campo fuori intervallo produce solo una stringa nulla.
Per esempio:
if ($(NF+1) != "") print "non è possibile" else print "è tutto normale"
dovrebbe stampare ‘è tutto normale’, perché NF+1
è certamente
fuori intervallo. (Vedi Istruzione if
per maggiori informazioni sulle istruzioni if-else
di awk
.
Vedi Tipi di variabile e confronti
per maggiori informazioni sull’operatore ‘!=’.)
È importante notare che facendo un assegnamento a un campo esistente cambia
il valore di $0
ma non cambia il valore di NF
,
anche qualora si assegni a un campo la stringa nulla. Per esempio:
$ echo a b c d | awk '{ OFS = ":"; $2 = "" > print $0; print NF }' -| a::c:d -| 4
Il campo è ancora lì; ha solo un valore vuoto, delimitato dai due "due punti" tra ‘a’ e ‘c’. Questo esempio mostra cosa succede se si crea un nuovo campo:
$ echo a b c d | awk '{ OFS = ":"; $2 = ""; $6 = "nuovo" > print $0; print NF }' -| a::c:d::nuovo -| 6
Il campo intermedio, $5
, è creato con un valore vuoto
(indicato dalla seconda coppia di due punti adiacenti),
e NF
è aggiornato col valore sei.
Decrementando NF
si eliminano i campi
dopo il nuovo valore di NF
e si ricalcola $0
.
(a.b.)
Vediamo un esempio:
$ echo a b c d e f | awk '{ print "NF =", NF; > NF = 3; print $0 }' -| NF = 6 -| a b c
ATTENZIONE: Alcune versioni di
awk
non ricostruiscono$0
quandoNF
viene diminuito.
Infine, ci sono casi in cui conviene forzare
awk
a ricostruire l’intero record, usando i valori correnti
dei campi e OFS
. Per far ciò, si usa
l’apparentemente innocuo assegnamento:
$1 = $1 # forza la ricostruzione del record print $0 # o qualsiasi altra cosa con $0
Questo forza awk
a ricostruire il record. Aggiungere un commento
rende tutto più chiaro, come abbiamo appena visto.
C’è un rovescio della medaglia nella relazione tra $0
e
i campi. Qualsiasi assegnamento a $0
fa sì che il record sia
rianalizzato (sintatticamente) e ridiviso in campi usando il valore
corrente di FS
. Questo si applica anche a qualsiasi funzione
predefinita che aggiorna $0
, come sub()
e gsub()
(vedi Funzioni per stringhe).
Comprendere
$0
È importante ricordare che È un errore comune tentare di cambiare il separatore di campo in un record
semplicemente impostando Questo non funziona, poiché non è stato fatto niente per cambiare quello stesso record. Invece, si deve forzare la ricostruzione del record, tipicamente con un’istruzione come ‘$1 = $1’, come descritto in precedenza. |
Successivo: Dimensione costante, Precedente: Cambiare i campi, Su: Leggere file [Contenuti][Indice]
• Separatori di campo di default: | Come di solito sono separati i campi. | |
• Separare campi con regexp: | Usare regexp come separatori. | |
• Campi di un solo carattere: | Fare di ogni carattere un campo separato. | |
• Separatori campo da riga di comando: | Assegnare FS dalla riga di
comando.
| |
• Campo intera riga: | Far sì che la riga intera sia un campo solo. | |
• Sommario sulla separazione campi: | Alcuni punti finali e una tavola di sommario. |
Il separatore di campo, che è un carattere singolo o un’espressione
regolare, controlla il modo in cui awk
suddivide un record in input
in campi. awk
fa una scansione del record in input per trovare i
caratteri che individuano il separatore; i campi sono il testo compreso tra i
separatori trovati.
Nell’esempio che segue, usiamo il simbolo del punto elenco (•) per rappresentare gli spazi nell’output. Se il separatore di campo è ‘oo’, la seguente riga:
moo goo gai pan
è suddivisa in tre campi: ‘m’, ‘•g’, e ‘•gai•pan’. Notare gli spazi iniziali nei valori del secondo e del terzo campo.
Il separatore di campo è rappresentato dalla variable predefinita FS
.
I programmatori di shell notino: awk
non usa il
nome IFS
che è usato dalle shell conformi a POSIX (come
la Unix Bourne shell, sh
, o Bash).
Il valore di FS
si può cambiare nel programma awk
con
l’operatore di assegnamento, ‘=’ (vedi Operatori di assegnamento).
Spesso il momento giusto per far ciò è all’inizio dell’esecuzione
prima che sia stato elaborato qualsiasi input, così che il primo record
sia letto col separatore appropriato. Per far questo, si usa il modello di
ricerca speciale
BEGIN
(vedi BEGIN/END).
Per esempio, qui impostiamo il valore di FS
alla stringa
","
:
awk 'BEGIN { FS = "," } ; { print $2 }'
Data la riga in input:
John Q. Smith, 29 Oak St., Walamazoo, MI 42139
questo programma awk
estrae e stampa la stringa
‘•29•Oak•St.’.
A volte i dati in input contengono caratteri separatori che non separano i campi nel modo in cui ci si sarebbe atteso. Per esempio, il nome della persona dell’esempio che abbiamo appena usato potrebbe avere un titolo o un suffisso annesso, come:
John Q. Smith, LXIX, 29 Oak St., Walamazoo, MI 42139
Lo stesso programma estrarrebbe ‘•LXIX’ invece di
‘•29•Oak•St.’.
Se ci si aspetta che il programma stampi l’indirizzo,
si rimarrà sorpresi. La morale è quella di scegliere la struttura dei dati
e i caratteri di separazione attentamente per evitare questi problemi.
(Se i dati non sono in una forma facile da elaborare, può darsi che
si possano manipolare con un programma awk
separato.)
Successivo: Separare campi con regexp, Su: Separatori di campo [Contenuti][Indice]
I campi sono separati normalmente da spazi vuoti
(spazi, tabulazioni e ritorni a capo), non solo da spazi singoli. Due spazi
in una riga non delimitano un campo vuoto. Il valore di default del separatore
di campo FS
è una stringa contenente un singolo spazio, " "
.
Se awk
interpretasse questo valore nel modo usuale, ogni carattere
di spazio separerebbe campi, quindi due spazi in una riga creerebbero un campo
vuoto tra di essi. Il motivo per cui questo non succede è perché un singolo
spazio come valore di FS
è un caso particolare: è preso per specificare
il modo di default di delimitare i campi.
Se FS
è qualsiasi altro carattere singolo, come ","
, ogni
ricorrenza di quel carattere separa due campi. Due ricorrenze consecutive
delimitano un campo vuoto. Se il carattere si trova all’inizio o alla fine
della riga, anche quello delimita un campo vuoto. Il carattere di spazio è
il solo carattere singolo che non segue queste
regole.
Successivo: Campi di un solo carattere, Precedente: Separatori di campo di default, Su: Separatori di campo [Contenuti][Indice]
La precedente sottosezione
ha illustrato l’uso di caratteri singoli o di stringhe semplici come
valore di FS
.
Più in generale, il valore di FS
può essere una stringa contenente
qualsiasi espressione regolare. Se questo è il caso, ogni corrispondenza nel
record con l’espressione regolare separa campi. Per esempio, l’assegnamento:
FS = ", \t"
trasforma ogni parte di una riga in input che consiste di una virgola seguita da uno spazio e una tabulazione in un separatore di campo.
Per un esempio meno banale di espressione regolare, si provi a usare spazi
singoli per separare campi nel modo in cui sono usate le virgole. FS
può essere impostato a "[ ]"
(parentesi quadra sinistra, spazio,
parentesi quadra destra). Quest’espressione regolare corrisponde a uno spazio
singolo e niente più. (vedi Espressioni regolari).
C’è una differenza importante tra i due casi di ‘FS = " "’
(uno spazio singolo) e ‘FS = "[ \t\n]+"’
(un’espressione regolare che individua uno o più spazi, tabulazioni o
ritorni a capo). Per entrambi i valori di FS
, i campi sono
separati da serie (ricorrenze adiacenti multiple) di spazi, tabulazioni
e/o ritorni a capo. Comunque, quando il valore di FS
è
" "
, awk
prima toglie lo spazio vuoto iniziale e finale
dal record e poi stabilisce dove sono i campi.
Per esempio, la seguente pipeline stampa ‘b’:
$ echo ' a b c d ' | awk '{ print $2 }' -| b
Invece la pipeline che segue stampa ‘a’ (notare lo spazio extra intorno a ogni lettera):
$ echo ' a b c d ' | awk 'BEGIN { FS = "[ \t\n]+" } > { print $2 }' -| a
In questo caso, il primo campo è nullo, o vuoto.
Il taglio degli spazi vuoti iniziale e finale ha luogo anche
ogniqualvolta $0
è ricalcolato.
Per esempio, si consideri questa pipeline:
$ echo ' a b c d' | awk '{ print; $2 = $2; print }' -| a b c d -| a b c d
La prima istruzione print
stampa il record così come è stato letto,
con lo spazio vuoto intatto. L’assegnamento a $2
ricostruisce
$0
concatenando insieme $1
fino a $NF
,
separati dal valore di OFS
(che è uno spazio per default).
Poiché lo spazio vuoto iniziale è stato ignorato quando si è trovato
$1
, esso non fa parte del nuovo $0
. Alla fine, l’ultima
istruzione print
stampa il nuovo $0
.
C’è un’ulteriore sottigliezza da considerare quando si usano le espressioni
regolari per separare i campi.
Non è ben specificato nello standard POSIX, né altrove, cosa
significhi ‘^’ nella divisione dei campi. Il ‘^’ cerca
corrispondenze solo all’inizio dell’intero record? Oppure ogni separatore di
campo è una nuova stringa? Di fatto versioni differenti di awk
rispondono a questo quesito in modo diverso, e non si dovrebbe far affidamento
su alcun comportamento specifico nei propri programmi.
(a.b.)
Di sicuro, BWK awk
individua con ‘^’
solo l’inizio del record. Anche gawk
funziona in questo modo. Per esempio:
$ echo 'xxAA xxBxx C' | > gawk -F '(^x+)|( +)' '{ for (i = 1; i <= NF; i++) > printf "-->%s<--\n", $i }' -| --><-- -| -->AA<-- -| -->xxBxx<-- -| -->C<--
Successivo: Separatori campo da riga di comando, Precedente: Separare campi con regexp, Su: Separatori di campo [Contenuti][Indice]
Ci sono casi in cui si abbia la necessità di analizzare ciascun carattere di un
record separatamente. Questo si può fare in gawk
semplicemente
assegnando la stringa nulla (""
) a FS
. (e.c.)
In questo caso,
ogni singolo carattere nel record diventa un campo separato.
Per esempio:
$ echo a b | gawk 'BEGIN { FS = "" } > { > for (i = 1; i <= NF; i = i + 1) > print "Il campo", i, "è", $i > }' -| Il campo 1 è a -| Il campo 2 è -| Il campo 3 è b
Tradizionalmente, il comportamento di FS
quando è impostato a
""
non è stato definito. In questo caso, la maggior parte delle
versioni UNIX di awk
trattano l’intero record come se avesse un
unico campo.
(a.b.)
In modalità di compatibilità
(vedi Opzioni),
se FS
è la stringa nulla, anche gawk
si comporta in questo modo.
Successivo: Campo intera riga, Precedente: Campi di un solo carattere, Su: Separatori di campo [Contenuti][Indice]
FS
dalla riga di comandoFS
può essere impostata sulla riga di comando. Per far questo si usa
l’opzione -F. Per esempio:
awk -F, 'programma' i-file-di-input
imposta FS
al carattere ‘,’. Si noti che l’opzione richiede
un carattere maiuscolo ‘F’ anziché minuscolo ‘f’. Quest’ultima
opzione (-f) serve a specificare il file contenente un programma
awk
.
Il valore usato per l’argomento di -F è elaborato esattamente nello
stesso modo degli assegnamenti alla variabile predefinita FS
. Qualsiasi
carattere speciale nel separatore di campo dev’essere protetto in modo
appropriato. Per esempio, per usare un ‘\’ come separatore di campo
sulla riga di comando, si dovrebbe battere:
# equivale a FS = "\\" awk -F\\\\ '…' file …
Poiché ‘\’ è usato nella shell per proteggere caratteri, a awk
arriva ‘-F\\’. Quindi awk
elabora ‘\\’ per caratteri di
protezione (vedi Sequenze di protezione), producendo alla fine
un unico ‘\’ da usare come separatore di campo.
Come caso particolare, in modalità di compatibilità
(vedi Opzioni),
se l’argomento di -F è ‘t’, FS
è impostato al
carattere di tabulazione. Se si immette ‘-F\t’ nella
shell, senza che sia tra apici, ‘\’ viene cancellata,
così awk
conclude che si vuole realmente che i campi siano separati da tabulazioni e
non da delle ‘t’. Si usi ‘-v FS="t"’ o ‘-F"[t]"’ sulla riga di
comando se si vuole separare i campi con delle ‘t’.
Quando non si è in modalità di compatibilità si deve usare ‘-F '\t'’ per
specificare che le tabulazioni separano i campi.
Come esempio, usiamo un file di programma awk
chiamato
edu.awk che contiene il criterio di ricerca /edu/
e l’azione
‘print $1’:
/edu/ { print $1 }
Impostiamo inoltre FS
al carattere ‘-’ ed eseguiamo il programma
sul file mail-list. Il seguente comando stampa un elenco dei nomi
delle persone che lavorano all’università o che la frequentano, e le prime tre
cifre dei loro numeri di telefono:
$ awk -F- -f edu.awk mail-list -| Fabius 555 -| Samuel 555 -| Jean
Si noti la terza riga di output. La terza riga nel file originale è simile a questa:
Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R
Il ‘-’ che fa parte del nome della persona è stato usato come separatore di campo, al posto del ‘-’ presente nel numero di telefono, che ci si aspettava venisse usato. Questo lascia intuire il motivo per cui si deve stare attenti nella scelta dei separatori di campo e di record.
Forse l’uso più comune di un solo carattere come separatore di campo avviene quando si elabora il file delle password di un sistema Unix. Su molti sistemi Unix, ogni utente è descritto da un elemento nel file delle password del sistema, che contiene una riga singola per ogni utente. In queste righe le informazioni sono separate da dei caratteri ":". Il primo campo è il nome di login dell’utente e il secondo è la password dell’utente criptata o oscurata (una password oscurata è indicata dalla presenza di una sola ‘x’ nel secondo campo). Una riga nel file delle password potrebbe essere simile a questa:
arnold:x:2076:10:Arnold Robbins:/home/arnold:/bin/bash
Il seguente programma esamina il file delle password di sistema e stampa le voci relative agli utenti il cui nome completo non è presente nel file:
awk -F: '$5 == ""' /etc/passwd
Successivo: Sommario sulla separazione campi, Precedente: Separatori campo da riga di comando, Su: Separatori di campo [Contenuti][Indice]
Occasionalmente, è utile trattare l’intera riga in input come un solo campo.
Questo si può fare facilmente e in modo portabile semplicemente impostando
FS
a "\n"
(un ritorno a capo).21
awk -F'\n' 'programma' file …
In questo caso, $1
coincide con $0
.
Cambiare
FS non incide sui campi
Secondo lo standard POSIX, si suppone che Comunque, molte delle più vecchie implementazioni di sed 1q /etc/passwd | awk '{ FS = ":" ; print $1 }' normalmente stampa: -| root su un’implementazione non standard di root:x:0:0:Root:/: (Il comando |
Precedente: Campo intera riga, Su: Separatori di campo [Contenuti][Indice]
È importante ricordare che quando si assegna una costante stringa
come valore di FS
, questa subisce una normale elaborazione di stringa
da parte di awk
. Per esempio, con Unix awk
e
gawk
, l’assegnamento ‘FS = "\.."’ assegna la stringa di
caratteri ".."
a FS
(la barra inversa è tolta). Questo crea un’espressione regolare
che significa “i campi sono separati da ricorrenze di due caratteri
qualsiasi”. Se invece si vuole che i campi siano separati da un punto
seguito da un qualsiasi carattere singolo, si deve usare ‘FS = "\\.."’.
Il seguente elenco riassume come i campi vengono divisi, in base al valore
di FS
(‘==’ significa “è uguale a”):
FS == " "
I campi sono separati da serie di spazi vuoti. Gli spazi vuoti iniziale e finale sono ignorati. Questo è il comportamento di default.
FS == qualsiasi altro carattere singolo
I campi sono separati da ogni ricorrenza del carattere. Ricorrenze successive multiple delimitano campi vuoti, e lo stesso fanno le ricorrenze iniziali e finali del carattere. Il carattere può essere anche un metacarattere di espressione regolare, che non è necessario proteggere.
FS == espressione regolare
I campi sono separati da ricorrenze di caratteri che corrispondono alla espressione regolare. Corrispondenze iniziali e finali della regexp delimitano campi vuoti.
FS == ""
Ogni sinngolo carattere nel record diventa un campo separato. (Questa è un’estensione comune; non è specificata dallo standard POSIX.)
FS e IGNORECASE
La variabile FS = "c" IGNORECASE = 1 $0 = "aCa" print $1 L’output è ‘aCa’. Se si vuol veramente dividere i campi su un carattere
alfabetico ignorandone il maiuscolo/minuscolo, si deve usare un’espressione
regolare che lo farà in automatico (p.es., ‘FS = "[c]"’). In questo
caso, |
Successivo: Separazione in base al contenuto, Precedente: Separatori di campo, Su: Leggere file [Contenuti][Indice]
Questa
sezione tratta una funzionalità avanzata
di gawk
. Se si è un utente alle prime armi di awk
,
la si può saltare in prima lettura.
gawk
fornisce una funzionalità per il trattamento di campi
a larghezza fissa senza un separatore di campo distintivo. Per esempio,
dati di questo tipo si trovano nell’input per vecchi programmi Fortran dove
dei numeri sono elencati uno dopo l’altro, o nell’output di programmi che
non prevedono che il loro output sia dato in input ad altri programmi.
Un esempio di quest’ultimo caso è una tabella dove tutte le colonne sono
allineate usando un numero variabile di spazi e dove i campi vuoti
sono solo spazi. Chiaramente, la normale divisione in campi di
awk
basata su FS
non funziona bene in questa situazione.
Sebbene un programma awk
portabile possa usare una serie di chiamate substr()
su $0
(vedi Funzioni per stringhe),
questo è scomodo e inefficiente se il numero dei campi è elevato.
La suddivisione di un record in input in campi a larghezza fissa viene
specificata assegnando una stringa contenente numeri separati da spazi alla
variabile predefinita FIELDWIDTHS
. Ogni numero specifica la larghezza
del campo, comprese le colonne tra i campi. Se si vogliono ignorare le
colonne tra i campi si può specificare la loro larghezza come un campo
separato che verrà poi ignorato.
È un errore fatale definire una larghezza di campo che abbia un valore
negativo. I dati seguenti costituiscono l’output del programma di utilità
Unix w
. È utile per spiegare l’uso di FIELDWIDTHS
:
10:06pm up 21 days, 14:04, 23 users User tty login idle JCPU PCPU what hzuo ttyV0 8:58pm 9 5 vi p24.tex hzang ttyV3 6:37pm 50 -csh eklye ttyV5 9:53pm 7 1 em thes.tex dportein ttyV6 8:17pm 1:47 -csh gierd ttyD3 10:00pm 1 elm dave ttyD4 9:47pm 4 4 w brent ttyp0 26Jun91 4:46 26:46 4:41 bash dave ttyq4 26Jun9115days 46 46 wnewmail
Il seguente programma prende l’input sopra mostrato, converte il tempo di inattività in numero di secondi, e stampa i primi due campi e il tempo di inattività calcolato:
BEGIN { FIELDWIDTHS = "9 6 10 6 7 7 35" } NR > 2 { inat = $4 sub(/^ +/, "", inat) # togli spazi prima del valore if (inat == "") inat = 0 if (inat ~ /:/) { split(inat, t, ":") inat = t[1] * 60 + t[2] } if (inat ~ /days/) inat *= 24 * 60 * 60 print $1, $2, inat }
NOTA: Questo programma usa diverse funzionalità di
awk
non ancora trattate.
L’esecuzione del programma sui dati produce il seguente risultato:
hzuo ttyV0 0 hzang ttyV3 50 eklye ttyV5 0 dportein ttyV6 107 gierd ttyD3 1 dave ttyD4 0 brent ttyp0 286 dave ttyq4 1296000
Un altro esempio (forse più pratico) di dati di input con larghezza costante è l’input da un mazzo di schede elettorali. In alcune parti degli Stati Uniti, i votanti marcano le loro scelte perforando delle schede elettroniche.
Queste schede vengono poi elaborate per contare i voti espressi per ogni
singolo candidato o su ogni determinato quesito. Siccome un votante può
scegliere di non votare su alcune questioni, qualsiasi colonna della scheda
può essere vuota. Un programma awk
per elaborare tali dati potrebbe
usare la funzionalità FIELDWIDTHS
per semplificare la lettura dei dati.
(Naturalmente, riuscire a eseguire gawk
su un sistema con lettori di
schede è un’altra storia!)
L’assegnazione di un valore a FS
fa sì che gawk
usi FS
per separare nuovamente i campi. Si può usare ‘FS = FS’ per ottenere
questo effetto, senza dover conoscere il valore corrente di FS
.
Per vedere quale tipo di separazione sia in atto,
si può usare PROCINFO["FS"]
(vedi Variabili auto-assegnate).
Il suo valore è "FS"
se si usa la normale separazione in campi,
o "FIELDWIDTHS"
se si usa la separazione in campi a larghezza fissa:
if (PROCINFO["FS"] == "FS") separazione in campi normale… else if (PROCINFO["FS"] == "FIELDWIDTHS") separazione in campi a larghezza fissa… else separazione dei campi in base al contenuto… (si veda la sezione successiva)
Quest’informazione è utile quando si scrive una funzione che
necessita di cambiare temporaneamente FS
o FIELDWIDTHS
,
leggere alcuni record, e poi ripristinare le impostazioni originali
(vedi Funzioni Passwd,
per un esempio di tale funzione).
Successivo: Righe multiple, Precedente: Dimensione costante, Su: Leggere file [Contenuti][Indice]
Questa
sezione tratta una funzionalità avanzata
di gawk
. Se si è un utente alle prime armi di awk
,
la si può saltare in prima lettura.
Normalmente, quando si usa FS
, gawk
definisce i campi come
le parti del record che si trovano tra due separatori di campo. In altre
parole, FS
definisce cosa un campo non è, invece di cosa
è.
Tuttavia, ci sono casi in cui effettivamente si ha bisogno di definire i campi
in base a cosa essi sono, e non in base a cosa non sono.
Il caso più emblematico è quello dei dati cosiddetti comma-separated value (CSV). Molti fogli elettronici, per esempio, possono esportare i dati in file di testo, dove ogni record termina con un ritorno a capo e i campi sono separati tra loro da virgole. Se le virgole facessero solo da separatore fra i dati non ci sarebbero problemi. Il problema sorge se uno dei campi contiene una virgola al suo interno. In queste situazioni, la maggioranza dei programmi include il campo fra doppi apici.23 Così, potremmo avere dei dati di questo tipo:
Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,12345-6789,USA
La variabile FPAT
offre una soluzione per casi come questo.
Il valore di FPAT
dovrebbe essere una stringa formata da un’espressione
regolare. L’espressione regolare descrive il contenuto di ciascun campo.
Nel caso dei dati CSV visti prima, ogni campo è “qualsiasi cosa che non
sia una virgola,” oppure “doppi apici, seguiti da qualsiasi cosa che non
siano doppi apici, e doppi apici di chiusura”. Se fosse scritta come una
costante regexp
(vedi Espressioni regolari),
sarebbe /([^,]+)|("[^"]+")/
.
Dovendola scrivere come stringa si devono proteggere i doppi apici,
e quindi si deve scrivere:
FPAT = "([^,]+)|(\"[^\"]+\")"
Come esempio pratico, si può vedere questo semplice programma che analizza e divide i dati:
BEGIN { FPAT = "([^,]+)|(\"[^\"]+\")" } { print "NF = ", NF for (i = 1; i <= NF; i++) { printf("$%d = <%s>\n", i, $i) } }
Eseguendolo, avendo in input la riga vista sopra, si ottiene:
$ gawk -f simple-csv.awk addresses.csv NF = 7 $1 = <Robbins> $2 = <Arnold> $3 = <"1234 A Pretty Street, NE"> $4 = <MyTown> $5 = <MyState> $6 = <12345-6789> $7 = <USA>
Si noti la virgola contenuta nel valore del campo $3
.
Un semplice miglioramento se si elaborano dati CSV di questo tipo potrebbe essere quello di rimuovere i doppi apici, se presenti, con del codice di questo tipo:
if (substr($i, 1, 1) == "\"") { len = length($i) $i = substr($i, 2, len - 2) # Ottiene il testo tra doppi apici }
Come per FS
, la variabile IGNORECASE
(vedi Variabili modificabili dall'utente) ha effetto sulla separazione dei
campi con FPAT
.
Se si assegna un valore a FPAT
la divisione in campi non viene
effettuata utilizzando FS
o FIELDWIDTHS
.
Analogamente a FIELDWIDTHS
, il valore di PROCINFO["FS"]
sarà "FPAT"
se è in uso la suddivisione in campi in base al contenuto.
NOTA: Alcuni programmi esportano dei dati CSV che contengono dei ritorni a capo al loro interno in campi rinchiusi tra doppi apici.
gawk
non è in grado di trattare questi dati. Malgrado esista una specifica ufficiale per i dati CSV, non c’è molto da fare; il meccanismo diFPAT
fornisce una soluzione elegante per la maggioranza dei casi, e per gli sviluppatori digawk
ciò può bastare.
Come visto, l’espressione regolare usata per FPAT
richiede
che ogni campo contenga almeno un carattere. Una semplice modifica
(cambiare il primo ‘+’ con ‘*’) permette che siano presenti dei
campi vuoti:
FPAT = "([^,]*)|(\"[^\"]+\")"
Infine, la funzione patsplit()
rende la stessa funzionalità disponibile
per suddividere normali stringhe (vedi Funzioni per stringhe).
Per ricapitolare, gawk
fornisce tre metodi indipendenti per
suddividere in campi i record in input.
Il meccanismo usato è determinato da quella tra le tre
variabili—FS
, FIELDWIDTHS
, o FPAT
—a cui
sia stato assegnato un valore più recentemente.
Successivo: Getline, Precedente: Separazione in base al contenuto, Su: Leggere file [Contenuti][Indice]
In alcune banche-dati, una sola riga non può contenere in modo adeguato tutte le informazioni di una voce. In questi casi si possono usare record multiriga. Il primo passo è quello di scegliere il formato dei dati.
Una tecnica è quella di usare un carattere o una stringa non usuali per
separare i record. Per esempio, si può usare il carattere di interruzione di
pagina (scritto ‘\f’ sia in awk
che in C) per separarli,
rendendo ogni record una pagina del file. Per far ciò, basta impostare la
variabile RS
a "\f"
(una stringa contenente il carattere di
interruzione di pagina). Si potrebbe ugualmente usare qualsiasi altro
carattere, sempre che non faccia parte dei dati di un record.
Un’altra tecnica è quella di usare righe vuote per separare i record.
Per una particolare
convenzione, una stringa nulla come valore di RS
indica che i record
sono separati da una o più righe vuote. Quando RS
è impostato alla
stringa nulla, ogni record termina sempre alla prima riga vuota che è stata
trovata. Il record successivo non inizia prima della successiva riga non
vuota. Indipendentemente dal numero di righe vuote presenti in successione,
esse costituiscono sempre un unico separatore di record.
(Le righe vuote devono essere completamente vuote; righe che contengono
spazi bianchi non sono righe vuote.)
Si può ottenere lo stesso effetto di ‘RS = ""’ assegnando la stringa
"\n\n+"
a RS
. Quest’espressione regolare individua
il ritorno a capo alla fine del record e una o più righe vuote dopo il
record. In aggiunta, un’espressione regolare individua sempre la sequenza più
lunga possibile quando una tale stringa sia presente.
(vedi Più lungo da sinistra).
Quindi, il record successivo non inizia prima della successiva riga non
vuota; indipendentemente dal numero di righe vuote presenti in una voce di
banca-dati, esse sono considerate come un unico separatore di record.
Comunque, c’è una sostanziale differenza tra ‘RS = ""’ e ‘RS = "\n\n+"’. Nel primo caso, i ritorni a capo iniziali nel file-dati di input vengono ignorati, e se un file termina senza righe vuote aggiuntive dopo l’ultimo record, il ritorno a capo viene rimosso dal record. Nel secondo caso, questa particolare elaborazione non viene fatta. (a.b.)
Ora che l’input è separato in record, il secondo passo è quello di separare i
campi all’interno dei record. Un modo per farlo è quello di dividere in
campi ognuna delle righe in input
nel modo solito. Questo viene fatto per default tramite una
speciale funzionalità. Quando RS
è impostato alla stringa nulla
e FS
è impostato a un solo carattere, il carattere di
ritorno a capo agisce sempre come separatore di campo.
Questo in aggiunta a tutte le separazioni di campo che risultano da
FS
.24
La motivazione originale per questa particolare eccezione probabilmente era
quella di prevedere un comportamento che fosse utile nel caso di default
(cioè, FS
uguale a " "
). Questa funzionalità può
costituire un problema se non si vuole che il carattere di ritorno a capo
faccia da separatore tra i campi, perché non c’è alcun modo per impedirlo.
Tuttavia, si può aggirare il problema usando la funzione split()
per spezzare i record manualmente.
(vedi Funzioni per stringhe).
Se si ha un separatore di campo costituito da un solo carattere, si può
aggirare la funzionalità speciale in modo diverso, trasformando FS
in un’espressione regolare contenente
quel carattere singolo. Per esempio, se il separatore di campo è
un carattere di percentuale, al posto di
‘FS = "%"’, si può usare ‘FS = "[%]"’.
Un altro modo per separare i campi è quello di
mettere ciascun campo su una riga separata: per far questo basta impostare la
variabile FS
alla stringa "\n"
.
(Questo separatore di un solo carattere individua un singolo ritorno a capo.)
Un esempio pratico di un file-dati organizzato in questo modo potrebbe essere
un elenco di indirizzi, in cui delle righe vuote fungono da separatore fra
record. Si consideri un elenco di indirizzi in un file chiamato
indirizzi, simile a questo:
Jane Doe 123 Main Street Anywhere, SE 12345-6789 John Smith 456 Tree-lined Avenue Smallville, MW 98765-4321 …
Un semplice programma per elaborare questo file è il seguente:
# addrs.awk --- semplice programma per una lista di indirizzi postali # I record sono separati da righe bianche # Ogni riga è un campo. BEGIN { RS = "" ; FS = "\n" } { print "Il nome è:", $1 print "L'indirizzo è:", $2 print "Città e Stato sono:", $3 print "" }
L’esecuzione del programma produce questo output:
$ awk -f addrs.awk addresses -| Il nome è: Jane Doe -| L'indirizzo è: 123 Main Street -| Città e Stato sono: Anywhere, SE 12345-6789 -| -| Il nome è: John Smith -| L'indirizzo è: 456 Tree-lined Avenue -| Città e Stato sono: Smallville, MW 98765-4321 -| …
Vedi Programma labels, per un programma più realistico per gestire
elenchi di indirizzi. Il seguente elenco riassume come sono divisi i record,
a seconda del valore assunto da
RS
:
RS == "\n"
I record sono separati dal carattere di ritorno a capo (‘\n’). In effetti, ogni riga nel file-dati è un record separato, comprese le righe vuote. Questo è il comportamento di default.
RS == qualsiasi carattere singolo
I record sono separati da ogni ricorrenza del carattere specificato. Più ricorrenze adiacenti delimitano record vuoti.
RS == ""
I record sono separati da una o più righe vuote.
Quando FS
è un carattere singolo,
il carattere di ritorno a capo
serve sempre come separatore di campo, in aggiunta a qualunque valore possa
avere FS
. I ritorni a capo all’inizio e alla fine del file sono
ignorati.
RS == regexp
I record sono separati da ricorrenze di caratteri corrispondenti a
regexp. Le corrispondenze iniziali e finali di
regexp designano record vuoti.
(Questa è un’estensione di gawk
; non è specificata dallo
standard POSIX.)
Se non è eseguito in modalità di compatibilità (vedi Opzioni),
gawk
imposta RT
al testo di input corrispondente
al valore specificato da RS
.
Ma se al termine del file in input non è stato trovato un testo che
corrisponde a RS
, gawk
imposta RT
alla stringa nulla.
Successivo: Timeout in lettura, Precedente: Righe multiple, Su: Leggere file [Contenuti][Indice]
getline
Finora abbiamo ottenuto i dati di input dal flusso di input principale di
awk
: lo standard input (normalmente la tastiera, a volte
l’output di un altro programma) o i
file indicati sulla riga di comando. Il linguaggio awk
ha uno
speciale comando predefinito chiamato getline
che
può essere usato per leggere l’input sotto il diretto controllo dell’utente.
Il comando getline
è usato in molti modi diversi e
non dovrebbe essere usato dai principianti.
L’esempio che segue alla spiegazione del comando getline
comprende del materiale che ancora non è stato trattato. Quindi, è meglio
tornare indietro e studiare il comando getline
dopo aver rivisto
il resto
di questo Documento
e avere acquisito una buona conoscenza di come funziona awk
.
Il comando getline
restituisce 1 se trova un record e 0 se
trova la fine del file. Se si verifica qualche errore cercando di leggere
un record, come un file che non può essere aperto, getline
restituisce -1. In questo caso, gawk
imposta la variabile
ERRNO
a una stringa che descrive l’errore in questione.
Se il messaggio di errore ERRNO
indica che l’operazione di I/O può
essere ritentata e la variabile PROCINFO["input", "RETRY"]
è
impostata a 1, getline
restituisce un codice di ritorno -2
invece che -1, e si può provare a chiamare ulterioriormente
getline
. Vedi Proseguire dopo errore in input per ulteriori
informazioni riguardo a questa funzionalità.
Negli esempi seguenti, comando sta per un valore di stringa che rappresenta un comando della shell.
NOTA: Quando è stata specificata l’opzione --sandbox (vedi Opzioni), la lettura di input da file, pipe e coprocessi non è possibile.
• Getline semplice: | Usare getline senza argomenti.
| |
• Getline variabile: | Usare getline in una variabile.
| |
• Getline file: | Usare getline da un file.
| |
• Getline variabile file: | Usare getline in una variabile da un
file.
| |
• Getline pipe: | Usare getline da una pipe.
| |
• Getline variabile pipe: | Usare getline in una variabile da una
pipe.
| |
• Getline coprocesso: | Usare getline da un coprocesso.
| |
• Getline variabile coprocesso: | Usare getline in una variabile da un
coprocesso.
| |
• Note su getline: | Cose importanti da sapere su getline .
| |
• Sommario di getline: | Sommario delle varianti di getline .
|
Successivo: Getline variabile, Su: Getline [Contenuti][Indice]
getline
senza argomentiIl comando getline
può essere usato senza argomenti per leggere l’input
dal file in input corrente. Tutto quel che fa in questo caso è leggere il
record in input successivo e dividerlo in campi. Questo è utile se è
finita l’elaborarezione del record corrente, e si vogliono fare delle
elaborazioni particolari sul record successivo proprio adesso.
Per esempio:
# rimuovere il testo tra /* e */, compresi { if ((i = index($0, "/*")) != 0) { prima = substr($0, 1, i - 1) # la parte iniziale della stringa dopo = substr($0, i + 2) # ... */ ... j = index(dopo, "*/") # */ è nella parte finale? if (j > 0) { dopo = substr(dopo, j + 2) # rimozione del commento } else { while (j == 0) { # passa ai record seguenti if (getline <= 0) { print("Fine file inattesa o errore:", ERRNO) > "/dev/stderr" exit } # incrementare la riga usando la concatenazione di stringhe dopo = dopo $0 j = index(dopo, "*/") # è */ nella parte finale? if (j != 0) { dopo = substr(dopo, j + 2) break } } } # incrementare la riga di output usando la concatenazione # di stringhe $0 = prima dopo } print $0 }
Questo programma awk
cancella i commenti in stile C
(‘/* … */’) dall’input.
Usa diverse funzionalità che non sono ancora state trattate, incluse la
concatenazione di stringhe
(vedi Concatenazione)
e le funzioni predefinite index()
e substr()
(vedi Funzioni per stringhe).
Sostituendo ‘print $0’ con altre
istruzioni, si possono effettuare elaborazioni più complesse sull’input
decommentato, come ricercare corrispondenze di un’espressione regolare.
(Questo programma ha un piccolo problema: non funziona se c’è più di un
commento che inizia e finisce
sulla stessa riga.)
Questa forma del comando getline
imposta NF
,
NR
, FNR
, RT
e il valore di $0
.
NOTA: Il nuovo valore di
$0
è usato per verificare le espressioni di ricerca di ogni regola successiva. Il valore originale di$0
che ha attivato la regola che ha eseguito lagetline
viene perso. A differenza digetline
, l’istruzionenext
legge un nuovo record ma inizia a elaborarlo normalmente, a partire dalla prima regola presente nel programma. Vedi Istruzione next.
Successivo: Getline file, Precedente: Getline semplice, Su: Getline [Contenuti][Indice]
getline
in una variabileSi può usare ‘getline var’ per leggere il record successivo
in input ad awk
nella variabile var. Non vien fatta
nessun’altra elaborazione.
Per esempio, supponiamo che la riga successiva sia un commento o una stringa
particolare, e la si voglia leggere senza innescare nessuna regola. Questa
forma di getline
permette di leggere quella riga e memorizzarla in una
variabile in modo che il ciclo principale di awk
che "legge una riga
e controlla ogni regola" non la veda affatto.
L’esempio seguente inverte tra loro a due a due le righe in input:
{ if ((getline tmp) > 0) { print tmp print $0 } else print $0 }
Prende la seguente lista:
wan tew free phore
e produce questo risultato:
tew wan phore free
Il comando getline
usato in questo modo imposta solo le variabili
NR
, FNR
e RT
(e, naturalmente, var).
Il record non viene
suddiviso in campi, e quindi i valori dei campi (compreso $0
) e
il valore di NF
non cambiano.
Successivo: Getline variabile file, Precedente: Getline variabile, Su: Getline [Contenuti][Indice]
getline
da un fileSi usa ‘getline < file’ per leggere il record successivo da file. Qui, file è un’espressione di tipo stringa che specifica il nome-file. ‘< file’ è una cosidetta ridirezione perché richiede che l’input provenga da un posto differente. Per esempio, il seguente programma legge il suo record in input dal file secondary.input quando trova un primo campo con un valore uguale a 10 nel file in input corrente:
{ if ($1 == 10) { getline < "secondary.input" print } else print }
Poiché non viene usato il flusso principale di input, i valori di NR
e
FNR
restano immutati. Comunque, il record in input viene diviso in
modo normale, per cui vengono cambiati i valori di $0
e degli altri
campi, producendo un nuovo valore di NF
.
Viene impostato anche RT
.
Per lo standard POSIX, ‘getline < espressione’ è ambiguo se
espressione contiene operatori che non sono all’interno di parentesi,
ad esclusione di ‘$’; per esempio, ‘getline < dir "/" file’ è
ambiguo perché l’operatore di concatenazione (non ancora trattato;
vedi Concatenazione) non è posto tra parentesi.
Si dovrebbe scrivere invece ‘getline < (dir "/" file)’, se il
programma dev’essere portabile su tutte le implementazioni di awk
.
Successivo: Getline pipe, Precedente: Getline file, Su: Getline [Contenuti][Indice]
getline
in una variabile da un fileSi usa ‘getline var < file’ per leggere l’input dal file file, e metterlo nella variabile var. Come prima, file è un’espressione di tipo stringa che specifica il file dal quale legggere.
In questa versione di getline
, nessuna delle variabili predefinite è
cambiata e il record non è diviso in campi. La sola variabile cambiata è
var.25
Per esempio, il seguente programma copia tutti i file in input nell’output, ad
eccezione dei record che dicono ‘@include nomefile’.
Tale record è sostituito dal contenuto del file
nomefile:
{ if (NF == 2 && $1 == "@include") { while ((getline line < $2) > 0) print line close($2) } else print }
Si noti come il nome del file in input aggiuntivo non compaia all’interno del
programma; è preso direttamente dai dati, e precisamente dal secondo campo
della riga di @include
.
La funzione close()
viene chiamata per assicurarsi che se nell’input
appaiono due righe @include
identiche, l’intero file specificato sia
incluso ogni volta.
Vedi Chiusura file e pipe.
Una carenza di questo programma è che non gestisce istruzioni
@include
nidificate
(cioè, istruzioni @include
contenute nei file inclusi)
nel modo in cui ci si aspetta che funzioni un vero preelaboratore di macro.
Vedi Programma igawk per un programma
che gestisce le istruzioni @include
nidificate.
Successivo: Getline variabile pipe, Precedente: Getline variabile file, Su: Getline [Contenuti][Indice]
getline
da una pipeL’onniscienza ha molti aspetti positivi. Se non si può ottenerla, l’attenzione ai dettagli può aiutare.
L’output di un comando può anche essere convogliato in getline
, usando
‘comando | getline’. In
questo caso, la stringa comando viene eseguita come comando di shell e
il suo output è passato ad awk
per essere usato come input.
Questa forma di getline
legge un record alla volta dalla pipe.
Per esempio, il seguente programma copia il suo input nel suo output,
ad eccezione delle righe che iniziano con ‘@execute’, che sono
sostituite dall’output prodotto dall’esecuzione del resto della riga
costituito da un comando di shell.
{ if ($1 == "@execute") { tmp = substr($0, 10) # Rimuove "@execute" while ((tmp | getline) > 0) print close(tmp) } else print }
La funzione close()
viene chiamata per assicurarsi che, se appaiono
nell’input due righe ‘@execute’ identiche, il comando sia eseguito per
ciascuna di esse.
Vedi Chiusura file e pipe.
Dato l’input:
pippo pluto paperino @execute who gastone
il programma potrebbe produrre:
pippo pluto paperino arnold ttyv0 Jul 13 14:22 miriam ttyp0 Jul 13 14:23 (murphy:0) bill ttyp1 Jul 13 14:23 (murphy:0) gastone
Si osservi che questo programma ha eseguito who
e stampato il
risultato. (Eseguendo questo programma, è chiaro che ciascun utente otterrà
risultati diversi, a seconda di chi è collegato al sistema.)
Questa variante di getline
divide il record in campi, imposta il valore
di NF
, e ricalcola il valore di $0
. I valori di
NR
e FNR
non vengono cambiati.
Viene impostato RT
.
Per lo standard POSIX, ‘espressione | getline’ è ambiguo se
espressione contiene operatori che non sono all’interno di parentesi,
ad esclusione di ‘$’. Per esempio,
‘"echo " "date" | getline’ è ambiguo perché
l’operatore di concatenazione non è tra parentesi. Si dovrebbe scrivere
invece ‘("echo " "date") | getline’, se il programma dev’essere
portabile su tutte le implementazioni di awk
.
NOTA: Sfortunatamente,
gawk
non ha un comportamento uniforme nel trattare un costrutto come ‘"echo " "date" | getline’. La maggior parte delle versioni, compresa la versione corrente, lo tratta come ‘("echo " "date") | getline’. (Questo è anche il comportamento di BWKawk
.) Alcune versioni invece lo trattano come ‘"echo " ("date" | getline)’. (Questo è il comportamento dimawk
.) In breve, per evitare problemi, è sempre meglio usare parentesi esplicite.
Successivo: Getline coprocesso, Precedente: Getline pipe, Su: Getline [Contenuti][Indice]
getline
in una variabile da una pipeQuando si usa ‘comando | getline var’,
l’output di comando è inviato tramite una pipe a
getline
ad una variabile var. Per esempio, il
seguente programma legge la data e l’ora corrente nella variabile
current_time
, usando il programma di utilità date
, e poi lo
stampa:
BEGIN { "date" | getline current_time close("date") print "Report printed on " current_time }
In questa versione di getline
, nessuna delle variabili predefinite è
cambiata e il record non è diviso in campi. In ogni caso, RT
viene
impostato.
Successivo: Getline variabile coprocesso, Precedente: Getline variabile pipe, Su: Getline [Contenuti][Indice]
getline
da un coprocessoLeggere dell’input in getline
da una pipe è un’operazione
unidirezionale.
Il comando avviato con ‘comando | getline’ invia dati
al programma awk
.
Occasionalmente, si potrebbe avere la necessità di inviare dei dati a un altro
programma che li elabori, per poi leggere il risultato che esso genera.
gawk
permette di avviare un coprocesso, col quale sono
possibili comunicazioni bidirezionali. Questo vien fatto con l’operatore
‘|&’.
Tipicamente, dapprima si inviano dati al coprocesso e poi si leggono
i risultati da esso prodotto, come mostrato di seguito:
print "some query" |& "db_server" "db_server" |& getline
esso invia una richiesta a db_server
e poi legge i risultati.
I valori di NR
e
FNR
non vengono cambiati,
perché non è cambiato il flusso principale.
In ogni caso, il record è diviso in campi
nel solito modo, cambiando così i valori di $0
, degli altri campi,
e di NF
e RT
.
I coprocessi sono una funzionalità avanzata. Vengono trattati qui solo perché
questa è la
sezione su getline
.
Vedi I/O bidirezionale,
dove i coprocessi vengono trattati più dettagliatamente.
Successivo: Note su getline, Precedente: Getline coprocesso, Su: Getline [Contenuti][Indice]
getline
in una variabile da un coprocessoQuando si usa ‘comando |& getline var’, l’output dal
coprocesso comando viene inviato tramite una pipe bidirezionale a
getline
e nella variabile var.
In questa versione di getline
, nessuna delle variabili predefinite
viene cambiata e il record non viene diviso in campi. La sola variabile che
cambia è var.
In ogni caso, RT
viene impostato.
Successivo: Sommario di getline, Precedente: Getline variabile coprocesso, Su: Getline [Contenuti][Indice]
getline
Qui sono elencate diverse considerazioni su getline
da tener presenti:
getline
cambia il valore di $0
e NF
,
awk
non salta automaticamente all’inizio del
programma per iniziare a provare il nuovo record su ogni criterio di ricerca.
Comunque, il nuovo record viene provato su ogni regola successiva.
awk
limitano a una sola il
numero di pipeline che un programma awk
può tenere aperte.
In gawk
, non c’è questo limite.
Si possono aprire tante pipeline (e coprocessi) quante ne permette il
sistema operativo in uso.
getline
, senza
una ridirezione, all’interno di una regola BEGIN
. Poiché una
getline
non ridiretta legge dai file-dati specificati nella riga di
comando, il primo comando getline
fa sì che awk
imposti
il valore di FILENAME
. Normalmente, FILENAME
non ha ancora un
valore all’interno delle regole BEGIN
, perché non si è ancora
iniziato a elaborare il
file-dati della riga di comando.
(a.b.)
(Si veda BEGIN/END;
e vedi Variabili auto-assegnate.)
FILENAME
con getline
(‘getline < FILENAME’)
può essere fonte di
confusione. awk
apre un flusso separato di input, diverso dal
file in input corrente. Comunque, poiché non si usa una variabile,
$0
e NF
vengono aggiornati. Se si sta facendo questo, è
probabilmente per sbaglio, e si dovrebbe rivedere quello che si sta cercando
di fare.
getline
e le variabili da esse modificate.
È degno di nota che le varianti che non usano la ridirezione
possono far sì che FILENAME
venga aggiornato se chiedono ad
awk
di iniziare a leggere un nuovo file in input.
awk
si comportano in modo diverso quando trovano la
fine-del-file [EOF]. Alcune versioni non valutano l’espressione; molte
versioni (compreso gawk
) lo fanno. Si veda un esempio, gentilmente
fornito da Duncan Moore:
BEGIN { system("echo 1 > f") while ((getline a[++c] < "f") > 0) { } print c }
Qui l’effetto secondario è ‘++c’. Se viene trovata la fine del file
prima di assegnare l’elemento a
, c
è incrementato o no?
gawk
tratta getline
come una chiamata di funzione, e valuta
l’espressione ‘a[++c]’ prima di cercare di leggere da f.
Comunque, alcune versioni di awk
valutano l’espressione solo
se c’è un valore di stringa da assegnare.
Precedente: Note su getline, Su: Getline [Contenuti][Indice]
getline
La Tabella 4.1
riassume le otto varianti di getline
,
elencando le variabili predefinite che sono impostate da ciascuna di esse,
e se la variante è standard o è un’estensione di gawk
.
Nota: per ogni variante, gawk
imposta la variabile predefinita
RT
.
Variante | Effetto | awk / gawk |
---|---|---|
getline | Imposta $0 , NF , FNR , NR , e RT | awk |
getline var | Imposta var, FNR , NR , e RT | awk |
getline < file | Imposta $0 , NF , e RT | awk |
getline var < file | Imposta var e RT | awk |
comando | getline | Imposta $0 , NF , e RT | awk |
comando | getline var | Imposta var e RT | awk |
comando |& getline | Imposta $0 , NF , e RT | gawk |
comando |& getline var | Imposta var e RT | gawk |
Tabella 4.1: Varianti di getline
e variabili impostate da ognuna
Successivo: Proseguire dopo errore in input, Precedente: Getline, Su: Leggere file [Contenuti][Indice]
Questa
sezione descrive una funzionalità disponibile solo in
gawk
.
Si può specificare un tempo limite in millisecondi per leggere l’input dalla
tastiera, da una pipe o da una comunicazione bidirezionale, compresi i
socket TCP/IP. Questo può essere fatto per input, per comando o per
connessione, impostando un elemento speciale nel vettore PROCINFO
(vedi Variabili auto-assegnate):
PROCINFO["nome_input", "READ_TIMEOUT"] = tempo limite in millisecondi
Se è impostato, gawk
smette di attendere una risposta e restituisce
insuccesso se non sono disponibili dati da leggere entro il limite di tempo
specificato. Per esempio, un cliente TCP può decidere di abbandonare se
non riceve alcuna risposta dal server dopo un certo periodo di tempo:
Service = "/inet/tcp/0/localhost/daytime" PROCINFO[Service, "READ_TIMEOUT"] = 100 if ((Service |& getline) > 0) print $0 else if (ERRNO != "") print ERRNO
Qui vediamo come ottenere dati interattivamente dall’utente26 aspettando per non più di cinque secondi:
PROCINFO["/dev/stdin", "READ_TIMEOUT"] = 5000 while ((getline < "/dev/stdin") > 0) print $0
gawk
termina l’operazione di lettura se l’input non
arriva entro il periodo di tempo limite, restituisce insuccesso
e imposta ERRNO
a una stringa di valore adeguato.
Un valore del tempo limite negativo o pari a zero equivale a non specificare
affatto un tempo limite.
Si può impostare un tempo limite anche per leggere dalla tastiera nel ciclo implicito che legge i record in input e li confronta coi criteri di ricerca, come:
$ gawk 'BEGIN { PROCINFO["-", "READ_TIMEOUT"] = 5000 } > { print "You entered: " $0 }' gawk -| You entered: gawk
In questo caso, la mancata risposta entro cinque secondi dà luogo al seguente messaggio di errore:
error→ gawk: linea com.:2: (FILENAME=- FNR=1) fatale: errore leggendo error→ file in input `-': Connessione scaduta
Il tempo limite può essere impostato o cambiato in qualsiasi momento, e avrà effetto al tentativo successivo di leggere dal dispositivo di input. Nel seguente esempio, partiamo con un valore di tempo limite di un secondo e lo riduciamo progressivamente di un decimo di secondo finché l’attesa per l’input diventa illimitata.
PROCINFO[Service, "READ_TIMEOUT"] = 1000 while ((Service |& getline) > 0) { print $0 PROCINFO[Service, "READ_TIMEOUT"] -= 100 }
NOTA: Non si deve dare per scontato che l’operazione di lettura si blocchi esattamente dopo che è stato stampato il decimo record. È possibile che
gawk
legga e tenga in memoria i dati di più di un record la prima volta. Per questo, cambiare il valore del tempo limite come nell’esempio appena visto non è molto utile.
Se l’elemento di PROCINFO
non è presente e la variabile d’ambiente
GAWK_READ_TIMEOUT
esiste,
gawk
usa il suo valore per inizializzare il valore di tempo limite.
L’uso esclusivo della variabile d’ambiente per specificare il tempo limite
ha lo svantaggio di non essere
adattabile per ogni comando o per ogni connessione.
gawk
considera errore un superamento di tempo limite anche se
il tentativo di leggere dal dispositivo sottostante potrebbe riuscire
in un tentativo successivo. Questa è una limitazione, e inoltre
significa che non è possibile usarlo per ottenere input multipli,
provenienti da due o più sorgenti. Vedi Proseguire dopo errore in input
per una modalità che consente di tentare ulteriori operazioni di I/O.
Assegnare un valore di tempo limite previene un blocco a tempo indeterminato
legato a operazioni di lettura. Si tenga però presente che ci sono altre
situazioni in cui gawk
può restare bloccato in attesa che un
dispositivo di input sia pronto. Un cliente di rete a volte può impiegare
molto tempo per stabilire una
connessione prima di poter iniziare a leggere qualsiasi dato,
oppure il tentativo di aprire un file speciale FIFO in lettura può bloccarsi
indefinitamente in attesa che qualche altro processo lo apra in scrittura.
Successivo: Directory su riga di comando, Precedente: Timeout in lettura, Su: Leggere file [Contenuti][Indice]
Questa
sezione descrive una funzionalità disponibile solo in
gawk
.
Qualora gawk
incontri un errore durante la lettura dell’input,
per default getline
ha come codice di ritorno -1, e i
successivi tentativi di leggere dallo stesso file restituiscono una
indicazione di fine-file. È tuttavia possibile chiedere a
gawk
di consentire un ulteriore tentativo di lettura in presenza
di certi errori, impostando uno speciale elemento del vettore
PROCINFO
(vedi Variabili auto-assegnate):
PROCINFO["nome_input_file", "RETRY"] = 1
Quando un tale elemento esiste, gawk
controlla il valore della
variabile di sistema
(nel linguaggio C)
errno
quando si verifica un errore di I/O.
Se errno
indica che un ulteriore tentativo di lettura può
terminare con successo, getline
ha come codice di ritorno -2
e ulteriori chiamate a getline
possono terminare correttamente.
Questo vale per i seguenti valori di errno
: EAGAIN
,
EWOULDBLOCK
, EINTR
, e ETIMEDOUT
.
Questa funzionalità è utile quando si assegna un valore all’elemento
PROCINFO["nome_input_file", "READ_TIMEOUT"]
o in situazioni
in cui un descrittore di file sia stato configurato per comportarsi in
modo non bloccante.
Successivo: Sommario di Input, Precedente: Proseguire dopo errore in input, Su: Leggere file [Contenuti][Indice]
Per lo standard POSIX, i file che compaiono sulla riga di comando di
awk
devono essere file di testo; è un errore fatale se non lo sono.
La maggior parte delle versioni di awk
genera un errore fatale
quando trova una directory sulla riga di comando.
Per default, gawk
emette un avvertimento se c’è una directory sulla
riga di comando, e in ogni caso la ignora. Questo rende più facile usare
metacaratteri di shell col proprio programma awk
:
$ gawk -f whizprog.awk * Le directory potrebbero far fallire il programma
Se viene data una delle opzioni --posix
o --traditional, gawk
considera invece
una directory sulla riga di comando come un errore fatale.
Vedi Esempio di estensione Readdir per un modo di trattare le directory
come dati usabili da un programma awk
.
Successivo: Esercizi su Input, Precedente: Directory su riga di comando, Su: Leggere file [Contenuti][Indice]
RS
.
Le possibilità sono le seguenti:
Valore di RS | Record separati da … | awk / gawk |
---|---|---|
Un carattere singolo | Quel carattere | awk |
La stringa nulla ("" ) | Serie di due o più ritorni a capo | awk |
Un’espressione regolare | Testo corrispondente alla regexp | gawk |
FNR
indica quanti record sono stati letti dal file in input corrente;
NR
indica quanti record sono stati letti in totale.
gawk
imposta RT
al testo individuato da RS
.
awk
divide
i record in singoli campi, chiamati $1
, $2
e così
via. $0
è l’intero record, e NF
indica quanti campi
contiene. Il metodo di default per dividere i campi utilizza i
caratteri di spazio vuoto.
$NF
.
Ai campi possono anche essere assegnati dei valori, e questo implica che il
valore di $0
sia ricalcolato se ad esso si fa riferimento in seguito.
Fare un assegnamento a un campo con un numero maggiore di NF
crea il
campo e ricostruisce il record, usando OFS
per separare i campi.
Incrementare NF
fa la stessa cosa. Decrementare NF
scarta dei
campi e ricostruisce il record.
Valore del separatore di campo | Campi separati … | awk / gawk |
---|---|---|
FS == " " | Da serie di spazi vuoti | awk |
FS == un solo carattere | Da quel carattere | awk |
FS == espr. reg. | Dal testo che corrisponde alla regexp | awk |
FS == "" | Così ogni singolo carattere è un campo separato | gawk |
FIELDWIDTHS == lista di colonne | Basata sulla posizione del carattere | gawk |
FPAT == regexp | Dal testo attorno al testo corrispondente alla regexp | gawk |
FS
può essere impostato dalla riga di comando con l’opzione
-F.
Si può fare la stessa cosa usando un assegnamento di variabile da riga di
comando.
PROCINFO["FS"]
permette di sapere come i campi sono separati.
getline
nelle sue diverse forme serve per leggere record aggiuntivi
provenienti dal flusso di input di default, da un file, o da una pipe
o da un coprocesso.
PROCINFO[file, "READ_TIMEOUT"]
si può usare per impostare un
tempo limite alle operazioni di lettura da file.
awk
standard;
gawk
le ignora se non è in modalità POSIX.
Precedente: Sommario di Input, Su: Leggere file [Contenuti][Indice]
FIELDWIDTHS
(vedi Dimensione costante),
scrivere un programma per leggere i dati delle elezioni, dove ogni record
rappresenta i voti di un votante. Trovare un modo per definire quali colonne
sono associate a ogni quesito elettorale, e stampare i voti totali,
comprese le astensioni, per ciascun quesito.
Successivo: Espressioni, Precedente: Leggere file, Su: Top [Contenuti][Indice]
Una delle azioni che un programma fa più comunemente, è quella di produrre
stampe, ossia scrivere in output l’input letto, tutto o in parte.
Si può usare l’istruzione print
per una stampa semplice, e l’istruzione
printf
per una formattazione dell’output più sofisticata.
L’istruzione print
non ha un limite al numero di elementi quando
calcola quali valori stampare. Peraltro, con due eccezioni,
non è possibile specificare come stamparli: quante
colonne, se usare una notazione esponenziale o no, etc.
(Per le eccezioni, vedi Separatori di output e
la OFMT.)
Per stampare fornendo delle specifiche, è necessario usare
l’istruzione printf
(vedi Printf).
Oltre alla stampa semplice e formattata, questo capitolo
esamina anche le ridirezioni di I/O verso file e pipe, introduce
i nomi-file speciali che gawk
elabora internamente,
e parla della funzione predefinita close()
.
• Print: | L’istruzione print .
| |
• Esempi su print: | Semplici esempi di
istruzioni print .
| |
• Separatori di output: | I separatori di output e come modificarli. | |
• OFMT: | Controllare l’output di numeri con
print .
| |
• Printf: | l’istruzione printf .
| |
• Ridirezione: | Come ridirigere l’output a diversi file e pipe. | |
• FD speciali: | I/O con FD [Descrittori File] speciali. | |
• File speciali: | Interpretazione nomi file in
gawk . gawk
Permette di accedere a descrittori
file già aperti a inizio esecuzione
| |
• Chiusura file e pipe: | Chiudere file in input e di output e pipe. | |
• Continuazione dopo errori: | Abilitare continuazione dopo errori in output. | |
• Sommario di Output: | Sommario di Output. | |
• Esercizi su Output: | Esercizi. |
Successivo: Esempi su print, Su: Stampare [Contenuti][Indice]
print
L’istruzione print
si usa per produrre dell’output formattato in
maniera semplice, standardizzata. Si
specificano solo le stringhe o i numeri
da stampare, in una lista separata da virgole. Questi elementi sono stampati,
separati tra loro da spazi singoli, e alla fine viene stampato un ritorno a
capo. L’istruzione è simile a questa:
print elemento1, elemento2, …
L’intera lista di elementi può facoltativamente essere racchiusa fra parentesi. Le parentesi sono obbligatorie se qualche espressione presente in uno degli elementi usa l’operatore relazionale ‘>’, che potrebbe essere confuso con una ridirezione dell’output (vedi Ridirezione).
Gli elementi da stampare possono essere stringhe costanti o numeri, campi
del record corrente (come $1
), variabili, o quasiasi espressione
awk
. I valori numerici sono convertiti in stringhe prima di essere
stampati.
Una semplice istruzione ‘print’ senza specificare elementi equivale a
‘print $0’: stampa l’intero record corrente. Per stampare una riga
vuota, si usa ‘print ""’.
Per stampare un testo che non cambia, si usi come elemento una costante
stringa, per esempio "Non v'allarmate"
. Dimenticandosi di mettere
i doppi apici, il testo è preso per un’espressione awk
,
e probabilmente verrà emesso un messaggio di errore. Occorre tener presente
che tra ogni coppia di elementi viene stampato uno spazio.
Si noti che l’istruzione print
è un’istruzione, e non
un’espressione: non è possibile usarla nella parte modello [di ricerca] di
un’istruzione criterio di ricerca–azione, per esempio.
Successivo: Separatori di output, Precedente: Print, Su: Stampare [Contenuti][Indice]
print
Ogni istruzione print
produce almeno una riga in output. Comunque,
non è limitata a una sola riga. Se il valore di un elemento è una stringa
che contiene un ritorno a capo, il ritorno a capo è stampato insieme al
resto della stringa. Una
singola istruzione print
può in questo modo generare un numero
qualsiasi di righe.
Quel che segue è un esempio di stampa di una stringa che contiene al suo interno dei ritorni a capo (la ‘\n’ è una sequenza di protezione, che si usa per rappresentare il carattere di ritorno a capo; vedi Sequenze di protezione):
$ awk 'BEGIN { print "riga uno\nriga due\nriga tre" }' -| riga uno -| riga due -| riga tre
Il prossimo esempio, eseguito sul file inventory-shipped, stampa i primi due campi di ogni record in input, separandoli con uno spazio:
$ awk '{ print $1, $2 }' inventory-shipped -| Jan 13 -| Feb 15 -| Mar 15 …
Un errore frequente usando l’istruzione print
è quello di tralasciare
la virgola tra due elementi. Questo ha spesso come risultato la stampa di
elementi attaccati tra loro, senza lo spazio di separazione. Il motivo per
cui ciò accade è che la scrittura di due
espressioni di stringa in awk
ne indica la concatenazione. Qui si
vede l’effetto dello stesso programma,
senza le virgole:
$ awk '{ print $1 $2 }' inventory-shipped -| Jan13 -| Feb15 -| Mar15 …
Per chi non conosce il file inventory-shipped nessuno
dei due output di esempio risulta molto comprensibile. Una riga iniziale di
intestazione li renderebbe più chiari.
Aggiungiamo qualche intestazione alla nostra tabella dei mesi
($1
) e dei contenitori verdi spediti ($2
). Lo facciamo usando
una regola BEGIN
(vedi BEGIN/END) in modo che le intestazioni siano
stampate una volta sola:
awk 'BEGIN { print "Mese Contenitori" print "----- -----------" } { print $1, $2 }' inventory-shipped
Una volta eseguito, il programma stampa questo:
Mese Contenitori ----- ----------- Jan 13 Feb 15 Mar 15 …
Il solo problema, in effetti, è che le intestazioni e i dati della tabella non sono allineati! Possiamo provvedere stampando alcuni spazi tra i due campi:
awk 'BEGIN { print "Mese Contenitori" print "----- -----------" } { print $1, " ", $2 }' inventory-shipped
Allineare le colonne in questo modo può diventare piuttosto
complicato, quando ci sono parecchie colonne da tenere allineate. Contare gli
spazi per due o tre colonne è semplice, ma oltre questo limite comincia a
volerci molto tempo. Ecco perché è disponibile l’istruzione printf
(vedi Printf);
una delle possibilità che offre è quella di allineare colonne di dati.
NOTA: Si può continuare su più righe sia l’istruzione
printf
semplicemente mettendo un ritorno a capo dopo una virgola qualsiasi (vedi Istruzioni/Righe).
Successivo: OFMT, Precedente: Esempi su print, Su: Stampare [Contenuti][Indice]
Come detto sopra, un’istruzione print
contiene una lista di elementi
separati da virgole. Nell’output, gli elementi sono solitamente separati
da spazi singoli. Non è detto tuttavia che debba sempre essere così; uno
spazio singolo è semplicemnte il valore di default. Qualsiasi stringa di
caratteri può essere usata come
separatore di campo in output impostando la variabile
predefinita OFS
. Il valore iniziale di questa variabile è
la stringa " "
(cioè, uno spazio singolo).
L’output di un’istruzione print
completa è detto un record di
output. Ogni istruzione print
stampa un record di output, e alla fine
ci aggiunge una stringa detta separatore record in output (o
ORS
). Il valore iniziale di ORS
è la stringa "\n"
(cioè, un carattere di ritorno a capo). Quindi, ogni istruzione
print
normalmente genera [almeno] una riga a sé stante.
Per cambiare il tipo di separazione in output di campi e record, si impostano
valori differenti alle variabili OFS
e ORS
. Il posto più
indicato per farlo è nella regola BEGIN
(vedi BEGIN/END), in modo che l’assegnazione abbia effetto prima
dell’elaborazione di ogni record in input. Questi valori si possono
anche impostare dalla riga di comando, prima della lista dei file in input,
oppure usando l’opzione della riga di comando -v
(vedi Opzioni).
L’esempio seguente stampa il primo e il secondo campo di ogni record in input,
separati da un punto e virgola, con una riga vuota aggiunta dopo ogni
ritorno a capo:
$ awk 'BEGIN { OFS = ";"; ORS = "\n\n" } > { print $1, $2 }' mail-list -| Amelia;555-5553 -| -| Anthony;555-3412 -| -| Becky;555-7685 -| -| Bill;555-1675 -| -| Broderick;555-0542 -| -| Camilla;555-2912 -| -| Fabius;555-1234 -| -| Julie;555-6699 -| -| Martin;555-6480 -| -| Samuel;555-3430 -| -| Jean-Paul;555-2127 -|
Se il valore di ORS
non contiene un ritorno a capo, l’output del
programma viene scritto tutto su un’unica riga.
Successivo: Printf, Precedente: Separatori di output, Su: Stampare [Contenuti][Indice]
print
Quando si stampano valori numerici con l’istruzione print
,
awk
converte internamente ogni numero in una stringa di caratteri
e stampa quella stringa. awk
usa la funzione sprintf()
per effettuare questa conversione
(vedi Funzioni per stringhe).
Per ora, basta dire che la funzione sprintf()
accetta una specifica di formato che indica come formattare
i numeri (o le stringhe), e che ci sono svariati modi per formattare i
numeri. Le differenti specifiche di formato sono trattate più
esaurientemente
in
Lettere di controllo.
La variabile predefinita OFMT
contiene la specifica di formato
che print
usa con sprintf()
per convertire un numero in
una stringa per poterla stampare.
Il valore di default di OFMT
è "%.6g"
.
Il modo in cui print
stampa i numeri si può cambiare
fornendo una specifica di formato differente
per il valore di OFMT
, come mostrato nell’esempio seguente:
$ awk 'BEGIN { > OFMT = "%.0f" # Stampa numeri come interi (arrotonda) > print 17.23, 17.54 }' -| 17 18
Per lo standard POSIX, il comportamento di awk
è indefinito
se OFMT
contiene qualcosa di diverso da una specifica di conversione
di un numero a virgola mobile.
(a.b.)
Successivo: Ridirezione, Precedente: OFMT, Su: Stampare [Contenuti][Indice]
printf
per stampe sofisticatePer un controllo più ampio sul formato di output di quello fornito da
print
, si può usare printf
.
Con printf
si può
specificare lo spazio da utilizzare per ogni elemento, e anche le varie
scelte di formattazione disponibile per i numeri (come la base da usare in
output, se stampare con notazione esponenziale, se inserire un segno, e quante
cifre stampare dopo il separatore decimale).
• Printf Fondamenti: | Sintassi dell’istruzione
printf .
| |
• Lettere di controllo: | Lettere di controllo del formato. | |
• Modificatori di formato: | Modificatori specifiche di formato. | |
• Esempi su printf: | Numerosi esempi. |
Successivo: Lettere di controllo, Su: Printf [Contenuti][Indice]
printf
Una semplice istruzione printf
è qualcosa di simile a questo:
printf formato, elemento1, elemento2, …
Come nel caso di print
, l’intera lista degli argomenti può
facoltativamente essere racchiusa fra
parentesi. Anche qui, le parentesi sono obbligatorie se l’espressione di
qualche elemento usa l’operatore
relazionale ‘>’, che potrebbe
essere confuso con una ridirezione dell’output (vedi Ridirezione).
La differenza tra printf
e print
è l’argomento formato.
Questo è un’espressione il cui valore è visto come una stringa;
specifica come scrivere in output ognuno degli altri argomenti. È chiamata
stringa di formato.
La stringa di formato è molto simile a quella usata dalla funzione di
libreria ISO C printf()
. Buona parte del formato è testo da
stampare così come è scritto.
All’interno di questo testo ci sono degli specificatori di formato,
uno per ogni elemento da stampare.
Ogni specificatore di formato richiede di stampare l’elemento successivo
nella lista degli argomenti
in quella posizione del formato.
L’istruzione printf
non aggiunge in automatico un ritorno a capo
al suo output. Scrive solo quanto specificato dalla stringa di formato.
Quindi, se serve un ritorno a capo, questo va incluso nella stringa di formato.
Le variabili di separazione dell’output OFS
e ORS
non hanno
effetto sulle istruzioni printf
.
Per esempio:
$ awk 'BEGIN { > ORS = "\nAHI!\n"; OFS = "+" > msg = "Non v\47allarmate!" > printf "%s\n", msg > }' -| Non v'allarmate!
Qui, né il ‘+’ né l’esclamazione ‘AHI!’ compaiono nel messaggio in output.
Successivo: Modificatori di formato, Precedente: Printf Fondamenti, Su: Printf [Contenuti][Indice]
Uno specificatore di formato inizia col carattere ‘%’ e termina con
una lettera di controllo del formato; e dice all’istruzione
printf
come stampare un elemento. La lettera di controllo del
formato specifica che tipo
di valore stampare. Il resto dello specificatore di formato è costituito da
modificatori facoltativi che controllano come stampare il valore,
per esempio stabilendo la larghezza del campo. Ecco una lista delle
lettere di controllo del formato:
%c
Stampa un numero come un carattere; quindi, ‘printf "%c", 65’ stampa la lettera ‘A’. L’output per un valore costituito da una stringa è il primo carattere della stringa stessa.
NOTA: Lo standard POSIX richiede che il primo carattere di una stringa sia stampato. In localizzazioni con caratteri multibyte,
gawk
tenta di convertire i primi byte della stringa in un carattere multibyte valido e poi di stampare la codifica multibyte di quel carattere. Analogamente, nella stampa di un valore numerico,gawk
ammette che il valore appartenga all’intervallo numerico di valori che possono essere contenuti in un carattere multibyte. Se la conversione alla codifica multibyte non riesce,gawk
usa gli ultimi otto bit della cifra (quelli meno significativi) come carattere da stampare.Altre versioni di
awk
generalmente si limitano a stampare il primo byte di una stringa o i valori numerici che possono essere rappresentati in un singolo byte (0–255).
%d
, %i
Stampa un numero intero in base decimale. Le due lettere di controllo sono equivalenti. (La specificazione ‘%i’ è ammessa per compatibilità con ISO C.)
%e
, %E
Stampa un numero nella notazione scientifica (con uso di esponente). Per esempio:
printf "%4.3e\n", 1950
stampa ‘1.950e+03’, con un totale di quattro cifre significative, tre delle quali seguono il punto che separa la parte intera da quella decimale [in Italia si usa la virgola al posto del punto] (L’espressione ‘4.3’ rappresenta due modificatori, introdotti nella prossima sottosezione). ‘%E’ usa ‘E’ invece di ‘e’ nell’output.
%f
Stampa un numero in notazione a virgola mobile. Per esempio:
printf "%4.3f", 1950
stampa ‘1950.000’, con un totale di quattro cifre significative, tre delle quali vengono dopo il punto decimale. (L’espressione ‘4.3’ rappresenta due modificatori, introdotti nella prossima sottosezione).
In sistemi che implementano il formato a virgola mobile, come specificato dallo standard IEEE 754, il valore infinito negativo è rappresentato come ‘-inf’ o ‘-infinity’, e l’infinito positivo come ‘inf’ o ‘infinity’. Il valore speciale “not a number” [non è un numero] viene scritto come ‘-nan’ o ‘nan’ (vedi Definizioni matematiche).
%F
Come ‘%f’, ma i valori di infinito e di “not a number” sono scritti in lettere maiuscole.
Il formato ‘%F’ è un’estensione POSIX allo standard ISO C; non tutti
i sistemi lo prevedono. In tali casi,
gawk
usa il formato ‘%f’.
%g
, %G
Stampa un numero usando o la notazione scientifica o quella a virgola mobile, scegliendo la forma più concisa; se il risultato è stampato usando la notazione scientifica, ‘%G’ usa ‘E’ invece di ‘e’.
%o
Stampa un numero intero in ottale, senza segno (vedi Numeri non-decimali).
%s
Stampa una stringa.
%u
Stampa un numero intero decimale, senza segno.
(Questo formato è poco usato, perché tutti i numeri in awk
sono a virgola mobile; è disponibile principalmente per compatibilità col
linguaggio C.)
%x
, %X
Stampa un intero esadecimale senza segno; ‘%X’ usa le lettere da ‘A’ a ‘F’ invece che da ‘a’ a ‘f’ (vedi Numeri non-decimali).
%%
Stampa un solo carattere ‘%’. Questa notazione non serve per stampare alcun argomento e ignora eventuali modificatori.
NOTA: Quando si usano lettere di controllo del formato per numeri interi per stampare valori esterni all’intervallo massimo disponibile nel linguaggio C per i numeri interi,
gawk
usa lo specificatore di formato ‘%g’. Se si specifica l’opzione --lint sulla riga di comando (vedi Opzioni),gawk
emette un messaggio di avvertimento. Altre versioni diawk
possono stampare valori non validi, o comportarsi in modo completamente differente. (a.b.)
Successivo: Esempi su printf, Precedente: Lettere di controllo, Su: Printf [Contenuti][Indice]
printf
Una specifica di formato può anche includere dei modificatori che possono controllare che parte stampare del valore dell’elemento, e anche quanto spazio utilizzare per stamparlo. I modificatori sono posizionati tra il ‘%’ e la lettera che controlla il formato. Negli esempi seguenti verrà usato il simbolo del punto elenco “•” per rappresentare spazi nell’output. Questi sono i modificatori previsti, nell’ordine in cui possono apparire:
N$
Una costante intera seguita da un ‘$’ è uno specificatore posizionale. Normalmente, le specifiche di formato sono applicate agli argomenti nell’ordine in cui appaiono nella stringa di formato. Con uno specificatore posizionale, la specifica di formato è applicata a un argomento indicato per numero, invece che a quello che sarebbe il prossimo argomento nella lista. Gli specificatori posizionali iniziano a contare partendo da uno. Quindi:
printf "%s %s\n", "Non", "v'allarmate" printf "%2$s %1$s\n", "v'allarmate", "Non"
stampa per due volte il famoso consiglio amichevole.
A prima vista, questa funzionalità non sembra di grande utilità.
Si tratta in effetti di un’estensione gawk
, pensata per essere
usata nella traduzione di messaggi emessi in fase di esecuzione.
Vedi Ordinamento di printf,
che descrive come e perché usare specificatori posizionali.
Per ora li possiamo ignorare.
-
(Segno meno)Il segno meno, usato prima del modificatore di larghezza (si veda più avanti in questa lista), richiede di allineare a sinistra l’argomento mantenendo la larghezza specificata. Normalmente, l’argomento è stampato allineato a destra, con la larghezza specificata. Quindi:
printf "%-6s", "pippo"
stampa ‘pippo•’.
Applicabile a conversioni numeriche, richiede di inserire uno spazio prima dei valori positivi e un segno meno prima di quelli negativi.
+
Il segno più, usato prima del modificatore di larghezza (si veda più avanti in questa lista), richiede di mettere sempre un segno nelle conversioni numeriche, anche se il dato da formattare ha valore positivo. Il ‘+’ prevale sul modificatore spazio.
#
Richiede di usare una “forma alternativa” per alcune lettere di controllo.
Per ‘%o’, preporre uno zero.
Per ‘%x’ e ‘%X’, preporre ‘0x’ o ‘0X’ se il
numero è diverso da zero.
Per ‘%e’, ‘%E’, ‘%f’, e ‘%F’, il risultato deve contenere
sempre un separatore decimale.
Per %g
e %G
, gli zeri finali non significativi non sono
tolti dal numero stampato.
0
Uno ‘0’ (zero) iniziale serve a richiedere che l’output sia riempito con zeri (invece che con spazi), prima delle cifre significative. Questo si applica solo ai formati di output di tipo numerico. Questo flag ha un effetto solo se la larghezza del campo è maggiore di quella del valore da stampare.
'
Un carattere di apice singolo o un apostrofo è un’estensione POSIX allo standard ISO C. Indica che la parte intera di un valore a virgola mobile, o la parte intera di un valore decimale intero, ha un carattere di separazione delle migliaia. Ciò è applicabile solo alle localizzazioni che prevedono un tale carattere. Per esempio:
$ cat migliaia.awk Visualizza il programma sorgente -| BEGIN { printf "%'d\n", 1234567 } $ LC_ALL=C gawk -f migliaia.awk -| 1234567 Risultato nella localizzazione "C" $ LC_ALL=en_US.UTF-8 gawk -f migliaia.awk -| 1,234,567 Risultato nella localizzazione UTF inglese americana
Per maggiori informazioni relative a localizzazioni e internazionalizzazioni, si veda Localizzazioni.
NOTA: Il flag ‘'’ è una funzionalità interessante, ma utilizza un carattere che è fonte di complicazioni, perché risulta difficila da usare nei programmi scritti direttamente sulla riga di comando. Per informazioni sui metodi appropriati per gestire la cosa, si veda Protezione.
Questo è un numero che specifica la larghezza minima che deve occupare un campo. L’inserimento di un numero tra il segno ‘%’ e il carattere di controllo del formato fa sì che il campo si espanda a quella larghezza. Il modo di default per fare questo è di aggiungere degli spazi a sinistra. Per esempio:
printf "%6s", "pippo"
stampa ‘•pippo’.
il valore di larghezza indica la larghezza minima, non la massima. Se il valore dell’elemento richiede più caratteri della larghezza specificata, questa può essere aumentata secondo necessità. Quindi, per esempio:
printf "%6s", "pippo-pluto"
stampa ‘pippo-pluto’.
Anteponendo un segno meno alla larghezza si richiede che l’output sia esteso con spazi a destra, invece che a sinistra.
.precisione
Un punto, seguito da una costante intera specifica la precisione da usare nella stampa. Il tipo di precisione varia a seconda della lettera di controllo:
%d
, %i
, %o
, %u
, %x
, %X
Minimo numero di cifre da stampare.
%e
, %E
, %f
, %F
Numero di cifre alla destra del separatore decimale.
%g
, %G
Massimo numero di cifre significative.
%s
Massimo numero di caratteri della stringa che possono essere stampati.
Quindi, l’istruzione:
printf "%.4s", "foobar"
stampa ‘foob’.
Le funzionalità di larghezza e precisione dinamiche (cioè,
"%*.*s"
) disponibili nell’istruzione printf
della libreria C sono
utilizzabili.
Invece che fornire esplicitamente una larghezza e/o una precisione
nella stringa di formato, queste sono fornite come parte della lista degli
argomenti. Per esempio:
w = 5 p = 3 s = "abcdefg" printf "%*.*s\n", w, p, s
equivale esattamente a:
s = "abcdefg" printf "%5.3s\n", s
Entrambi i programmi stampano ‘••abc’.
Versioni più datate di awk
non consentivano questa possibilità.
Dovendo usare una di queste versioni, è possibile simulare questa
funzionalità usando la concatenazione per costruire una stringa di formato,
come per esempio:
w = 5 p = 3 s = "abcdefg" printf "%" w "." p "s\n", s
Questo codice non è di facile lettura, ma funziona.
Chi programma in C probabilmente è abituato a specificare modificatori
addizionali (‘h’, ‘j’, ‘l’, ‘L’, ‘t’ e ‘z’) nelle
stringhe di formato di printf
. Questi modificatori non sono validi
in awk
. La maggior parte della implementazioni di awk
li
ignora senza emettere messaggi. Se si specifica l’opzione --lint
sulla riga di comando (vedi Opzioni), gawk
emette un messaggio
di avvertimento quando li si usa. Se si specifica l’opzione --posix,
il loro uso genera un errore fatale.
Precedente: Modificatori di formato, Su: Printf [Contenuti][Indice]
printf
Il seguente semplice esempio mostra
come usare printf
per preparare una tabella allineata:
awk '{ printf "%-10s %s\n", $1, $2 }' mail-list
Questo comando
stampa i nomi delle persone ($1
) nel file
mail-list come una stringa di 10 caratteri allineati a sinistra.
Stampa anche i numeri telefonici ($2
) a fianco, sulla stessa riga.
Il risultato è una tabella allineata, contenente due colonne, di nomi e numeri
telefonici, come si può vedere qui:
$ awk '{ printf "%-10s %s\n", $1, $2 }' mail-list -| Amelia 555-5553 -| Anthony 555-3412 -| Becky 555-7685 -| Bill 555-1675 -| Broderick 555-0542 -| Camilla 555-2912 -| Fabius 555-1234 -| Julie 555-6699 -| Martin 555-6480 -| Samuel 555-3430 -| Jean-Paul 555-2127
In questo caso, i numeri telefonici debbono essere stampati come stringhe, poiché includono un trattino. Una stampa dei numeri telefonici come numeri semplici avrebbe visualizzato solo le prime tre cifre: ‘555’, e questo non sarebbe stato di grande utilità.
Non era necessario specificare una larghezza per i numeri telefonici poiché sono nell’ultima colonnna di ogni riga. Non c’è bisogno di avere un allineamento di spazi dopo di loro.
La tabella avrebbe potuto essere resa più leggibile aggiungendo
intestazioni in cima
alle colonne. Questo si può fare usando una regola BEGIN
(vedi BEGIN/END)
in modo che le intestazioni siano stampate una sola volta, all’inizio del
programma awk
:
awk 'BEGIN { print "Nome Numero" print "---- ------" } { printf "%-10s %s\n", $1, $2 }' mail-list
L’esempio precedente usa sia l’istruzione print
che l’istruzione
printf
nello stesso programma. Si possono ottenere gli stessi
risultati usando solo istruzioni printf
:
awk 'BEGIN { printf "%-10s %s\n", "Nome", "Numero" printf "%-10s %s\n", "----", "------" } { printf "%-10s %s\n", $1, $2 }' mail-list
Stampare ogni intestazione di colonna con la stessa specifica di formato usata per gli elementi delle colonne ci dà la certezza che le intestazioni sono allineate esattamente come le colonne.
Il fatto che usiamo per tre volte la stessa specifica di formato si può evidenziare memorizzandola in una variabile, così:
awk 'BEGIN { format = "%-10s %s\n" printf format, "Nome", "Numero" printf format, "----", "------" } { printf format, $1, $2 }' mail-list
Successivo: FD speciali, Precedente: Printf, Su: Stampare [Contenuti][Indice]
print
e printf
Finora, l’output di print
e printf
è stato diretto
verso lo standard output,
che di solito è lo schermo. Sia print
che printf
possono
anche inviare il loro output in altre direzioni.
È quel che si chiama ridirezione.
NOTA: Quando si specifica --sandbox (vedi Opzioni), la ridirezione dell’output verso file, pipe e coprocessi non è consentita.
Una ridirezione è posta dopo l’istruzione print
o printf
.
Le ridirezioni in awk
sono scritte come le ridirezioni nei
comandi della shell, l’unica differenza è che si trovano all’interno di
un programma awk
.
Ci sono quattro forme di ridirezione dell’output:
output scritto su un file,
output aggiunto in fondo a un file,
output che fa da input a un altro comando (usando una pipe) e
output diretto a un coprocesso.
Vengono descritti per l’istruzione print
,
ma funzionano allo stesso modo per printf
:
print elementi > output-file
Questa ridirezione stampa gli elementi nel file di output chiamato
output-file. Il nome-file output-file può essere
una qualsiasi espressione. Il suo valore è trasformato in una stringa e
quindi usato come
nome-file (vedi Espressioni).
Quando si usa questo tipo di ridirezione, il file output-file viene
cancellato prima che su di esso sia stato scritto il primo record in uscita.
Le successive scritture verso lo stesso file output-file non cancellano
output-file, ma continuano ad aggiungervi record.
(Questo comportamento è differente da quello delle ridirezioni usate negli
script della shell.)
Se output-file non esiste, viene creato. Per esempio, ecco
come un programma awk
può scrivere una lista di nomi di persone
su un file di nome lista-nomi, e una lista di numeri telefonici
su un altro file di nome lista-telefoni:
$ awk '{ print $2 > "lista-telefoni" > print $1 > "lista-nomi" }' mail-list $ cat lista-telefoni -| 555-5553 -| 555-3412 … $ cat lista-nomi -| Amelia -| Anthony …
Ogni file in output contiene un nome o un numero su ogni riga.
print elementi >> output-file
Questa ridirezione stampa gli elementi in un file di output preesistente,
di nome output-file. La differenza tra questa ridirezione e quella
con un solo ‘>’ è che il precedente contenuto (se esiste) di
output-file non viene cancellato. Invece, l’output di awk
è
aggiunto in fondo al file.
Se output-file non esiste, viene creato.
print elementi | comando
È possibile inviare output a un altro programma usando una pipe invece di inviarlo a un file. Questa ridirezione apre una pipe verso comando, e invia i valori di elementi, tramite questa pipe, a un altro processo creato per eseguire comando.
L’argomento comando, verso cui è rivolta la ridirezione, è in realtà
un’espressione
awk
. Il suo valore è convertito in una stringa il cui contenuto
costituisce un comando della shell che deve essere eseguito. Per esempio,
il seguente programma produce due file, una lista non ordinata di nomi di
persone e una lista ordinata in ordine alfabetico inverso:
awk '{ print $1 > "nomi.non.ordinati" comando = "sort -r > nomi.ordinati" print $1 | comando }' mail-list
La lista non ordinata è scritta usando una ridirezione normale, mentre
la lista ordinata è scritta inviando una pipe in input al programma
di utilità sort
.
Il prossimo esempio usa la ridirezione per inviare un messaggio alla
mailing list bug-sistema
. Questo può tornare utile se si hanno
problemi con uno script awk
eseguito periodicamente per la
manutenzione del sistema:
report = "mail bug-sistema" print("Script awk in errore:", $0) | report print("al record numero", FNR, "di", NOME_FILE) | report close(report)
La funzione close()
è stata chiamata perché è una buona idea chiudere
la pipe non appena tutto l’output da inviare alla pipe è stato
inviato. Vedi Chiusura file e pipe
per maggiori informazioni.
Questo esempio illustra anche l’uso di una variabile per rappresentare
un file o un comando; non è necessario usare sempre
una costante stringa. Usare una variabile è di solito una buona idea,
perché (se si vuole riusare lo stesso file o comando)
awk
richiede che il valore della stringa sia sempre scritto
esattamente nello stesso modo.
print elementi |& comando
Questa ridirezione stampa gli elementi nell’input di comando.
La differenza tra questa ridirezione e quella
con la sola ‘|’ è che l’output da comando
può essere letto tramite getline
.
Quindi, comando è un coprocesso, che lavora in parallelo al
programma awk
, ma è al suo servizio.
Questa funzionalità è un’estensione gawk
, e non è disponibile in
POSIX awk
.
Vedi Getline coprocesso,
per una breve spiegazione.
I/O bidirezionale
per un’esposizione più esauriente.
Ridirigere l’output usando ‘>’, ‘>>’, ‘|’ o ‘|&’ richiede al sistema di aprire un file, una pipe o un coprocesso solo se il particolare file o comando che si è specificato non è già stato utilizzato in scrittura dal programma o se è stato chiuso dopo l’ultima scrittura.
È un errore comune usare la ridirezione ‘>’ per la prima istruzione
print
verso un file, e in seguito usare ‘>>’ per le successive
scritture in output:
# inizializza il file print "Non v'allarmate" > "guida.txt" … # aggiungi in fondo al file print "Evitate generatori di improbabilità" >> "guide.txt"
Questo è il modo in cui le ridirezioni devono essere usate lavorando
con la shell. Ma in awk
ciò non è necessario. In casi di questo
genere, un programma dovrebbe
usare ‘>’ per tutte le istruzioni print
, perché il file di
output è aperto una sola volta.
(Usando sia ‘>’ che ‘>>’ nello stesso programma, l’output è prodotto
nell’ordine atteso.
Tuttavia il mischiare gli operatori per lo stesso file è sintomo di uno
stile di programmazione inelegante, e può causare confusione in chi legge
il programma.)
Come visto in precedenza
(vedi Note su getline),
molte
tra le più vecchie implementazioni di
awk
limitano il numero di pipeline che un programma
awk
può mantenere aperte a una soltanto! In gawk
, non c’è
un tale limite. gawk
consente a un programma di
aprire tante pipeline quante ne consente il sistema operativo su cui
viene eseguito.
Inviare pipe alla
sh
Una maniera particolarmente efficace di usare la ridirezione è quella di
preparare righe di comando da passare
come pipe alla shell,
{ printf("mv %s %s\n", $0, tolower($0)) | "sh" } END { close("sh") } La funzione Vedi Apici alla shell per una funzione che può essere utile nel generare righe di comando da passare alla shell. |
Successivo: File speciali, Precedente: Ridirezione, Su: Stampare [Contenuti][Indice]
I programmi in esecuzione hanno convenzionalmente tre flussi di input e output a disposizione, già aperti per la lettura e la scrittura. Questi sono noti come lo standard input, lo standard output e lo standard error output. A questi flussi aperti (e a tutti gli altri file aperti o pipe) si fa spesso riferimento usando il termine tecnico descrittori di file [FD].
Questi flussi sono, per default, associati alla tastiera e allo schermo, ma spesso sono ridiretti, nella shell, utilizzando gli operatori ‘<’, ‘<<’, ‘>’, ‘>>’, ‘>&’ e ‘|’. Lo standard error è tipicamente usato per scrivere messaggi di errore; la ragione per cui ci sono due flussi distinti, standard output e standard error, è per poterli ridirigere indipendentemente l’uno dall’altro.
Nelle tradizionali implementazioni di awk
, il solo modo per
scrivere un messaggio di errore allo
standard error in un programma awk
è il seguente:
print "Ho trovato un errore grave!" | "cat 1>&2"
Con quest’istruzione si apre una pipeline verso un comando della shell
che è in grado di accedere al flusso di standard error che eredita dal
processo awk
.
Questo è molto poco elegante, e richiede anche di innescare un processo
separato. Per questo chi scrive programmi awk
spesso
non usa questo metodo. Invece, invia i messaggi di errore allo
schermo, in questo modo:
print "Ho trovato un errore grave!" > "/dev/tty"
(/dev/tty è un file speciale fornito dal sistema operativo
ed è connesso alla tastiera e allo schermo. Rappresenta il
“terminale”,27 che nei sistemi odierni
è una tastiera e uno schermo, e non una console seriale).
Questo ha generalmente lo stesso effetto, ma non è sempre detto: sebbene il
flusso di standard error sia solitamente diretto allo schermo, potrebbe
essere stato
ridiretto; se questo è il caso, scrivere verso lo schermo non serve. In
effetti, se awk
è chiamato da un lavoro che non è eseguito
interattivamente,
può non avere a disposizione alcun terminale su cui scrivere.
In quel caso, l’apertura di /dev/tty genera un errore.
gawk
, BWK awk
, e mawk
mettono a disposizione
speciali nomi-file per accedere ai tre flussi standard.
Se il nome-file coincide con uno di questi nomi speciali, quando
gawk
(o uno degli altri) ridirige l’input o l’output, usa
direttamente il descrittore di file identificato dal nome-file. Questi
nomi-file sono gestiti così in tutti i sistemi operativi nei quali
gawk
è disponibile, e non solo in quelli che aderiscono allo
standard POSIX:
Lo standard input (descrittore di file 0).
Lo standard output (descrittore di file 1).
Lo standard error output (descrittore di file 2).
Usando questa funzionalità la maniera corretta di scrivere un messaggio di errore diviene quindi:
print "Ho trovato un errore grave!" > "/dev/stderr"
Si noti l’uso di doppi apici per racchiudere il nome-file. Come per ogni altra ridirezione, il valore dev’essere una stringa. È un errore comune omettere i doppi apici, il che conduce a risultati inattesi.
gawk
non tratta questi nomi-file come speciali quando opera
in modalità di compatibilità POSIX. Comunque, poiché BWK awk
li prevede, gawk
li ammette anche quando viene
invocato con l’opzione --traditional (vedi Opzioni).
Successivo: Chiusura file e pipe, Precedente: FD speciali, Su: Stampare [Contenuti][Indice]
gawk
Oltre all’accesso a standard input, standard output e standard error,
gawk
consente di accedere a ogni descrittore di file aperto.
In più, ci sono dei nomi-file speciali riservati per accedere a
reti TCP/IP.
• Altri file ereditati: | Accedere ad altri file aperti con
gawk .
| |
• Reti speciali: | File speciali per comunicazioni con la rete. | |
• Avvertimenti speciali: | Cose a cui prestare attenzione. |
Successivo: Reti speciali, Su: File speciali [Contenuti][Indice]
gawk
Oltre ai valori speciali di nomi-file
/dev/stdin
, /dev/stdout
e /dev/stderr
già menzionati, gawk
prevede una sintassi
per accedere a ogni altro file aperto ereditato:
Il file associato al descrittore di file N. Il file indicato deve
essere aperto dal programma che inizia l’esecuzione di awk
(tipicamente la shell). Se non sono state poste in essere iniziative
speciali nella shell da cui gawk
è stato invocato, solo i
descrittori 0, 1, e 2 sono disponibili.
I nomi-file /dev/stdin, /dev/stdout e /dev/stderr sono essenzialmente alias per /dev/fd/0, /dev/fd/1 e /dev/fd/2, rispettivamente. Comunque, i primi nomi sono più autoesplicativi.
Si noti che l’uso di close()
su un nome-file della forma
"/dev/fd/N"
, per numeri di descrittore di file
oltre il due, effettivamente chiude il descrittore di file specificato.
Successivo: Avvertimenti speciali, Precedente: Altri file ereditati, Su: File speciali [Contenuti][Indice]
I programmi gawk
possono aprire una connessione bidirezionale
TCP/IP, fungendo o da client o da server.
Questo avviene usando uno speciale nome-file della forma:
/tipo-rete/protocollo/porta-locale/host-remoto/porta-remota
il tipo-rete può essere ‘inet’, ‘inet4’ o ‘inet6’. Il protocollo può essere ‘tcp’ o ‘udp’, e gli altri campi rappresentano gli altri dati essenziali necessari per realizzare una connessione di rete. Questi nomi-file sono usati con l’operatore ‘|&’ per comunicare con un coprocesso (vedi I/O bidirezionale). Questa è una funzionalità avanzata, qui riferita solo per completezza. Una spiegazione esauriente sarà fornita nella Reti TCP/IP.
Precedente: Reti speciali, Su: File speciali [Contenuti][Indice]
Sono qui elencate alcune cose da tener presente usando i
nomi-file speciali forniti da gawk
:
gawk
è in modalità compatibile
(o --traditional o --posix;
vedi Opzioni).
gawk
interpreta sempre
questi nomi-file speciali.
Per esempio, se si usa ‘/dev/fd/4’
per l’output, si scrive realmente sul descrittore di file 4, e non su un nuovo
descrittore di file generato duplicando (con dup()
) il descrittore
file 4. Solitamente questo non ha importanza; comunque, è importante
non chiudere alcun file correlato ai descrittori di file 0, 1 e 2.
Se lo si fa, il comportamente risultante è imprevedibile.
Successivo: Continuazione dopo errori, Precedente: File speciali, Su: Stampare [Contenuti][Indice]
Se lo stesso nome-file o lo stesso comando di shell è usato con getline
più di una volta durante l’esecuzione di un programma awk
(vedi Getline),
il file viene aperto (o il comando viene eseguito) solo la prima volta.
A quel punto, il primo record in input è letto da quel file o comando.
La prossima volta che lo stesso file o comando è usato con getline
,
un altro record è letto da esso, e così via.
Analogamente, quando un file o una pipe sono aperti in output,
awk
ricorda
il nome-file o comando a essi associato, e le successive
scritture verso lo stesso file o comando sono accodate alle precedenti
scritture.
Il file o la pipe rimangono aperti fino al termine dell’esecuzione
di awk
.
Questo implica che sono necessari dei passi speciali per rileggere nuovamente
lo stesso file dall’inizio, o per eseguire di nuovo un comando di shell
(invece che leggere ulteriore output dal precedente comando). La funzione
close()
rende possibile fare queste cose:
close(NOME_FILE)
o:
close(comando)
l’argomento NOME_FILE o comando può essere qualsiasi espressione, il cui valore dev’essere esattamente uguale alla stringa usata per aprire il file o eseguire il comando (spazi e altri caratteri “irrilevanti” inclusi). Per esempio, se si apre una pipe così:
"sort -r nomi" | getline pippo
essa va chiusa in questo modo:
close("sort -r nomi")
Una volta eseguita questa chiamata di funzione, la successiva getline
da quel file o comando, o la successiva print
o printf
verso quel
file o comando, riaprono il file o eseguono nuovamente il comando.
Poiché l’espressione da usare per chiudere un file o una pipeline deve
essere uguale all’espressione usata per aprire il file o eseguire il comando,
è buona norma usare una variabile che contenga il nome-file o il comando.
Il precedente esempio cambia come segue:
sortcom = "sort -r nomi" sortcom | getline pippo … close(sortcom)
Questo aiuta a evitare nei programmi awk
errori di battitura
difficili da scoprire. Queste sono alcune delle ragioni per cui è bene
chiudere un file di output:
awk
. Occorre chiudere il file dopo aver finito di scriverlo, e poi
iniziare a rileggerlo con getline
.
awk
. Se i file non vengono chiusi, prima o poi awk
può
superare il limite di sistema per il numero di file aperti in un processo.
È meglio chiudere ogni file quando il programma ha finito di scriverlo.
mail
, il messaggio non è effettivamente inviato finché la pipe
non viene chiusa.
Per esempio, si supponga che una programma invii tramite una pipe
dell’output al programma mail
.
Se invia parecchie linee ridirigendole a questa pipe senza chiuderla,
queste costituiscono un solo messaggio di parecchie righe. Invece, se il
programma chiude la pipe dopo ogni riga dell’output, allora ogni riga
costituisce un messaggio separato.
Se si usano file in numero superiore a quelli che il sistema permette
di mantenere aperti,
gawk
tenta di riutilizzare i file aperti disponibili fra
i file-dati. La possibilità che gawk
lo faccia dipende dalle
funzionalità del sistema operativo, e quindi non è detto che questo riesca
sempre. È quindi sia una buona pratica
che un buon suggerimento per la portabilità quello di usare sempre
close()
sui file, una volta che si è finito di operare su di essi.
In effetti, se si usano molte pipe, è fondamentale che i comandi
vengano chiusi, una volta finita la loro elaborazione. Per esempio, si
consideri qualcosa del tipo:
{ … comando = ("grep " $1 " /qualche/file | un_mio_programma -q " $3) while ((comando | getline) > 0) { elabora output di comando } # qui serve close(comando) }
Questo esempio crea una nuova pipeline a seconda dei dati contenuti in
ogni record.
Senza la chiamata a close()
indicata come commento, awk
genera processi-figlio per eseguire i comandi, fino a che non finisce per
esaurire i descrittori di file
necessari per creare ulteriori pipeline.
Sebbene ogni comando sia stato completato (come si deduce dal codice di
fine-file restituito dalla getline
), il processo-figlio non è
terminato;28
inoltre, e questo è ciò che più ci interessa, il descrittore di file
per la pipe non è chiuso e liberato finché non si chiama
close()
o finché il programma awk
non termina.
close()
non fa nulla (e non emette messaggi) se le viene fornito come
argomento una stringa che non rappresenta un file, una pipe o un
coprocesso che sia stato aperto mediante una ridirezione. In quel caso,
close()
restituisce un valore di ritorno negativo, che indica un
errore. Inoltre, gawk
imposta ERRNO
a una stringa che indica il tipo di errore.
Si noti anche che ‘close(NOME_FILE)’ non ha effetti “magici” sul ciclo
implicito che legge ogni record dei file indicati nella riga di comando.
Si tratta, con ogni probabilità, della chiusura di un file che non era mai
stato aperto con una ridirezione,
e per questo awk
silenziosamente non fa nulla, tranne impostare un
codice di ritorno negativo.
Quando si usa l’operatore ‘|&’ per comunicare con un coprocesso,
è talora utile essere in grado di chiudere un lato della pipe
bidirezionale, senza chiudere l’altro lato.
Questo è possibile fornendo un secondo argomento a close()
.
Come in ogni altra invocazione di close()
,
il primo argomento è il nome del comando o file speciale usato
per iniziare il coprocesso.
Il secondo argomento dovrebbe essere una stringa, con uno dei due valori
"to"
[a] oppure "from"
[da]. Maiuscolo/minuscolo non fa
differenza. Poiché questa è una funzionalità avanzata, la trattazione è
rinviata alla
I/O bidirezionale,
che ne parla più dettagliatamente e fornisce un esempio.
Usare il valore di ritorno di
close()
In molte versioni di Unix comando = "…" comando | getline info retval = close(comando) # errore di sintassi in parecchi Unix awk
In
Tabella 5.1: Valori di ritorno dalla Lo standard POSIX è molto generico; dice che |
Successivo: Sommario di Output, Precedente: Chiusura file e pipe, Su: Stampare [Contenuti][Indice]
Questa sezione descrive una funzionalità specifica di gawk
.
In awk
standard, l’output con print
o printf
a un file che non esiste, o qualche altro errore di I/O (come p.es.
esaurire lo spazio disponibile su disco) è un errore fatale (termina
l’esecuzione del programma).
$ gawk 'BEGIN { print "ciao" > "/file/non/esistente" }' error→ gawk: riga com.:1: fatale: non riesco a ri-dirigere a error→ `/file/non/esistente' (/file o directory non esistente)
gawk
rende possibile accorgersi che c’è stato un errore,
permettendo di tentare di rimediare, o almeno di stampare un messaggio
di errore prima di terminare il programma.
È possibile fare questo in due modi differenti:
PROCINFO["NONFATAL"]
.
PROCINFO[nome_file, "NONFATAL"]
.
nome_file è il nome del file per il quale
si desidera che l’errore di output non faccia terminare il programma.
Una volta abilitata la continuazione dopo un errore di output, si dovrà
controllare la variabile ERRNO
dopo ogni istruzione
print
o printf
diretta a quel file, per controllare che
tutto sia andato a buon fine. È anche una buona idea inizializzare
ERRNO
a zero prima di tentare l’operazione di scrittura.
Per esempio:
$ gawk ' > BEGIN { > PROCINFO["NONFATAL"] = 1 > ERRNO = 0 > print "ciao" > "/file/non/esistente" > if (ERRNO) { > print("Output non riuscito:", ERRNO) > "/dev/stderr" > exit 1 > } > }' error→ Output non riuscito: No such file or directory
gawk
non genera un errore fatale; permette invece
al programma awk
di rendersi conto del problema e di gestirlo.
Questo meccanismo si applica anche allo standard output e allo standard error.
Per lo standard output, si può usare PROCINFO["-", "NONFATAL"]
o PROCINFO["/dev/stdout", "NONFATAL"]
.
Per lo standard error, occorre
usare PROCINFO["/dev/stderr", "NONFATAL"]
.
Se si tenta di aprire un socket TCP/IP (vedi Reti TCP/IP),
gawk
tenta di farlo per un certo numero di volte.
La variabile d’ambiente GAWK_SOCK_RETRIES
(vedi Altre variabili d'ambiente) consente di alterare il numero di
tentativi che gawk
farebbe per default. Tuttavia,
una volta abilitata la continuazione dopo un errore di I/O per un certo
socket, gawk
si limita a un solo tentativo,
lasciando al codice del programma awk
il compito di gestire
l’eventuale problema.
Successivo: Esercizi su Output, Precedente: Continuazione dopo errori, Su: Stampare [Contenuti][Indice]
print
stampa una lista di espressioni separate da virgole.
Le espressioni sono separate tra loro dal valore di OFS
e completate
dal valore di ORS
. OFMT
fornisce il formato di conversione
dei valori numerici per l’istruzione print
.
printf
fornisce un controllo più preciso sull’output,
con lettere di controllo del formato per diversi tipi di dati e vari
modificatori
che cambiano il comportamento delle lettere di controllo del formato.
print
che di printf
può essere ridiretto a
file, pipe, e coprocessi.
gawk
fornisce nomi-file speciali per accedere allo
standard input, standard output e standard error, e per comunicazioni di rete.
close()
per chiudere file aperti, pipe e ridirezioni a
coprocessi.
Per i coprocessi, è possibile chiudere anche soltanto una delle due
direzioni di comunicazione.
print
o
printf
, questi causano la fine del programma.
gawk
consente di proseguire l’elaborazione anche in questo
caso, o per un errore su qualsiasi file in output, o per un errore
relativo a qualche file in particolare.
Resta a carico del programma controllare se si sono verificati errori
dopo l’esecuzione di ogni istruzione di output interessata.
Precedente: Sommario di Output, Su: Stampare [Contenuti][Indice]
awk 'BEGIN { print "Mese Contenitori" print "----- -----------" } { print $1, $2 }' inventory-shipped
come
in
Separatori di output, usando un nuovo valore per OFS
.
printf
per allineare le intestazioni e i dati
della tabella
per il file di esempio inventory-shipped utilizzato nella Print.
BEGIN { print "Ho trovato un errore grave!" > /dev/stderr }
Successivo: Criteri di ricerca e azioni, Precedente: Stampare, Su: Top [Contenuti][Indice]
Le espressioni sono i mattoni fondamentali dei modelli di ricerca e delle
azioni di awk
. Un’espressione genera un valore che si
può stampare, verificare o passare a una funzione. Oltre a ciò, un’espressione
può assegnare un nuovo valore a una variabile o a un campo usando un operatore
di assegnamento.
Un’espressione può servire come modello di ricerca o istruzione di azione a
sé stante. La maggior parte degli altri tipi di istruzione contengono una o
più espressioni che specificano i dati sui quali operare. Come in altri
linguaggi, le espressioni in awk
possono includere variabili,
riferimenti a vettori e a costanti, e chiamate di funzione, come pure
combinazioni tra questi usando diversi operatori.
• Valori: | Costanti, variabili ed espressioni regolari. | |
• Tutti gli operatori: | Gli operatori di gawk .
| |
• Valori e condizioni di verità: | Determinare Vero/Falso. | |
• Chiamate di funzione: | Una chiamata di funzione può essere un’espressione. | |
• Precedenza: | Come si nidificano i vari operatori. | |
• Localizzazioni: | Come la localizzazione influenza la gestione dati. | |
• Sommario delle espressioni: | Sommario delle espressioni. |
Successivo: Tutti gli operatori, Su: Espressioni [Contenuti][Indice]
Le espressioni sono costruite a partire da valori e dalle operazioni eseguite su di essi. Questa sezione descrive gli oggetti elementari che forniscono i valori usati nelle espressioni.
• Costanti: | Costanti di tipo stringa, numeriche ed espressioni regolari | |
• Usare le costanti regexp: | Quando e come usare una costante specificata tramite espressioni regolari | |
• Variabili: | Le variabili permettono di definire valori da usare in seguito. | |
• Conversione: | La conversione di stringhe in numeri e viceversa. |
Successivo: Usare le costanti regexp, Su: Valori [Contenuti][Indice]
Il tipo di espressione più semplice è una costante, che ha sempre lo stesso valore. Ci sono tre tipi di costanti: numeriche, di stringa e di espressione regolare.
Ognuna di esse si usa nel contesto appropriato quando occorre assegnare ai dati un valore che non dovrà essere cambiato. Le costanti numeriche possono avere forme diverse, ma sono memorizzate internamente sempre allo stesso modo.
• Costanti scalari: | Costanti numeriche e stringhe. | |
• Numeri non-decimali: | Cosa sono i numeri ottali ed esadecimali. | |
• Costanti come espressioni regolari: | Costanti fornite tramite espressioni regolari. |
Successivo: Numeri non-decimali, Su: Costanti [Contenuti][Indice]
Una costante numerica è un numero. Questo numero può essere un numero intero, una frazione decimale o un numero in notazione scientifica (esponenziale).30 Ecco alcuni esempi di costanti numeriche che hanno tutte lo stesso valore:
105 1.05e+2 1050e-1
Una costante stringa è formata da una sequenza di caratteri racchiusi tra doppi apici. Per esempio:
"pappagallo"
rappresenta la stringa il cui contenuto è la parola ‘pappagallo’.
Le stringhe in
gawk
possono essere di qualsiasi lunghezza, e possono contenere
tutti i possibili caratteri ASCII a otto bit, compreso il carattere ASCII
NUL (carattere con codice zero).
Altre implementazioni di awk
possono avere difficoltà con alcuni
particolari codici di carattere.
Successivo: Costanti come espressioni regolari, Precedente: Costanti scalari, Su: Costanti [Contenuti][Indice]
In awk
, tutti i numeri sono espressi nel sistema decimale (cioè a
base 10).
Molti altri linguaggi di programmazione consentono di specificare i numeri in
altre basi, spesso in ottale (base 8) e in esadecimale (base 16).
Nel sistema ottale i numeri hanno la sequenza 0, 1, 2, 3, 4, 5, 6, 7, 10, 11,
12, e così via. Come ‘11’ decimale è una volta 10 più 1, così
‘11’ ottale è una volta 8 più 1. Questo equivale a 9 nel sistema decimale.
Nell’esadecimale ci sono 16 cifre. Poiché l’usuale sistema numerico decimale
ha solo dieci cifre (‘0’–‘9’), le lettere da
‘a’ a ‘f’ rappresentano le cifre ulteriori.
(normalmente è irrilevante se le lettere sono maiuscole o minuscole; gli
esadecimali ‘a’ e ‘A’ hanno lo stesso valore).
Così, ‘11’ esadecimale è 1 volta 16 più 1,
il che equivale a 17 decimale.
Guardando solamente un ‘11’ puro e semplice, non si capisce in quale base sia. Così, in C, C++, e in altri linguaggi derivati da C, c’è una speciale notazione per esprimere la base. I numeri ottali iniziano con uno ‘0’, e i numeri esadecimali iniziano con uno ‘0x’ o ‘0X’:
11
Valore decimale 11
011
11 ottale, valore decimale 9
0x11
11 esadecimale, valore decimale 17
Quest’esempio mostra le differenze:
$ gawk 'BEGIN { printf "%d, %d, %d\n", 011, 11, 0x11 }' -| 9, 11, 17
Poter usare costanti ottali ed esadecimali nei propri programmi è molto utile quando si lavora con dati che non possono essere rappresentati convenientemente come caratteri o come numeri regolari, come i dati binari di vario tipo.
gawk
permette l’uso di costanti ottali ed esadecimali nel testo di
un programma. Comunque, se numeri non-decimali sono presenti tra i dati in
input, essi non sono trattati in maniera speciale; trattarli in modo speciale
per default significherebbe che vecchi programmi awk
produrrebbero
risultati errati.
(Se si vuole che i numeri siano trattati in maniera speciale, si deve
specificare l’opzione da riga di comando
--non-decimal-data;
vedi Dati non decimali.)
Se si devono gestire dati ottali o esadecimali,
si può usare la funzione strtonum()
(vedi Funzioni per stringhe)
per convertire i dati in numeri.
La maggior parte delle volte, le costanti ottali o esadecimali si usano quando
si lavora con le funzioni predefinite di manipolazione di bit;
si veda Funzioni a livello di bit
per maggiori informazioni.
Diversamente da alcune tra le prime implementazioni di C, ‘8’ e ‘9’
non sono cifre valide nelle costanti ottali. Per esempio, gawk
tratta ‘018’ come un 18 decimale:
$ gawk 'BEGIN { print "021 è", 021 ; print 018 }' -| 021 è 17 -| 18
Le costanti ottali ed esadecimali nel codice sorgente sono un’estensione di
gawk
. Se gawk
è in modalità compatibile
(vedi Opzioni),
non sono disponibili.
La base di una costante non influisce sul suo valore
Una volta che una costante numerica è stata
convertita internamente in un numero [decimale],
$ gawk 'BEGIN { printf "0x11 vale <%s>\n", 0x11 }' -| 0x11 vale <17> |
Precedente: Numeri non-decimali, Su: Costanti [Contenuti][Indice]
Una costante regexp è la descrizione di un’espressione regolare
delimitata
da barre, come /^inizio e fine$/
. La maggior parte delle
regexp usate nei programmi awk
sono costanti, ma gli operatori
di confronto ‘~’ e ‘!~’ possono confrontare anche regexp
calcolate o dinamiche (che tipicamente sono solo stringhe ordinarie o
variabili che contengono un’espressione regolare, ma potrebbero anche essere
espressioni più complesse).
Le costanti regexp sono costituite da testo che descrive un’espressione
regolare, racchiusa fra barre (come p.e. /la +risposta/
).
Questa
sezione tratta del comportamento di tali costanti in
POSIX awk
e in gawk
, e prosegue descrivendo le
costanti regexp fortemente tipizzate, che sono
un’estensione gawk
.
• Costanti regexp normali: | Costanti regexp normali in
awk .
| |
• Costanti regexp forti: | Costanti regexp fortemente tipizzate. |
Successivo: Costanti regexp forti, Su: Usare le costanti regexp [Contenuti][Indice]
awk
.Quand’è usata a destra degli operatori ‘~’ o ‘!~’,
una costante regexp rappresenta semplicemente l’espressione regolare che
dev’essere confrontata. Comunque, le costanti regexp (come /pippo/
)
possono essere usate come semplici espressioni.
Quando una
costante regexp compare da sola, ha lo stesso significato di quando
compare in un criterio di ricerca (cioè ‘($0 ~ /pippo/)’).
(a.b.)
Vedi Espressioni come criteri di ricerca.
Ciò vuol dire che i due frammenti di codice seguenti:
if ($0 ~ /barfly/ || $0 ~ /camelot/) print "trovato"
e:
if (/barfly/ || /camelot/) print "trovato"
sono esattamente equivalenti. Una conseguenza piuttosto bizzarra di questa regola è che la seguente espressione booleana è valida, ma non fa quel che probabilmente l’autore si aspettava:
# Notare che /pippo/ è alla sinistra della ~ if (/pippo/ ~ $1) print "trovato pippo"
Questo codice “ovviamente” intende verificare se $1
contiene
l’espressione regolare /pippo/
. Ma in effetti l’espressione
‘/pippo/ ~ $1’ significa realmente ‘($0 ~ /pippo/) ~ $1’. In altre
parole, prima confronta il record in input con l’espressione regolare
/pippo/
. Il risultato è zero o uno, a seconda che il confronto dia esito
positivo o negativo. Questo risultato
è poi confrontato col primo campo nel record.
Siccome è improbabile che qualcuno voglia mai fare realmente questo genere di
test, gawk
emette un avvertimento quando vede questo costrutto in
un programma.
Un’altra conseguenza di questa regola è che l’istruzione di assegnamento:
confronta = /pippo/
assegna zero o uno alla variabile confronta
, a seconda
del contenuto del record in input corrente.
Le espressioni regolari costanti possono essere usate anche come primo
argomento delle
funzioni gensub()
, sub()
, e gsub()
, come secondo argomento
della funzione match()
,
e come terzo argomento delle funzioni split()
e patsplit()
(vedi Funzioni per stringhe).
Le moderne implementazioni di awk
, incluso gawk
, permettono
di usare come terzo argomento di split()
una costante regexp, ma
alcune implementazioni più vecchie non lo consentono.
(a.b.)
Poiché alcune funzioni predefinite accettano costanti regexp come
argomenti, può generare confusione l’uso di costanti regexp come
argomenti di funzioni definite dall’utente
(vedi Funzioni definite dall'utente). Per esempio:
function mysub(modello, sostituzione, stringa, globale) { if (globale) gsub(modello, sostituzione, stringa) else sub(modello, sostituzione, stringa) return stringa } { … text = "salve! salve a te!" mysub(/salve/, "ciao", text, 1) … }
In quest’esempio, il programmatore vuol passare una costante regexp alla
funzione definita dall’utente mysub()
, che a sua volta la passa o a
sub()
o a gsub()
. Comunque, quel che realmente succede è che
al parametro modello
è assegnato un valore di uno o zero, a seconda
che $0
corrisponda a /salve/
o no.
gawk
emette un avvertimento quando vede una costante regexp
usata come parametro di una funzione definita dall’utente, poiché
passare un valore vero/falso in questo modo probabilmente non è quello che
si intendeva fare.
Precedente: Costanti regexp normali, Su: Usare le costanti regexp [Contenuti][Indice]
Questa
sezione descrive una funzionalità specifica di gawk
.
Come visto
in
precedente,
le costanti regexp (/…/
) hanno uno strano posto nel
linguaggio awk
. In molti contesti, si comportano come
un’espressione:
‘$0 ~ /…/’. In altri contesti, denotano solo una regexp da
individuare. In nessun caso esse sono davvero “cittadine di serie A” del
linguaggio. Ovvero, non è possibile definire una variabile scalare il cui
tipo sia “regexp” nello stesso senso in cui si può definire una
variabile che sia un numero o una stringa:
num = 42 Variabile numerica str = "hi" Variabile di tipo stringa re = /pippo/ Errore! re è il risultato di $0 ~ /pippo/
Per alcuni casi di uso più avanzati, sarebbe bello poter avere costanti regexp che sono fortemente tipizzate; in altre parole, che sostituiscono una regexp utile per effettuare dei confronti, e non una semplice espressione regolare.
gawk
prevede questa funzionalità. Un’espressione regolare
fortemente tipizzata è molto simile a un’espressione regolare normale,
tranne per il fatto di essere preceduta dal simbolo ‘@’:
re = @/foo/ variabile regexp fortemente tipizzata
Le variabili regexp fortemente tipizzate non possono essere usate in ogni istruzione in cui compare un’espressione regolare normale. perché ciò renderebbe il linguaggio ancora più fuorviante. Queste espressioni possono essere usate solo in alcuni contesti:
case
di un’istruzione switch
(vedi Istruzione switch).
gensub()
,
gsub()
,
match()
,
patsplit()
,
split()
e
sub()
(vedi Funzioni per stringhe).
qualche_variabile
è di tipo regexp.
Inoltre, qualche_variabile
può essere usata con gli operatori ‘~’ e ‘!~’, passata a una
delle funzioni predefinite sopra elencate o passata come parametro
a una funzione definita dall’utente.
Si può usare la funzione predefinita typeof()
(vedi Funzioni per i tipi)
per determinare se un parametro passato a una funzione
è una variabile di tipo regexp.
La vera efficacia di questa funzionalità consiste nella capacità di creare variabili il cui tipo è regexp. Tali variabili possono essere passate a funzioni definite dall’utente, senza gli inconvenenti che si hanno usando espressioni regolari calcolate, a partire da stringhe o da costanti di tipo stringa. Queste variabili possono anche essere passate utilizzando chiamate indirette a funzioni (vedi Chiamate indirette) e alle funzioni predefinite che accettano costanti di tipo regesp.
Quando sono usate per effettuare conversioni numeriche, le variabili regexp fortemente tipizzate vengono convertite alla cifra zero. Quando sono usate per effettuare conversioni a stringhe, vengono convertite al valore di stringa del testo della regexp originale.
Successivo: Conversione, Precedente: Usare le costanti regexp, Su: Valori [Contenuti][Indice]
Le variabili consentono di memorizzare valori in un certo punto di un
programma, per usarli più tardi in un’altra parte del programma. Le variabili
possono essere gestite interamente all’interno del testo del programma, ma
ad esse possono essere assegnati valori sulla riga di comando, in fase di
invocazione di awk
.
• Usare variabili: | Usare variabili nei propri programmi. | |
• Opzioni di assegnamento: | Impostare variabili dalla riga di comando, e un sommario della sintassi della riga di comando. Questo è un metodo di input avanzato. |
Successivo: Opzioni di assegnamento, Su: Variabili [Contenuti][Indice]
Le variabili permettono di dare nomi ai valori e di far riferimento ad essi in
un secondo momento. Alcune variabili sono già state usate in molti degli
esempi.
Il nome di una variabile dev’essere una sequenza di lettere, cifre o trattini
bassi, e non deve iniziare con una cifra.
Qui, una lettera è una qualsiasi delle 52 lettere maiuscole e minuscole
dell’alfabeto inglese. Altri caratteri che possono essere definiti come
lettere in localizzazioni non inglesi non sono validi nei nomi di variabile.
Il maiuscolo o minuscolo sono significativi nei nomi di variabile;
a
e A
sono variabili diverse.
Un nome di variabile è un’espressione valida in se stessa; rappresenta il
valore corrente della variabile. I valori delle variabili possono essere
modificati tramite operatori di assegnamento, operatori di
incremento e operatori di decremento
(Vedi Operatori di assegnamento).
Inoltre, le funzioni sub()
e gsub()
possono cambiare il valore
di una variabile e le funzioni
match()
, split()
, e patsplit()
possono cambiare il
contenuto dei loro parametri che sono
costituiti da vettori
(vedi Funzioni per stringhe).
Alcune variabili hanno un significato speciale predefinito, come FS
(il separatore di campo) e NF
(il numero di campi nel record di input
corrente). Vedi Variabili predefinite per un elenco delle variabili
predefinite. Queste variabili predefinite possono essere usate e possono
ricevere assegnamenti come tutte le altre variabili, ma i loro valori sono
anche usati o cambiati automaticamente da awk
. Tutti i nomi delle
variabili predefinite sono in caratteri maiuscoli.
Alle variabili in awk
possono essere assegnati valori numerici o
valori di stringa. Il tipo di valore che una variabile contiene può cambiare
durante la vita di un programma. Per default, le variabili sono inizializzate
alla stringa nulla, che vale zero se viene convertita in un numero. Non c’è
alcuna
necessità di inizializzare esplicitamente una variabile in awk
,
come invece occorre fare in C e nella maggior parte dei linguaggi
tradizionali.
Precedente: Usare variabili, Su: Variabili [Contenuti][Indice]
Si può impostare qualsiasi variabile awk
includendo un
assegnamento di variabile tra gli argomenti sulla riga di comando quando
viene invocato awk
(vedi Altri argomenti).
Tale assegnamento ha la seguente forma:
variabile=testo
Con questo assegnamento, una variabile viene impostata o all’inizio
dell’esecuzione di awk
o tra la lettura di un file in input e il
successivo file in input.
Quando l’assegnamento è preceduto dall’opzione -v,
come nel seguente esempio:
-v variabile=testo
la variabile è impostata proprio all’inizio, ancor prima che sia eseguita la
regola BEGIN
. L’opzione -v e il suo assegnamento
deve precedere tutti gli argomenti nome-file, e anche il testo del programma.
(Vedi Opzioni per maggiori informazioni sull’opzione
-v.)
In alternativa, l’assegnamento di variabile è effettuata in un momento
determinato
dalla posizione dell’opzione tra gli argomenti "file in input", cioè dopo
l’elaborazione del precedente argomento "file in input". Per esempio:
awk '{ print $n }' n=4 inventory-shipped n=2 mail-list
stampa il valore del campo numero n
per tutti i record in input. Prima
che venga letto il primo file, la riga di comando imposta la variabile n
al valore quattro. Questo fa sì che venga stampato il quarto campo delle righe
del file inventory-shipped. Dopo la fine del primo file, ma prima
che inizi il secondo file, n
viene impostato a due, e quindi poi
viene stampato il secondo campo delle righe di mail-list:
$ awk '{ print $n }' n=4 inventory-shipped n=2 mail-list -| 15 -| 24 … -| 555-5553 -| 555-3412 …
Gli argomenti da riga di comando sono resi disponibili dal programma
awk
nel vettore ARGV
per poter essere esaminati esplicitamente
(vedi ARGC e ARGV).
awk
elabora i valori degli assegnamenti da riga di comando per
sequenze di protezione
(vedi Sequenze di protezione).
(a.b.)
Le conversioni di numeri in stringhe e di stringhe in numeri sono generalmente
semplici. Ci possono essere delle sottigliezze che bisogna tenere presenti;
questa sezione tratta di quest’importante sfaccettatura di awk
.
• Stringhe e numeri: | Come awk converte tra
stringhe e numeri.
| |
• Localizzazione e conversioni: | Come la localizzazione può influire sulle conversioni. |
Successivo: Localizzazione e conversioni, Su: Conversione [Contenuti][Indice]
awk
converte tra stringhe e numeriLe stringhe sono convertite in numeri e i numeri sono convertiti in stringhe,
se il contesto del programma awk
lo richiede. Per esempio, se il
valore di pippo
o pluto
nell’espressione ‘pippo + pluto’
è una stringa, viene convertita in un numero prima di eseguire l’addizione.
Se in una concatenazione di stringhe ci sono valori numerici, questi sono
convertiti in stringhe. Si consideri il seguente esempio:
due = 2; tre = 3 print (due tre) + 4
Stampa il valore (numerico) di 27. I valori numerici delle
variabili due
e tre
sono convertiti in stringhe e
concatenati insieme. La stringa risultante è riconvertita nel
numero 23, al quale poi viene aggiunto 4.
Se, per qualche ragione, si vuole forzare la conversione di un numero in
una stringa, basta concatenare a quel numero la stringa nulla, ""
.
Per forzare la conversione di una stringa in un numero, basta aggiungere zero
a quella stringa. Una stringa viene convertita in un numero interpretando
qualsiasi prefisso numerico della stringa come numero:
"2.5"
si converte in 2.5, "1e3"
si converte in 1000, e
"25fix"
ha un valore numerico di 25.
Le stringhe che non possono essere interpretate come numeri validi vengono
convertite al valore zero.
Il modo esatto in cui i numeri sono convertiti in stringhe è controllato dalla
variabile predefinita di awk
CONVFMT
(vedi Variabili predefinite). I numeri vengono convertiti usando la
funzione sprintf()
con CONVFMT
come specificatore di formato
(vedi Funzioni per stringhe).
Il valore di default di CONVFMT
è "%.6g"
, che crea un valore con
un massimo di sei cifre significative. Per alcune applicazioni potrebbe essere
opportuno cambiare questo valore per ottenere una maggiore precisione.
Sulla maggior parte delle macchine moderne
normalmente bastano 17 cifre per esprimere esattamente il valore di un numero
in virgola mobile.31
Si possono avere strani risultati se si imposta CONVFMT
a una stringa
che non indica a sprintf()
come formattare i numeri in virgola mobile
in un modo utile. Per esempio, se ci si dimentica la ‘%’ nel formato,
awk
converte tutti i numeri alla stessa stringa costante.
Come caso particolare, per un numero intero, il risultato della conversione
a una stringa è sempre un numero intero, indipendentemente da quale
sia il valore di CONVFMT
. Dato il seguente fammento di codice:
CONVFMT = "%2.2f" a = 12 b = a ""
b
ha valore "12"
, non "12.00"
.
(a.b.)
awk prima di POSIX usava OFMT per la conversione di stringhe
Prima dello standard POSIX, |
Precedente: Stringhe e numeri, Su: Conversione [Contenuti][Indice]
Il luogo dove si è può avere importanza quando si tratta di convertire numeri e
stringhe. La lingua e i caratteri—la localizzazione—possono
influire sui formati numerici. In particolare, per i programmi awk
,
influiscono sui caratteri separatore decimale e separatore delle migliaia.
La localizzazione "C"
, e la maggior parte delle localizzazioni inglesi,
usano il punto (‘.’) come separatore decimale e non prevedono un
separatore delle
migliaia. Tuttavia, molte (se non la maggior parte) delle localizzazioni
europee e non inglesi usano la virgola (‘,’) come separatore dei decimali.
Le localizzazioni europee spesso usano o lo spazio o il punto come separatore
delle migliaia, all’occorrenza.
Lo standard POSIX prevede che awk
usi sempre il punto come separatore
dei decimali nel codice sorgente del programma awk
, e per
gli assegnamenti di variabile da riga di comando (vedi Altri argomenti).
Tuttavia, nell’interpretazione dei dati in input, per l’output di
print
e printf
, e per la conversione da numeri a stringhe,
viene usato il
separatore decimale locale. (a.b.) In ogni caso, i numeri nel
codice sorgente e nei dati di input non possono avere un separatore delle
migliaia. Di seguito sono riportati alcuni esempi che illustrano la differenza
di comportamento, su un sistema GNU/Linux:
$ export POSIXLY_CORRECT=1 Forzare aderenza a standard POSIX $ gawk 'BEGIN { printf "%g\n", 3.1415927 }' -| 3.14159 $ LC_ALL=en_DK.utf-8 gawk 'BEGIN { printf "%g\n", 3.1415927 }' -| 3,14159 $ echo 4,321 | gawk '{ print $1 + 1 }' -| 5 $ echo 4,321 | LC_ALL=en_DK.utf-8 gawk '{ print $1 + 1 }' -| 5,321
La localizzazione en_DK.utf-8
è per l’inglese in Danimarca, dove
le virgole fungono da separatore decimale. Nella localizzazione "C"
normale, gawk
tratta ‘4,321’ come 4, mentre nella localizzazione
danese è trattato come numero completo comprendente la parte frazionaria,
4.321.
Alcune delle prime versioni di gawk
si conformavano completamente con
quest’aspetto dello standard. Tuttavia, molti utenti di localizzazioni non
inglesi si lamentavano di questo comportamento, perché i loro dati usavano il
punto come separatore decimale, per cui fu ripristinato il comportamento di
default che usava il punto come carattere di separazione decimale. Si può
usare l’opzione --use-lc-numeric (vedi Opzioni) per forzare
gawk
a usare il carattere separatore decimale della localizzazione.
(gawk
usa il separatore decimale della localizzazione anche quando
è in modalità POSIX, o con l’opzione --posix o con la variabile
d’ambiente POSIXLY_CORRECT
, come appena visto.)
Tabella 6.1 descrive i casi in cui si usa il separatore decimale locale e quando si usa il punto. Alcune di queste funzionalità non sono state ancora descritte.
Funzione | Default | --posix o --use-lc-numeric |
---|---|---|
%'g | Usa la localizzazione | Usa la localizzazione |
%g | Usa il punto | Usa la localizzazione |
Input | Usa il punto | Usa la localizzazione |
strtonum() | Usa il punto | Usa la localizzazione |
Tabella 6.1: Separatore decimale locale o punto
Infine, gli standard ufficiali correnti e la rappresentazione dei numeri
in virgola mobile dello standard IEEE possono avere un effetto insolito ma
importante sul modo in cui gawk
converte alcuni valori di stringa
speciali in
numeri. I dettagli sono illustrati in Problemi virgola mobile POSIX.
Successivo: Valori e condizioni di verità, Precedente: Valori, Su: Espressioni [Contenuti][Indice]
Questa sezione introduce gli operatori che fanno uso dei valori forniti da costanti e variabili.
• Operatori aritmetici: | Operazioni aritmetiche (‘+’, ‘-’, etc.) | |
• Concatenazione: | Concatenazione di stringhe. | |
• Operatori di assegnamento: | Cambiare il valore di una variabile o di un campo. | |
• Operatori di incremento: | Incrementare il valore numerico di una variabile. |
Successivo: Concatenazione, Su: Tutti gli operatori [Contenuti][Indice]
Il linguaggio awk
usa i comuni operatori aritmetici nella valutazione
delle espressioni. Tutti questi operatori aritmetici seguono le normali regole
di precedenza e funzionano come ci si aspetta.
Il seguente esempio usa un file chiamato grades, che contiene una lista di nomi di studenti e anche i voti di tre verifiche per ogni studente (è una piccola classe):
Pat 100 97 58 Sandy 84 72 93 Chris 72 92 89
Questo programma prende il file grades e stampa la media dei voti:
$ awk '{ sum = $2 + $3 + $4 ; avg = sum / 3 > print $1, avg }' grades -| Pat 85 -| Sandy 83 -| Chris 84.3333
La lista seguente elenca gli operatori aritmetici in awk
,
in ordine di precedenza, da quella più alta a quella più bassa:
x ^ y
x ** y
Elevamento a potenza; x elevato alla potenza y. ‘2 ^ 3’ ha il valore otto; la sequenza di caratteri ‘**’ è equivalente a ‘^’. (e.c.)
- x
Negazione.
+ x
Più unario; l’espressione è convertita in un numero.
x * y
Moltiplicazione.
x / y
Divisione; poiché tutti i numeri in awk
sono numeri in virgola
mobile, il risultato non è arrotondato all’intero—‘3 / 4’ ha il
valore di 0.75. (Un errore comune, specialmente tra i programmatori in C, è
quello di dimenticare che tutti i numeri in awk
sono in virgola mobile,
e che la divisione di costanti rappresentate da numeri interi produce un
numero reale, non un numero intero.)
x % y
Resto della divisione; subito dopo questa lista, l’argomento viene ulteriormente dettagliato.
x + y
Addizione.
x - y
Sottrazione.
Il più e il meno unari hanno la stessa precedenza, gli operatori di moltiplicazione hanno tutti la stessa precedenza, e l’addizione e la sottrazione hanno la stessa precedenza.
Quando si calcola il resto di ‘x % y’, il quoziente è troncato all’intero e moltiplicato per y. Questo risultato è sottratto da x; quest’operazione è nota anche come “modulo”. La seguente relazione è sempre verificata:
b * int(a / b) + (a % b) == a
Un possibile effetto indesiderato di questa definizione di resto è che ‘x % y’ sia negativo se x è negativo. Così:
-17 % 8 = -1
In altre implementazioni di awk
il segno del resto
può essere dipendente dalla macchina.
NOTA: Lo standard POSIX specifica solo l’uso di ‘^’ per l’elevamento a potenza. Per garantire la massima portabilità è meglio non usare l’operatore ‘**’.
Successivo: Operatori di assegnamento, Precedente: Operatori aritmetici, Su: Tutti gli operatori [Contenuti][Indice]
Allora ci era sembrata una buona idea.
C’è una sola operazione di stringa: la concatenazione. Non ha un operatore specifico per rappresentarla. Piuttosto, la concatenazione è effettuata scrivendo le espressioni l’una vicino all’altra, senza alcun operatore. Per esempio:
$ awk '{ print "Campo numero uno: " $1 }' mail-list -| Campo numero uno: Amelia -| Campo numero uno: Anthony …
Senza lo spazio nella costante stringa dopo ‘:’, la riga rimane unita. Per esempio:
$ awk '{ print "Campo numero uno:" $1 }' mail-list -| Campo numero uno:Amelia -| Campo numero uno:Anthony …
Poiché la concatenazione di stringhe non ha un operatore esplicito, è spesso
necessario assicurarsi che venga effettuata al momento giusto usando le
parentesi per racchiudere gli elementi da concatenare. Per esempio, ci si
potrebbe aspettare che il
seguente fammento di codice concateni nome
e file
:
nome = "nome" file = "file" print "qualcosa di significativo" > nome file
Questo produce un errore di sintassi in alcune versioni di
awk
per Unix.32
È necessario usare la seguente sintassi:
print "qualcosa di significativo" > (nome file)
Si dovrebbero usare le parentesi attorno alle concatenazioni in tutti i
contesti non comuni, come, per esempio, sul lato destro di ‘=’.
Bisogna stare attenti
al tipo di espressioni usate nella concatenazione di stringhe. In particolare,
l’ordine di valutazione di espressioni usate per la concatenazione non è
definita nel linguaggio awk
. Si consideri quest’esempio:
BEGIN { a = "Non" print (a " " (a = "v'allarmate")) }
Non è definito se il secondo assegnamento ad a
debba avvenire
prima o dopo il recupero del valore di a
per produrre il
valore concatenato. Il risultato potrebbe essere sia ‘Non v'allarmate’,
sia ‘v'allarmate v'allarmate’.
La precedenza della concatenazione, quando è in combinazione con altri operatori, è spesso controintuitiva. Si consideri questo esempio:
$ awk 'BEGIN { print -12 " " -24 }' -| -12-24
Quest’esempio, “ovviamente” concatena -12, uno spazio, e -24.
Ma dov’è finito lo spazio?
La risposta sta nella combinazione di precedenze di operatori e nelle regole di
conversione automatica di awk
. Per ottenere il risultato desiderato,
si deve scrivere il programma in questo modo:
$ awk 'BEGIN { print -12 " " (-24) }' -| -12 -24
Questo forza il trattamento, da parte di awk
, del ‘-’ del
‘-24’ come operatore unario. Altrimenti è analizzato in questo modo:
-12 (" "
- 24)
⇒ -12 (0 - 24)
⇒ -12 (-24)
⇒ -12-24
Come si è detto precedentemente, quando si usa la concatenazione insieme ad altri operatori, è necessario usare le parentesi. Altrimenti, non si può essere mai completamente certi di quel che si ottiene.
Successivo: Operatori di incremento, Precedente: Concatenazione, Su: Tutti gli operatori [Contenuti][Indice]
Un assegnamento è un’espressione che memorizza un valore (generalmente
diverso da quello che la variabile aveva in precedenza) in una variabile.
Per esempio, si assegni il valore uno alla variabile z
:
z = 1
Dopo l’esecuzione di quest’espressione, la variabile z
ha il valore
uno. Qualsiasi precedente valore di z
prima dell’assegnamento
viene dimenticato.
Gli assegnamenti possono anche memorizzare valori di stringa. Il seguente
esempio memorizza
il valore "questo cibo è buono"
nella variabile messaggio
:
cosa = "cibo" predicato = "buono" messaggio = "questo " cosa " è " predicato
Quest’esempio illustra anche la concatenazione di stringhe. Il segno ‘=’ è un operatore di assegnamento. È il più semplice fra gli operatori di assegnamento perché il valore dell’operando di destra è memorizzato invariato. La maggior parte degli operatori (addizione, concatenazione e così via) non fanno altro che calcolare un valore. Se il valore non viene poi utilizzato non c’è alcun motivo per usare l’operatore. Un operatore di assegnamento è differente; produce un valore; anche se poi non lo si usa, l’assegnamento svolge ancora una funzione alterando la variabile. Chiamiamo questo un effetto collaterale.
L’operando di sinistra non dev’essere necessariamente una variabile (vedi Variabili); può essere anche un campo (vedi Cambiare i campi) o un elemento di un vettore (vedi Vettori). Questi operandi sono chiamati lvalue, il che significa che possono apparire sul lato sinistro di un operatore di assegnamento. L’operando sul lato destro può essere qualsiasi espressione; produce un nuovo valore che l’assegnamento memorizza nella variabile, nel campo o nell’elemento di vettore specificati. Tali valori sono chiamati rvalue.
È importante notare che le variabili non hanno dei tipi permanenti.
Il tipo di una variabile è semplicemente quello di qualsiasi valore le sia stato
assegnato per ultimo. Nel seguente frammento di programma, la variabile
pippo
ha dapprima un valore numerico, e in seguito un valore di stringa:
pippo = 1 print pippo pippo = "pluto" print pippo
Quando il secondo assegnamento dà a pippo
un valore di stringa, il fatto
che avesse precedentemente un valore numerico viene dimenticato.
Ai valori di stringa che non iniziano con una cifra viene assegnato il valore
numerico zero. Dopo l’esecuzione del seguente codice, il valore di pippo
è
cinque:
pippo = "una stringa" pippo = pippo + 5
NOTA: Usare una variabile sia come numero che come stringa può originare confusione e denota uno stile di programmazione scadente. I due esempi precedenti illustrano come funziona
awk
, non come si dovrebbero scrivere i programmi!
Un assegnamento è un’espressione, per cui ha un valore: lo stesso valore che le è stato assegnato. Così, ‘z = 1’ è un’espressione col valore uno. Una conseguenza di ciò è che si possono scrivere più assegnamenti insieme, come:
x = y = z = 5
Quest’esempio memorizza il valore cinque in tutte e tre le variabili,
(x
, y
e z
).
Questo perché il
valore di ‘z = 5’, che è cinque, è memorizzato in y
e poi
il valore di ‘y = z = 5’, che è cinque, è memorizzato in x
.
Gli assegnamenti possono essere usati ovunque sia prevista un’espressione. Per
esempio, è valido scrivere ‘x != (y = 1)’ per impostare y
a
uno, e poi verificare se x
è uguale a uno. Però questo stile rende i
programmi difficili da leggere; una tale nidificazione di assegnamenti dovrebbe
essere evitata, eccetto forse in un programma usa-e-getta.
Accanto a ‘=’, ci sono diversi altri operatori di assegnamento che
eseguono calcoli col vecchio valore di una variabile. Per esempio,
l’operatore ‘+=’ calcola un nuovo valore aggiungendo il valore sul lato
destro al vecchio valore di una variabile. Così, il seguente assegnamento
aggiunge cinque al valore di pippo
:
pippo += 5
Questo è equivalente a:
pippo = pippo + 5
Si usi la notazione che rende più chiaro il significato del programma.
Ci sono situazioni in cui usare ‘+=’ (o qualunque operatore di assegnamento) non è la stessa cosa che ripetere semplicemente l’operando di sinistra nell’espressione di destra. Per esempio:
# Grazie a Pat Rankin per quest'esempio BEGIN { pippo[rand()] += 5 for (x in pippo) print x, pippo[x] pluto[rand()] = pluto[rand()] + 5 for (x in pluto) print x, pluto[x] }
È praticamente certo che gli indici di pluto
siano differenti, perché
rand()
restituisce valori differenti ogni volta che viene chiamata.
(I vettori e la funzione rand()
non sono ancora stati trattati.
Vedi Vettori,
e
vedi Funzioni numeriche
per maggiori informazioni.)
Quest’esempio illustra un fatto importante riguardo agli operatori di
assegnamento: l’espressione di sinistra viene valutata una volta sola.
Dipende dall’implementazione stabilire quale espressione valutare per prima, se quella di sinistra o quella di destra. Si consideri quest’esempio:
i = 1 a[i += 2] = i + 1
Il valore di a[3]
potrebbe essere sia due sia quattro.
La Tabella 6.2 elenca gli operatori di assegnamento aritmetici. In ogni caso, l’operando di destra è un’espressione il cui valore è convertito in un numero.
Tabella 6.2: Operatori di assegnamento aritmetici
NOTA: Soltanto l’operatore ‘^=’ è definito da POSIX. Per avere la massima portabilità, non usare l’operatore ‘**=’.
Precedente: Operatori di assegnamento, Su: Tutti gli operatori [Contenuti][Indice]
Gli operatori di incremento e decremento incrementano o riducono il
valore di una variabile di uno. Un operatore di assegnamento può fare la
stessa cosa, per cui gli operatori di incremento non aggiungono funzionalità
al inguaggio awk
; in ogni caso, sono delle convenienti abbreviazioni
per operazioni molto comuni.
L’operatore per aggiungere uno è ‘++’. Può essere usato per incrementare
una variabile prima o dopo aver stabilito il suo valore. Per preincrementare
una variabile v
, si scrive ‘++v’. Questo aggiunge uno al valore di
v
; questo nuovo valore è anche il valore dell’espressione.
(L’espressione di assegnamento ‘v += 1’ è totalmente equivalente.)
Scrivendo ‘++’ dopo la variabile si specifica un postincremento.
Questo incrementa il valore della variabile nello stesso modo; la differenza è
che il valore dell’espressione d’incremento è il vecchio valore della
variabile. Così, se pippo
ha il valore quattro, l’espressione
‘pippo++’ ha il valore quattro, ma cambia il valore di pippo
in cinque.
In altre parole, l’operatore restituisce il vecchio valore della variabile, ma
con l’effetto collaterale di incrementarlo.
Il postincremento ‘pippo++’ è quasi come scrivere ‘(pippo += 1) - 1’.
Non è perfettamente equivalente perché tutti i numeri in awk
sono in
virgola mobile. In virgola mobile, ‘pippo + 1 - 1’ non è necessariamente
uguale a pippo
, ma la differenza è molto piccola finché si ha a che
fare con numeri relativamente piccoli (inferiori a
1012).
I campi di un record e gli elementi di un vettore vengono incrementati proprio come le variabili. (Si deve usare ‘$(i++)’ quando si deve fare un riferimento a un campo e incrementare una variabile allo stesso tempo. Le parentesi sono necessarie a causa della precedenza dell’operatore di riferimento ‘$’.)
L’operatore di decremento ‘--’ funziona proprio come ‘++’, solo che sottrae uno anziché aggiungerlo. Come ‘++’, si può usare prima di lvalue per predecrementare o dopo per postdecrementare. Quel che segue è un sommario delle espressioni di incremento e di decremento:
++lvalue
Incrementa lvalue, restituendo il nuovo valore come valore dell’espressione.
lvalue++
Incrementa lvalue, restituendo il vecchio valore di lvalue come valore dell’espressione.
--lvalue
Decrementa lvalue, restituendo il nuovo valore come valore dell’espressione. (Quest’espressione è come ‘++lvalue’, ma invece di aggiungere, sottrae.)
lvalue--
Decrementa lvalue, restituendo il vecchio valore di lvalue come valore dell’espressione. (Quest’espressione è come ‘lvalue++’, ma invece di aggiungere, sottrae.)
Successivo: Chiamate di funzione, Precedente: Tutti gli operatori, Su: Espressioni [Contenuti][Indice]
In certi contesti, i valori delle espressioni servono anche come
“valori di verità”; cioè, determinano quale sarà la direzione che il
programma prenderà durante la sua esecuzione. Questa
sezione descrive come awk
definisce “vero” e “falso”
e come questi valori sono confrontati.
• Valori di verità: | Cos’è “vero” e cos’è “falso”. | |
• Tipi di variabile e confronti: | Come alle variabili si assegna il tipo e l’effetto che questo ha sul confronto di numeri e stringhe con ‘<’, etc. | |
• Operatori booleani: | Combinare espressioni di confronto usando operatori booleani ‘||’ (“or”), ‘&&’ (“and”) e ‘!’ (“not”). | |
• Espressioni condizionali: | Le espressioni condizionali scelgono una fra due sottoespressioni, a seconda del valore di una terza sottoespressione. |
Successivo: Tipi di variabile e confronti, Su: Valori e condizioni di verità [Contenuti][Indice]
awk
Molti linguaggi di programmazione hanno una particolare rappresentazione per i
concetti di “vero” e “falso.” Questi linguaggi usano normalmente le
costanti speciali true
e false
, o forse i loro equivalenti
maiuscoli.
Però awk
è differente.
Prende in prestito un concetto molto semplice di vero e falso dal linguaggio
C. In awk
, ogni valore numerico diverso da zero oppure
ogni valore di stringa non vuota è vero. Ogni altro valore (zero o la stringa
nulla, ""
) è falso. Il seguente programma stampa ‘Uno strano
valore di verità’ tre volte:
BEGIN { if (3.1415927) print "Uno strano valore di verità" if ("Ottanta e sette anni or sono") print "Uno strano valore di verità" if (j = 57) print "Uno strano valore di verità" }
C’è una conseguenza sorprendente della regola “non zero o non nullo”:
la costante di stringa "0"
sta effettivamente per vero, perché
è non nulla.
(a.b.)
Successivo: Operatori booleani, Precedente: Valori di verità, Su: Valori e condizioni di verità [Contenuti][Indice]
La Guida galattica è infallibile. È la realtà, spesso, a essere inesatta.
Diversamente che in altri linguaggi di programmazione, le variabili di
awk
non hanno un tipo fisso. Possono essere sia un numero che una
stringa, a seconda del valore loro assegnato.
Vediamo ora come viene assegnato il tipo a una variabile, e come awk
le confronta.
• Tipi di variabile: | Tipo stringa rispetto a tipo numero. | |
• Operatori di confronto: | Gli operatori di confronto. | |
• Confronto POSIX di stringhe: | Confronto tra stringhe usando le regole POSIX. |
Successivo: Operatori di confronto, Su: Tipi di variabile e confronti [Contenuti][Indice]
Per gli elementi scalari in awk
(variabili, elementi di
vettore e campi), il tipo degli stessi viene attribuito dinamicamente.
Ciò significa che il tipo di un elemento può cambiare nel corso
dell’esecuzione di un programma, da untyped (non ancora tipizzata),
valore assunto prima che la variabile sia utilizzata,33 a stringa oppure a numero, e in seguito
da stringa a numero o da numero a stringa, nel prosieguo del programma.
(gawk
prevede anche degli scalari di tipo regexp, ma
per ora possiamo ignorarli;
vedi Costanti regexp forti.)
Non si può fare molto riguardo alle variabili di tipo untyped,
oltre a constatare che ancora non è stato loro attribuito un tipo.
Il seguente programma confronta la variabile a
con i valori
""
e 0
; il confronto dà esito positivo se alla
variabile a
non è mai stato assegnato un valore.
L’esempio usa la funzione predefinita typeof()
(non ancora trattata; vedi Funzioni per i tipi) per
visualizzare il tipo della variabile a
:
$ gawk 'BEGIN { print (a == "" && a == 0 ? > "a non ha un tipo" : "a ha un tipo!") ; print typeof(a) }' -| a non ha un tipo -| unassigned
Una variabile scalare diviene di tipo numerico quando le viene assegnato un valore numerico, per mezzo di una costante numerica, o tramite un’altra variabile scalare di tipo numerico:
$ gawk 'BEGIN { a = 42 ; print typeof(a) > b = a ; print typeof(b) }' number number
Analogamente, una variabile scalare diviene di tipo stringa quando le viene assegnato come valore una stringa, per mezzo di una costante stringa, o tramite un’altra variabile scalare di tipo stringa:
$ gawk 'BEGIN { a = "quarantadue" ; print typeof(a) > b = a ; print typeof(b) }' string string
Fin qui, tutto semplice e chiaro. Cosa succede, però, quando
awk
deve trattare dati forniti dall’utente?
Il primo caso da considerare è quello dei campi di dati in input.
Quale dovrebbe essere l’output del seguente programma?
echo ciao | awk '{ printf("%s %s < 42\n", $1, ($1 < 42 ? "è" : "non è")) }'
Poiché ‘ciao’ è un dato di tipo alfabetico, awk
può solo
effettuare un confronto di tipo stringa. Internamente, il numero 42
viene convertito in "42"
e vengono confrontate le due stringhe di
valore "ciao"
e "42"
. Questo è il risultato:
$ echo ciao | awk '{ printf("%s %s < 42\n", $1, > ($1 < 42 ? "è" : "non è")) }' -| ciao non è < 42
Tuttavia, cosa succede quando un dato utente assomiglia a un
numero?
Da un lato, in realtà, il dato in input è formato da alcuni caratteri, non da
valori numerici in formato binario. Ma, d’altro lato, il dato sembra
numerico, e awk
dovrebbe davvero trattarlo come tale. E in effetti
questo è ciò che avviene:
$ echo 37 | awk '{ printf("%s %s < 42\n", $1, > ($1 < 42 ? "è" : "non è")) }' -| 37 is < 42
Queste sono le regole seguite per determinare quando awk
tratta dei dati in input come numeri, e quando li considera stringhe.
Lo standard POSIX usa il concetto di stringa numerica, per dei dati in input che appaiono essere numerici. Il ‘37’ nell’esempio precedente è una stringa numerica. Quindi, qual è il tipo di una stringa numerica? Risposta: numerico.
Il tipo di una variabile è importante perché il tipo di due variabili determina il modo con cui le stesse vengono confrontate. La determinazione del tipo di variabile segue queste regole:
getline
, FILENAME
, elementi di
ARGV
, elementi di ENVIRON
, e gli elementi di un vettore
creato da match()
, split()
e patsplit()
che sono
stringhe numeriche hanno l’attributo strnum.34
Altrimenti, hanno l’attributo string.
Anche le variabili non inizializzate hanno l’attributo strnum.
L’ultima regola è particolarmente importante. Nel seguente programma,
a
è di tipo numerico, anche se viene usata in un secondo momento in
un’operazione di stringa:
BEGIN { a = 12.345 b = a " è un numero carino" print b }
Quando si confrontano due operandi, può essere usata sia il confronto come stringa che il confronto numerico, a seconda degli attributi degli operandi, secondo questa matrice simmetrica:
+---------------------------------------------- | STRING NUMERIC STRNUM --------+---------------------------------------------- | STRING | string string string | NUMERIC | string numeric numeric | STRNUM | string numeric numeric --------+----------------------------------------------
L’idea di base è che l’input dell’utente che appare come numerico—e
solo l’input dell’utente—dovrebbe essere trattato come numerico, anche
se in realtà è un insieme di caratteri e quindi anche una stringa.
Così, ad esempio, la costante di stringa " +3.14"
,
quando appare nel codice sorgente di un programma,
è una stringa—anche se sembra numerica—e non
viene mai trattato come numero
ai fini di un confronto.
In breve, quando un operando è una stringa “pura”, come una costante di
stringa, viene effettuato un confronto di stringa. In caso contrario viene
effettuato un confronto numerico.
(La differenza principale tra un numero e uno strnum è che per gli
strnum gawk
conserva anche il valore originale della stringa
che la variabile scalare aveva al momento in cui è stata letta.
Questo punto merita di essere ulteriormente ribadito: l’input che appare essere un numero è numerico. Tutto il resto dell’input è considerato essere una stringa.
Così, la stringa in input di sei caratteri ‘ +3.14’
riceve l’attributo strnum. Al contrario, la stringa di sei caratteri
" +3.14"
che compaia nel testo di un programma rimane una
costante di stringa. I seguenti esempi stampano ‘1’ quando il confronto
fra due diverse costanti è vero, altrimenti stampano ‘0’:
$ echo ' +3.14' | awk '{ print($0 == " +3.14") }' Vero -| 1 $ echo ' +3.14' | awk '{ print($0 == "+3.14") }' Falso -| 0 $ echo ' +3.14' | awk '{ print($0 == "3.14") }' Falso -| 0 $ echo ' +3.14' | awk '{ print($0 == 3.14) }' Vero -| 1 $ echo ' +3.14' | awk '{ print($1 == " +3.14") }' Falso -| 0 $ echo ' +3.14' | awk '{ print($1 == "+3.14") }' Vero -| 1 $ echo ' +3.14' | awk '{ print($1 == "3.14") }' Falso -| 0 $ echo ' +3.14' | awk '{ print($1 == 3.14) }' Vero -| 1
Per controllare il tipo di un campo in input (o di altro input immesso
dall’utente, si può usare typeof()
:
$ echo salve 37 | gawk '{ print typeof($1), typeof($2) }' -| string strnum
Successivo: Confronto POSIX di stringhe, Precedente: Tipi di variabile, Su: Tipi di variabile e confronti [Contenuti][Indice]
Le espressioni di confronto confrontano stringhe o numeri per metterli in relazione tra di loro, come ad esempio nella relazione di uguaglianza. Sono scritte usando operatori di relazione, che sono un superinsieme di quelli in C. Sono descritti nella Tabella 6.3.
Espressione | Risultato |
---|---|
x < y | Vero se x è minore di y |
x <= y | Vero se x è minore o uguale a y |
x > y | Vero se x è maggiore di y |
x >= y | Vero se x è maggiore o uguale a y |
x == y | Vero se x è uguale a y |
x != y | Vero se x è diverso da y |
x ~ y | Vero se la stringa x corrisponde alla regexp rappresentata da y |
x !~ y | Vero se la stringa x non corrisponde alla regexp rappresentata da y |
indice in vettore | Vero se il vettore vettore ha un elemento con indice indice |
Tabella 6.3: Operatori di relazione
Le espressioni di confronto valgono uno se sono vere e zero se false.
Quando si confrontano operandi di tipi diversi, gli operandi numerici sono
convertiti in stringhe usando il valore di CONVFMT
(vedi Conversione).
Il confronto tra stringhe avviene
confrontando il primo carattere di ciascuna stringa, poi il secondo carattere,
e così via. Quindi, "10"
è minore di "9"
. Se vi sono due
stringhe di cui una è il prefisso dell’altra, la stringa più corta è minore di
quella più lunga. Così, "abc"
è minore di "abcd"
.
È molto facile sbagliarsi scrivendo l’operatore ‘==’ e
omettendo uno dei due caratteri ‘=’. Il risultato è sempre un codice
awk
valido, ma il programma non fa quel che si voleva:
if (a = b) # oops! dovrebbe essere == b … else …
A meno che b
non sia zero o la stringa nulla, la parte if
del test ha sempre successo. Poiché gli operatori sono molto simili,
questo tipo di errore è molto difficile da individuare
rileggendo il codice sorgente.
Il seguente elenco di espressioni illustra il tipo di confronti
che awk
effettua e il risultato di ciascun confronto:
1.5 <= 2.0
Confronto numerico (vero)
"abc" >= "xyz"
Confronto tra stringhe (falso)
1.5 != " +2"
Confronto tra stringhe (vero)
"1e2" < "3"
Confronto tra stringhe (vero)
a = 2; b = "2"
a == b
Confronto tra stringhe (vero)
a = 2; b = " +2"
a == b
Confronto tra stringhe (falso)
In quest’esempio:
$ echo 1e2 3 | awk '{ print ($1 < $2) ? "vero" : "falso" }' -| falso
il risultato è ‘falso’ perché sia $1
che $2
sono immessi dall’utente. Sono stringhe numeriche—quindi hanno entrambe
l’attributo strnum, che richiede un confronto di tipo numerico.
Lo scopo delle regole di confronto e dell’uso di stringhe numeriche è quello
di cercare di produrre il comportamento "meno inaspettato possibile",
pur "facendo la cosa giusta".
I confronti di stringhe e i confronti di espressioni regolari sono molto diversi. Per esempio:
x == "att"
ha il valore uno, ossia è vero, se la variabile x
è precisamente ‘att’. Al contrario:
x ~ /att/
ha il valore uno se x
contiene ‘att’, come
"Oh, che matto che sono!"
.
L’operando di destra degli operatori ‘~’ e ‘!~’ può essere sia una
costante regexp (/
…/
) che un’espressione ordinaria.
In quest’ultimo caso, il valore dell’espressione come stringa è usato come una
regexp dinamica (vedi Uso di regexp; e
vedi Espressioni regolari calcolate).
Un’espressione regolare costante tra due barre è di per sé anche
un’espressione. /regexp/
è un’abbreviazione per la seguente
espressione di confronto:
$0 ~ /regexp/
Una particolare posizione dove /pippo/
non è un’abbreviazione di
‘$0 ~ /pippo/’ è quella in cui è l’operando di destra di ‘~’ o
‘!~’.
Vedi Usare le costanti regexp,
dove questo punto è trattato in maggiore dettaglio.
Precedente: Operatori di confronto, Su: Tipi di variabile e confronti [Contenuti][Indice]
Lo standard POSIX diceva che il confronto di stringhe viene effettuato secondo l’ordine di collazione locale. Questo è l’ordine secondo il quale sono disposti i caratteri, come definito dalla localizzazione (per una trattazione più dettagliata, vedi Localizzazioni). Quest’ordine normalmente è molto diverso dal risultato ottenuto quando si esegue un confronto rigorosamente "carattere per carattere".35
Poiché questo comportamento differisce sensibilmente dalla pratica corrente,
gawk
lo implementava solo quando eseguito in modalità POSIX
(vedi Opzioni).
Quest’esempio ilustra la differenza, in una localizzazione
en_US.UTF-8
:
$ gawk 'BEGIN { printf("ABC < abc = %s\n", > ("ABC" < "abc" ? "TRUE" : "FALSE")) }' -| ABC < abc = TRUE $ gawk --posix 'BEGIN { printf("ABC < abc = %s\n", > ("ABC" < "abc" ? "TRUE" : "FALSE")) }' -| ABC < abc = FALSE
Fortunatamente, dal mese di agosto 2016, un confronto basato sull’ordine
di collazione locale non è più richiesto per gli operatori ==
e
!=
.36 Tuttavia, un confronto basato sull’ordine di
collazione locale è ancora richiesto per gli operatori <
,
<=
, >
e >=
. POSIX, quindi, raccomanda quanto
segue:
Poiché l’operatore
==
controlla che se le stringhe sono identiche, e non se sono nell’ordine di collazione locale, le applicazioni che devono controllare se le stringhe sono nell’ordine di collazione locale possono usare:a <= b && a >= b
A partire dalla versione 4.2, gawk
continua a usare
l’ordine di collazione locale per <
, <=
, >
e >=
solo se eseguito nella modalità POSIX.
Successivo: Espressioni condizionali, Precedente: Tipi di variabile e confronti, Su: Valori e condizioni di verità [Contenuti][Indice]
Un’espressione booleana è una combinazione di espressioni di confronto o espressioni di ricerca, che usa gli operatori booleani "or" (‘||’), "and" (‘&&’), e "not" (‘!’), facendo uso di parentesi per controllare le nidificazioni. Il valore di verità dell’espressione booleana è calcolato calcolando i valori di verità delle espressioni componenti. Le espressioni booleane sono conosciute anche come espressioni logiche. I due termini sono equivalenti.
Le espressioni booleane possono essere usate in tutti i casi in cui è possibile
usare espressioni di confronto e di ricerca di corrispondenze. Si possono
usare nelle istruzioni if
, while
, do
e for
(vedi Istruzioni).
Hanno valori numerici (uno se vero, zero se falso) che entrano in gioco
se il risultato di un’espressione booleana è memorizzato in una variabile o
se è usato nei calcoli.
Inoltre, ogni espressione booleana è anche un modello di ricerca valido, così se ne può usare uno come modello di ricerca per controllare l’esecuzione di regole. Gli operatori booleani sono:
booleano1 && booleano2
Vero se booleano1 e booleano2 sono entrambi veri. Per esempio, la seguente istruzione stampa il record in input corrente se contiene sia ‘edu’ che ‘li’:
if ($0 ~ /edu/ && $0 ~ /li/) print
La sottoespressione booleano2 viene valutata solo se booleano1
è vero. Questo può comportare una differenza laddove booleano2 contenga
espressioni che hanno effetti collaterali. Nel caso di ‘$0 ~ /pippo/ &&
($2 == pluto++)’, la variabile pluto
non viene incrementata se non c’è
nessuna sottostringa ‘pippo’ nel record.
booleano1 || booleano2
Vero se almeno uno tra booleano1 e booleano2 è vero. Per esempio, la seguente istruzione stampa tutti i record dell’input che contengono ‘edu’ oppure ‘li’:
if ($0 ~ /edu/ || $0 ~ /li/) print
La sottoespressione booleano2 viene valutata solo se booleano1 è falso. Questo può comportare una differenza quando booleano2 contiene espressioni che hanno effetti collaterali. (Perciò, questo confronto non individua mai i record che contengono sia ‘edu’ che ‘li’: non appena ‘edu’ viene trovato, l’intero confronto è concluso positivamente.)
! booleano
Vero se booleano è falso. Per esempio,
il seguente programma stampa ‘nessuna home!’ nel
caso insolito che la variabile d’ambiente HOME
non sia stata definita:
BEGIN { if (! ("HOME" in ENVIRON)) print "nessuna home!" }
(L’operatore in
è descritto in
Visitare elementi.)
Gli operatori ‘&&’ e ‘||’ sono chiamati operatori di cortocircuito per il modo in cui funzionano. La valutazione dell’intera espressione è "cortocircuitata" se il risultato può già essere determinato prima di aver completato interamente la valutazione.
Le istruzioni che finiscono con ‘&&’ o ‘||’ si possono continuare semplicemente mettendo un ritorno a capo dopo di esse. Però non si può mettere un ritorno a capo prima di questi operatori senza usare la continuazione tramite la barra inversa (vedi Istruzioni/Righe).
Il valore reale di un’espressione che usa l’operatore ‘!’ è uno o zero, a seconda del valore di verità dell’espressione a cui è applicato. L’operatore ‘!’ spesso è utile per cambiare il senso di una variabile indicatore [flag] da falso a vero e viceversa. Per esempio, il seguente programma è un modo per stampare righe poste tra due speciali righe delimitatrici:
$1 == "START" { pertinente = ! pertinente; next } pertinente { print } $1 == "END" { pertinente = ! pertinente; next }
La variabile pertinente
, così come tutte le variabili di awk
,
è inizializzata a zero, che vale anche "falso". Quando viene trovata
una riga il cui primo campo è ‘START’, il valore di pertinente
viene commutato a vero, usando ‘!’. La regola nella riga seguente stampa righe finché
pertinente
resta vero. Quando viene trovata una riga il cui primo
campo è ‘END’, pertinente
viene nuovamente commutata a
falso.37
Più comunemente, l’operatore ‘!’ viene usato nelle condizioni delle
istruzioni if
e while
, dove ha più senso formulare espressioni
logiche in negativo:
if (! qualche condizione || qualche altra condizione) { … fai un'operazione a piacere … }
NOTA: L’istruzione
next
viene trattata in Istruzione next.next
dice adawk
di tralasciare il resto delle regole, leggere il record successivo, e iniziare a elaborare le regole partendo nuovamente dalla prima. Il motivo è quello di evitare di stampare le righe delimitatrici ‘START’ e ‘END’.
Precedente: Operatori booleani, Su: Valori e condizioni di verità [Contenuti][Indice]
Un’espressione condizionale è un tipo particolare di espressione che ha
tre operandi. Consente di usare il primo valore dell’espressione per
scegliere una o l’altra delle due ulteriori espressioni.
L’espressione condizionale in awk
è la stessa di quella del
linguaggio C, ovvero:
selettore ? espr-se-vero : espr-se-falso
Ci sono tre sottoespressioni. La prima, selettore, viene sempre
calcolata per prima. Se è "vera" (non zero o non nulla), viene poi calcolata
espr-se-vero e il suo valore diventa il valore dell’intera espressione.
Altrimenti, la seconda espressione che viene calcolata è espr-se-falso
e il suo valore diventa il valore dell’intera espressione.
Per esempio, la seguente espressione produce il valore assoluto di x
:
x >= 0 ? x : -x
Ogni volta che viene calcolata un’espressione condizionale, solo una delle
espressioni espr-se-vero e espr-se-falso viene usata; l’altra
è ignorata. Questo è importante quando le espressioni hanno effetti
collaterali. Per esempio, quest’espressione condizionale esamina l’elemento
i
del vettore a
o del vettore b
, e incrementa i
:
x == y ? a[i++] : b[i++]
Questa istruzione garantisce che i
sia incrementato una volta sola
per ogni esecuzione dell’istruzione stessa, perché ogni volta viene eseguita
solo una delle due espressioni di incremento, mentre l’altra viene
ignorata.
Vedi Vettori,
per maggiori informazioni sui vettori.
Come estensione minore di gawk
,
un’istruzione che usa ‘?:’ si può continuare mettendo
semplicemente un ritorno a capo dopo i due caratteri.
Tuttavia, se si mette un ritorno a capo prima dei due
caratteri, la continuazione non funziona, se non si aggiunge anche la barra inversa
(vedi Istruzioni/Righe).
Se viene specificata l’opzione --posix
(vedi Opzioni), quest’estensione è disabilitata.
Successivo: Precedenza, Precedente: Valori e condizioni di verità, Su: Espressioni [Contenuti][Indice]
Una funzione è un nome per richiedere un particolare calcolo.
Il nome permette di richiamare
la funzione da qualsiasi punto del programma.
Per esempio, la funzione sqrt()
calcola la radice quadrata di un numero.
Un certo numero di funzioni sono predefinite, ossia sono
disponibili in ogni programma awk
. La funzione sqrt()
è una
di queste. Vedi Funzioni predefinite per un elenco di funzioni
predefinite e per le loro rispettive descrizioni. In aggiunta, l’utente può
definire delle funzioni da usare nel proprio programma.
Vedi Funzioni definite dall'utente
per istruzioni su come farlo.
Infine, gawk
permette di scrivere funzioni in C o in C++ che possono
essere chiamate dal proprio programma (vedi Estensioni dinamiche).
Una funzione viene utilizzata invocandola tramite un’espressione di chiamata di funzione, che consiste nel nome della funzione seguito immediatamente da una lista di argomenti tra parentesi. Gli argomenti sono espressioni che forniscono i materiali grezzi su cui opera la funzione. Quando ci sono più argomenti, questi sono separati da virgole. Se non ci sono argomenti, basta scrivere ‘()’ dopo il nome della funzione. Gli esempi che seguono mostrano chiamate di funzione con e senza argomenti:
sqrt(x^2 + y^2) un argomento atan2(y, x) due argomenti rand() nessun argomento
ATTENZIONE: Non ci dev’essere nessuno spazio tra il nome della funzione e la parentesi aperta! Un nome di funzione definita dall’utente può essere scambiata per il nome di una variabile: uno spazio renderebbe l’espressione simile alla concatenazione di una variabile con un’espressione racchiusa tra parentesi. Per le funzioni predefinite, lo spazio prima delle parentesi non crea problemi, ma è meglio non prendere l’abitudine di usare spazi per evitare errori con le funzioni definite dall’utente.
Ogni funzione richiede uno specifico numero di argomenti.
Per esempio, la funzione sqrt()
dev’essere chiamata con
un solo argomento, il numero del quale si vuole estrarre la radice quadrata:
sqrt(argomento)
Alcune delle funzioni predefinite hanno uno o più argomenti opzionali. Se questi argomenti non vengono forniti, le funzioni usano un valore di default appropriato. Vedi Funzioni predefinite per tutti i dettagli. Se sono omessi argomenti in chiamate a funzioni definite dall’utente, tali argomenti sono considerati essere variabili locali. Il valore di tali variabili locali è la stringa nulla se usate in un contesto che richiede una stringa di caratteri, e lo zero se è richiesto un valore numerico (vedi Funzioni definite dall'utente).
Come funzionalità avanzata, gawk
prevede la possibilità di
effettuare chiamate di funzione
indirette, il che permette di scegliere la funzione da chiamare al momento
dell’esecuzione, invece che nel momento in cui si scrive il codice sorgente
del programma.
Si rimanda la trattazione di questa funzionalità
a un secondo momento; si veda Chiamate indirette.
Come ogni altra espressione, la chiamata di funzione ha un valore, chiamato spesso valore di ritorno, che è calcolato dalla funzione in base agli argomenti dati. In quest’esempio, il valore di ritorno di ‘sqrt(argomento)’ è la radice quadrata di argomento. Il seguente programma legge numeri, un numero per riga, e stampa la radice quadrata di ciascuno:
$ awk '{ print "La radice quadrata di", $1, "è", sqrt($1) }' 1 -| La radice quadrata di 1 è 1 3 -| La radice quadrata di 3 è 1.73205 5 -| La radice quadrata di 5 è 2.23607 Ctrl-d
Una funzione può avere anche effetti collaterali, come assegnare
valori a certe variabili o effettuare operazioni di I/O.
Questo programma mostra come la funzione match()
(vedi Funzioni per stringhe)
cambia le variabili RSTART
e RLENGTH
:
{ if (match($1, $2)) print RSTART, RLENGTH else print "non uguali" }
Qui vediamo un’esecuzione di esempio:
$ awk -f matchit.awk aaccdd c+ -| 3 2 pippo pluto -| non uguali abcdefg e -| 5 1
Successivo: Localizzazioni, Precedente: Chiamate di funzione, Su: Espressioni [Contenuti][Indice]
La precedenza degli operatori determina come gli operatori vengono
raggruppati quando diversi operatori appaiono uno vicino all’altro in
un’espressione.
Per esempio, ‘*’ ha una precedenza più alta di ‘+’; così,
‘a + b * c’ moltiplica b
e c
, e poi aggiunge a
al
prodotto (ovvero esegue ‘a + (b * c)’).
La normale precedenza degli operatori può essere cambiata usando delle parentesi. Si possono vedere le regole di precedenza come un modo per indicare dove si sarebbero dovute mettere delle parentesi. Di fatto, è cosa saggia usare sempre le parentesi in presenza di una combinazione di operatori insolita, perché altre persone che leggono il programma potrebbero non ricordare quale sia la precedenza in quel particolare caso. Anche dei programmatori esperti a volte dimenticano le regole esatte, il che porta a commettere errori. Usare esplicitamente le parentesi previene qualunque errore di questo tipo.
Quando vengono usati insieme operatori con uguale precedenza, quello più a sinistra viene raggruppato per primo, ad eccezione degli operatori di assegnamento, condizionali e di elevamento a potenza, che vengono raggruppati nell’ordine opposto. Quindi, ‘a - b + c’ raggruppa come ‘(a - b) + c’ e ‘a = b = c’ raggruppa come ‘a = (b = c)’.
Normalmente la precedenza degli operatori unari di prefisso non ha importanza, perché c’è un solo modo di interpretarli: prima il più interno. Quindi, ‘$++i’ significa ‘$(++i)’ e ‘++$x’ significa ‘++($x)’. Tuttavia, quando un altro operatore segue l’operando, la precedenza degli operatori unari può avere importanza. ‘$x^2’ significa ‘($x)^2’, ma ‘-x^2’ significa ‘-(x^2)’, perché ‘-’ ha precedenza più bassa rispetto a ‘^’, mentre ‘$’ ha precedenza più alta. Inoltre, gli operatori non possono essere combinati in modo tale da violare le regole di precedenza; per esempio, ‘$$0++--’ non è un’espressione valida perché il primo ‘$’ ha precedenza più alta di ‘++’; per evitare il problema l’espressione può essere scritta come ‘$($0++)--’.
Questa lista illustra gli operatori di awk
, in ordine di precedenza
dalla più alta alla più bassa:
(
…)
Raggruppamento.
$
Riferimento a un campo.
++ --
Incremento, decremento.
^ **
Elevamento a potenza. Questi operatori sono raggruppati da destra verso sinistra.
+ - !
Più, meno, “not” logico, unari.
* / %
Moltiplicazione, divisione, resto di una divisione.
+ -
Addizione, sottrazione.
Non c’è un simbolo speciale per la concatenazione. Gli operandi sono semplicemente scritti uno accanto all’altro. (vedi Concatenazione).
< <= == != > >= >> | |&
Operatori relazionali e ridirezione. Gli operatori relazionali e le ridirezioni hanno lo stesso livello di precedenza. I caratteri come ‘>’ servono sia come operatori relazionali che come ridirezioni; la distinzione tra i due significati dipende dal contesto.
Si noti che gli operatori di ridirezione I/O nelle istruzioni print
e
printf
appartengono al livello dell’istruzione, non alle espressioni.
La ridirezione non produce un’espressione che potrebbe essere l’operando di un
altro operatore. Di conseguenza, non ha senso usare un operatore di
ridirezione vicino a un altro operatore con precedenza più bassa senza
parentesi. Tali combinazioni generano errori di sintassi
(p.es., ‘print pippo > a ? b : c’).
Il modo corretto di scrivere quest’istruzione è ‘print pippo > (a ? b : c)’.
~ !~
Corrispondenza, non corrispondenza.
in
Appartenenza a un vettore.
&&
“and” logico.
||
“or” logico.
?:
Operatore condizionale. Questo operatore raggruppa da destra verso sinistra.
= += -= *= /= %= ^= **=
Assegnamento. Questi operatori raggruppano da destra verso sinistra.
NOTA: Gli operatori ‘|&’, ‘**’ e ‘**=’ non sono definiti da POSIX. Per la massima portabilità, è meglio non usarli.
Successivo: Sommario delle espressioni, Precedente: Precedenza, Su: Espressioni [Contenuti][Indice]
I moderni sistemi prevedono la nozione di localizzazione: un modo per
informare il sistema sulla serie di caratteri e sulla lingua locali.
Lo standard ISO C definisce una localizzazione di default "C"
, che è
l’ambiente tipico a cui molti programmatori in C sono abituati.
Un tempo, le impostazioni della localizzazione avevano influenza sulla ricerca di corrispondenze tramite regexp, ma ora non è più così (vedi Intervalli e localizzazione).
La localizzazione può influire sulla separazione dei record. Per il caso
normale di ‘RS = "\n"’, la localizzazione è generalmente irrilevante.
Per altri separatori di record di un solo carattere, impostare
la variabile d’ambiente ‘LC_ALL=C’
garantisce una migliore efficienza nella lettura dei record. Altrimenti,
gawk
dovrebbe fare diverse chiamate di funzione, per ogni
carattere in input, per determinare la fine del record.
La localizzazione può influire sulla formattazione delle date e delle ore
(vedi Funzioni di tempo). Per esempio, un modo comune per abbreviare la
data 4 settembre 2015, negli Stati Uniti è “9/4/15.” In molti paesi
dell’Europa, invece, l’abbreviazione è "4.9.15". Quindi, la specifica di
formato
%x
in una localizzazione "US"
potrebbe produrre ‘9/4/15’,
mentre in una localizzazione "EUROPA"
, potrebbe produrre ‘4.9.15’.
Secondo POSIX, anche il confronto tra stringhe è influenzato dalla localizzazione (come nelle espressioni regolari). I dettagli sono descritti in Confronto POSIX di stringhe.
Infine, la localizzazione influenza il valore del separatore decimale
usato quando gawk
analizza i dati in input. Questo è stato
trattato nel dettaglio in Conversione.
Precedente: Localizzazioni, Su: Espressioni [Contenuti][Indice]
awk
fornisce tre tipi di costanti: numerica, di stringa e
di regexp. Le costanti numeriche in gawk
si possono
specificare nei sistemi ottale ed esadecimale (con base 8 e 16), e anche nel
sistema decimale (base 10). In alcuni contesti, una costante regexp
isolata come /pippo/
ha lo stesso significato di ‘$0 ~ /pippo/’.
awk
, mentre altre permettono il controllo del comportamento
di awk
.
awk
. I valori numerici sono convertiti
come se fossero formattati con sprintf()
usando il formato contenuto in
CONVFMT
. La localizzazione può influire sulle conversioni.
awk
ci sono gli operatori aritmetici di uso comune (addizione,
sottrazione, moltiplicazione, divisione, modulo), e il più e il meno unari.
Ci sono anche operatori di confronto, operatori booleani, una verifica
dell’esistenza di una chiave in
un vettore, e operatori per la ricerca di corrispondenze con espressioni
regolari. La concatenazione di stringhe è effettuata mettendo due espressioni
una vicino all’altra; non c’è nessun operatore esplicito.
L’operatore con tre operandi ‘?:’ fornisce una verifica “if-else”
all’interno delle espressioni.
awk
, un valore è considerato vero se è diverso da zero
oppure non nullo. Altrimenti, il valore è falso.
awk
fornisce funzioni predefinite e prevede quelle definite
dall’utente; questo è descritto in
Funzioni.
awk
sono compatibili con quelle del linguaggio C.
awk
, e occasionalmente sul formato dei dati letti in input.
Successivo: Vettori, Precedente: Espressioni, Su: Top [Contenuti][Indice]
Come già visto, ogni istruzione awk
consiste di un criterio di
ricerca [pattern] a cui è associata un’azione. Questo capitolo
descrive come specificare criteri e azioni, cosa è possibile
fare tramite le azioni e quali sono le variabili predefinite in
awk
.
Le regole criterio di ricerca–azione e le istruzioni che si possono
dare all’interno delle azioni formano il nucleo centrale dei programmi
scritti in awk
.
In un certo senso, tutto quanto si è visto finora costituisce le fondamenta
sulle quali sono costruiti i programmi. È giunto il momento di iniziare a
costruire qualcosa di utile.
• Panoramica sui criteri di ricerca: | Come scrivere un criterio di ricerca. | |
• Usare variabili di shell: | come usare variabili della shell in
awk .
| |
• Panoramica sulle azioni: | Come scrivere un’azione. | |
• Istruzioni: | Descrizione dettagliata delle varie istruzioni di controllo. | |
• Variabili predefinite: | Sommario delle variabili predefinite. | |
• Sommario criteri e azioni: | Sommario di criteri di ricerca e azioni. |
Successivo: Usare variabili di shell, Su: Criteri di ricerca e azioni [Contenuti][Indice]
• regexp come criteri di ricerca: | Usare espressioni regolari come criteri di ricerca. | |
• Espressioni come criteri di ricerca: | Qualsiasi espressione può servire da criterio di ricerca. | |
• Intervalli: | Coppie di espressioni regolari per delimitare una ricerca. | |
• BEGIN/END: | Specificare regole di inizio e fine programma. | |
• BEGINFILE/ENDFILE: | Due condizioni speciali per controlli avanzati. | |
• Vuoto: | Il criterio di ricerca vuoto, che corrisponde a ogni record. |
I criteri di ricerca in awk
controllano l’esecuzione di
azioni: un’azione viene eseguita
quando il criterio di ricerca associato ad essa è soddisfatto dal
record in input corrente.
La tabella seguente è un sommario dei tipi di criteri di ricerca in
awk
:
/espressione regolare/
Un’espressione regolare. È verificata quando il testo di un record in input corrisponde all’espressione regolare. (Vedi Espressioni regolari.)
espressione
Una singola espressione. È verificata quando il suo valore è diverso da zero (se di tipo numerico) o non nullo (se è una stringa). (Vedi Espressioni come criteri di ricerca.)
inizio_interv, fine_interv
Una coppia di criteri di ricerca separati da una virgola, che specificano un intervallo di record. L’intervallo comprende sia il record iniziale che corrisponde a inizio_interv sia il record finale che corrisponde a fine_interv. (Vedi Intervalli.)
BEGIN
END
Criteri di ricerca speciali che consentono azioni di inizializzazione o
di pulizia in un programma awk
.
(Vedi BEGIN/END.)
BEGINFILE
ENDFILE
Criteri di ricerca speciali che consentono azioni di inizializzazione o di pulizia da eseguire all’inizio o alla fine di ogni file in input. (Vedi BEGINFILE/ENDFILE.)
vuoto
Il criterio di ricerca vuoto corrisponde a ciascun record in input. (Vedi Vuoto.)
Successivo: Espressioni come criteri di ricerca, Su: Panoramica sui criteri di ricerca [Contenuti][Indice]
Le espressioni regolari sono uno dei primi tipi di criteri di ricerca presentati in questo Documento. Questo tipo di criterio di ricerca è semplicemente una costante regexp posta nella parte criterio di ricerca di una regola. Equivale a scrivere ‘$0 ~ /criterio di ricerca/’. Il criterio di ricerca è verificato quando il record in input corrisponde alla regexp. Per esempio:
/pippo|pluto|paperino/ { personaggi_Disney++ } END { print personaggi_Disney, "Personaggi Disney visti" }
Successivo: Intervalli, Precedente: regexp come criteri di ricerca, Su: Panoramica sui criteri di ricerca [Contenuti][Indice]
Qualsiasi espressione awk
può essere usata come un criterio di
ricerca awk
.
Il criterio di ricerca è verificato se il valore dell’espressione è diverso
da zero (se è un numero), o non nullo (se è una stringa).
L’espressione è ricalcolata ogni volta che la regola viene applicata a un
nuovo record in input. Se l’espressione usa campi come $1
, il suo
valore dipende direttamente dal contenuto del record in input appena letto;
altrimenti, dipende solo da quel che è accaduto fino a quel momento durante
l’esecuzione del programma awk
.
Le espressioni di confronto, che usano gli operatori di confronto descritti in
Tipi di variabile e confronti,
sono un tipo di criterio di ricerca usato frequentemente.
Anche le regexp, verificate o non verificate, sono tipi di espressioni molto frequenti.
L’operando a sinistra degli operatori ‘~’ e ‘!~’ è una stringa.
L’operando di destra è un’espressione regolare costante delimitata da
barre (/regexp/
) o qualsiasi espressione il cui valore come
stringa è usato come un’espressione regolare dinamica
(vedi Espressioni regolari calcolate).
L’esempio seguente stampa il secondo campo di ogni record in input
il cui primo campo sia esattamente ‘li’:
$ awk '$1 == "li" { print $2 }' mail-list
(Il programma non stampa alcun output, perché nessuna persona ha come nome esattamente ‘li’.) Si veda la differenza con il seguente confronto di espressione regolare, che individua invece qualsiasi record il cui primo campo contenga ‘li’:
$ awk '$1 ~ /li/ { print $2 }' mail-list -| 555-5553 -| 555-6699
Una costante regexp usata come criterio di ricerca è anche un
caso speciale di criterio di ricerca costituito da un’espressione.
All’espressione /li/
viene assegnato il valore uno se ‘li’
viene trovato nel record in input corrente. Quindi, come criterio di ricerca,
/li/
individua tutti i record che contengono la stringa ‘li’.
Anche le espressioni booleane sono frequentemente usate come criteri di ricerca. Se un criterio di ricerca individua o no un record in input dipende dalla verifica delle sottoespressioni da cui è composto. Per esempio, il seguente comando stampa tutti i record in mail-list che contengono sia ‘edu’ che ‘li’:
$ awk '/edu/ && /li/' mail-list -| Samuel 555-3430 samuel.lanceolis@shu.edu A
Il seguente comando stampa tutti i record in mail-list che contengono ‘edu’ oppure ‘li’ (o entrambi, naturalmente):
$ awk '/edu/ || /li/' mail-list -| Amelia 555-5553 amelia.zodiacusque@gmail.com F -| Broderick 555-0542 broderick.aliquotiens@yahoo.com R -| Fabius 555-1234 fabius.undevicesimus@ucb.edu F -| Julie 555-6699 julie.perscrutabor@skeeve.com F -| Samuel 555-3430 samuel.lanceolis@shu.edu A -| Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R
Il seguente comando stampa tutti i record in mail-list che non contengono la stringa ‘li’:
$ awk '! /li/' mail-list -| Anthony 555-3412 anthony.asserturo@hotmail.com A -| Becky 555-7685 becky.algebrarum@gmail.com A -| Bill 555-1675 bill.drowning@hotmail.com A -| Camilla 555-2912 camilla.infusarum@skynet.be R -| Fabius 555-1234 fabius.undevicesimus@ucb.edu F -| Martin 555-6480 martin.codicibus@hotmail.com A -| Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R
Le sottoespressioni di un’operatore booleano in un criterio di ricerca possono
essere espressioni regolari costanti, confronti, o qualsiasi altra espressione
di awk
. Gli intervalli di ricerca
non sono espressioni, e quindi non possono apparire all’interno di
criteri di ricerca booleani. Analogamente, i criteri di ricerca speciali
BEGIN
, END
, BEGINFILE
ed ENDFILE
,
che non corrispondono ad alcun record in input, non sono espressioni e non
possono essere usati all’interno di criteri di ricerca booleani.
L’ordine di precedenza dei differenti operatori che possono essere usati nei criteri di ricerca è descritto in Precedenza.
Successivo: BEGIN/END, Precedente: Espressioni come criteri di ricerca, Su: Panoramica sui criteri di ricerca [Contenuti][Indice]
Un intervallo di ricerca è composto da due criteri di ricerca separati da una virgola, nella forma ‘inizio_intervallo, fine_intervallo’. È usato per individuare una serie di record consecutivi nei file in input. Il primo criterio di ricerca, inizio_intervallo, controlla dove inizia la serie di record, mentre fine_intervallo controlla dove finisce la serie stessa. L’esempio seguente:
awk '$1 == "on", $1 == "off"' miofile
stampa ogni record in miofile incluso tra i due record che iniziano con ‘on’/‘off’, estremi compresi.
Un intervallo di ricerca inizia con la valutazione di inizio_intervallo per ogni record in input. Quando un record soddisfa la condizione inizio_intervallo, l’intervallo di ricerca è attivato e l’intervallo di ricerca include anche quel record. Finché l’intervallo di ricerca rimane attivo, automaticamente vengono trovate corrispondenze in ogni record in input letto. L’intervallo di ricerca verifica anche fine_intervallo per ogni record in input; quando la verifica ha successo, il criterio di ricerca viene disattivato a partire dal record seguente. Quindi il criterio di ricerca torna a controllare inizio_intervallo per ogni nuovo record.
Il record che segnala l’inizio dell’intervallo
di ricerca e quello che segnala la fine di quell’intervallo soddisfano
entrambi il criterio di ricerca. Se non si vuole agire su tali record
si possono scrivere istruzioni if
nella parte azione della regola
per distinguerli dai record che il programma è interessato a trattare.
È possibile che un criterio di ricerca sia attivato e disattivato dallo
stesso record. Se il record soddisfa entrambe le condizioni, l’azione è
eseguita solo su quel record.
Per esempio, si supponga che ci sia un testo tra due separatori identici
(p.es., il simbolo ‘%’), ognuno dei quali sia su una riga a parte, che
dovrebbero essere ignorati. Un primo tentativo potrebbe essere quello di
combinare un intervallo di ricerca che descrive il
testo delimitato con l’istruzione
next
(non ancora introdotta, vedi Istruzione next).
Con quest’istruzione awk
non effettua alcuna azione sul record
corrente e inizia di nuovo a elaborare il successivo record in input.
Un tale programma è simile a questo:
/^%$/,/^%$/ { next } { print }
Questo programma non funziona, perché l’intervallo di ricerca è sia attivato che disattivato dalla prima riga incontrata, quella costituita da un ‘%’. Per ottenere l’effetto desiderato, si scriva il programma nella maniera che segue, utilizzando un flag:
/^%$/ { ignora = ! ignora; next } ignora == 1 { next } # ignora righe quando `ignora' è impostato a 1
In un intervallo di ricerca, la virgola (‘,’) ha la precedenza più bassa tra tutti gli operatori (cioè, è l’ultima a essere valutata). Il programma seguente tenta di combinare un intervallo di ricerca con un altro controllo, più semplice:
echo Yes | awk '/1/,/2/ || /Yes/'
L’intenzione in questo programma è quello di esprimere le condizioni
‘(/1/,/2/) || /Yes/’.
Tuttavia, awk
lo interpreta come se fosse
‘/1/, (/2/ || /Yes/)’.
Questo comportamento non può essere cambiato o evitato; gli intervalli di
ricerca non si possono combinare con altri criteri di ricerca:
$ echo Yes | gawk '(/1/,/2/) || /Yes/' error→ gawk: riga com.:1: (/1/,/2/) || /Yes/ error→ gawk: riga com.:1: ^ syntax error
Come punto di secondaria importanza, nonostante sia stilisticamente poco elegante, lo standard POSIX consente di andare a capo dopo la virgola in un intervallo di ricerca. (a.b.)
Successivo: BEGINFILE/ENDFILE, Precedente: Intervalli, Su: Panoramica sui criteri di ricerca [Contenuti][Indice]
BEGIN
ed END
Tutti i criteri di ricerca fin qui descritti servono a individuare dei record
in input.
I criteri di ricerca speciali BEGIN
ed END
non hanno questo scopo.
Servono invece per effettuare azioni di inizializzazione o di pulizia nei
programmi awk
.
Le regole BEGIN
ed END
devono prevedere azioni; non c’è
un’azione di default per queste regole, perché non c’è un record corrente
quando sono invocate.
Le regole BEGIN
ed END
sono spesso chiamate
“blocchi BEGIN
ed END
” da programmatori che usano awk
da molto tempo.
• Usare BEGIN/END: | Come e perché usare regole BEGIN/END. | |
• I/O e BEGIN/END: | Problemi di I/O nelle regole BEGIN/END. |
Successivo: I/O e BEGIN/END, Su: BEGIN/END [Contenuti][Indice]
Una regola BEGIN
è eseguita solo una volta, prima che sia letto il
primo record in input. Analogamente, una regola END
è eseguita
solo una volta, dopo che tutto l’input è già stato letto. Per esempio:
$ awk ' > BEGIN { print "Analisi di \"li\"" } > /li/ { ++n } > END { print "\"li\" è presente in", n, "record." }' mail-list -| Analisi di "li" -| "li" è presente in 4 record.
Questo programma trova il numero di record nel file in input
mail-list che contengono la stringa ‘li’. La regola BEGIN
stampa un titolo per il rapporto. Non c’è bisogno di usare la regola
BEGIN
per inizializzare il contatore n
a zero, poiché
awk
lo fa
automaticamente (vedi Variabili).
La seconda regola incrementa la variabile n
ogni volta che si legge
un record che soddisfa il criterio di ricerca ‘li’. La regola END
stampa il valore di n
alla fine del programma.
I criteri di ricerca speciali BEGIN
ed END
non possono essere
usati negli intervalli,
o con operatori booleani (in effetti, non possono essere combinati con nessun
altro operatore).
Un programma awk
può avere molte regole BEGIN
e/o END
.
Queste sono eseguite nell’ordine in cui compaiono nel programma: tutte le
regole BEGIN
a inizio programma e tutte le regole END
a fine programma.
Le regole BEGIN
ed END
possono apparire in qualsiasi
posizione all’interno del programma.
Questa funzionalità è stata aggiunta nella versione 1987 di awk
ed
è inclusa nello standard POSIX.
La versione originale (1978) di awk
richiedeva che la regola
BEGIN
fosse posta all’inizio del programma, e la regola
END
alla fine del programma, e solo una regola per tipo era ammessa.
Ciò non è più obbligatorio, ma è una buona idea continuare a seguire questo
modello per migliorare l’organizzazione e la leggibilità del programma.
Regole multiple BEGIN
ed END
sono utili per scrivere funzioni
di libreria, poiché ogni file di libreria può avere la sua propria regola
BEGIN
e/o END
per fare la propria inizializzazione e/o pulizia.
L’ordine in cui le funzioni di libreria sono menzionate nella riga dei comandi
determina l’ordine in cui le rispettive regole BEGIN
ed END
sono
eseguite. Per questo motivi, occorre prestare attenzione nello scrivere tali
regole nei file di libreria, in modo che non sia importante
l’ordine in cui tali regole vengono eseguite.
Vedi Opzioni per maggiori informazioni sull’uso di funzioni di libreria.
Vedi Funzioni di libreria,
per parecchie utili funzioni di libreria.
Se un programma awk
ha solo regole BEGIN
e nessun’altra
regola, il programma esce dopo aver eseguito le regole
BEGIN
.38 Tuttavia, se
una regola END
esiste, l’intero input è letto, anche se non sono
presenti altre regole nel programma. Ciò viene fatto necessariamente
per permettere che
la regola END
faccia uso delle variabili FNR
e NR
.
Precedente: Usare BEGIN/END, Su: BEGIN/END [Contenuti][Indice]
BEGIN
ed END
Ci sono parecchi punti (talora insidiosi) da tener presente se si fa dell’I/O
all’interno di una regola BEGIN
o END
.
Il primo ha a che fare con il valore di $0
in una regola BEGIN
.
Poiché le regole BEGIN
sono eseguite prima delle lettura di qualsiasi
input, non c’è assolutamente alcun record in input, e quindi nessun campo,
quando si eseguono delle regole BEGIN
. I riferimento a $0
e ai
campi restituiscono una stringa nulla o zero, a seconda del contesto.
Un modo per assegnare un valore effettivo a $0
è di eseguire un
comando getline
senza indicare una variabile (vedi Getline).
Un altro modo è semplicemente quello di assegnare un valore a $0
.
Il secondo punto è simile al primo, ma in direzione opposta.
Tradizionalmente, più che altro per problemi di implementazione, $0
e NF
erano indefiniti all’interno di una regola END
.
Lo standard POSIX prescrive che NF
sia disponibile all’interno di una
regola END
. Contiene il numero di campi dell’ultimo record in input.
Probabilmente per una svista, lo standard non specifica che è reso
disponibile anche $0
, sebbene possa apparire logico che sia così.
In effetti, BWK awk
, mawk
e gawk
mantengono il
valore di $0
in modo che sia possibile usarlo all’interno delle regole
END
. Occorre peraltro tener presente che alcune altre implementazioni
e parecchie tra le versioni più vecchie di Unix awk
non si
comportano così.
Il terzo punto è una conseguenza dei primi due. Il significato di
‘print’
all’interno di una regola BEGIN
o END
è quello di sempre:
‘print $0’. Se $0
è la stringa nulla, stampa una riga vuota.
Molti programmatori di lungo corso di awk
usano un semplice
‘print’ all’interno delle regole BEGIN
ed END
,
intendendo ‘print ""’, contando sul fatto che $0
sia una
stringa nulla. Sebbene questo funzioni solitamente con le regole
BEGIN
, è una pessima idea nelle regole END
,
almeno in gawk
. È anche stilisticamente inelegante, perché se
serve una riga vuota in output, il programma dovrebbe stamparne
una esplicitamente.
Per finire, le istruzioni next
e nextfile
non sono consentite
all’interno di una regola BEGIN
, perché il ciclo implicito
leggi-un-record-e-confrontalo-con-le-regole non è ancora iniziato.
Analogamente, tali istruzioni non sono valide all’interno di una regola
END
, perché tutto l’input è già stato letto.
(Vedi Istruzione next e
vedi Istruzione nextfile.)
Successivo: Vuoto, Precedente: BEGIN/END, Su: Panoramica sui criteri di ricerca [Contenuti][Indice]
BEGINFILE
ed ENDFILE
Questa sezione descrive una funzionalità specifica di gawk
.
Due tipi speciali di criterio di ricerca, BEGINFILE
ed ENDFILE
,
forniscono
degli “agganci” per intervenire durante il ciclo di elaborazione dei file
specificati sulla riga di comando di gawk
.
Come con le regole BEGIN
ed END
(vedi BEGIN/END),
tutte le regole BEGINFILE
in un programma sono riunite, mantenendole
nell’ordine in cui sono lette da gawk
e lo stesso viene fatto
per tutte le regole ENDFILE
.
Il corpo delle regole BEGINFILE
è eseguito subito prima che
gawk
legga il primo record da un file. La variabile FILENAME
è impostata al nome del file corrente e FNR
è impostata a zero.
La regola BEGINFILE
dà la possibilità di eseguire due compiti
che sarebbe difficile o impossibile effettuare altrimenti:
gawk
viene terminato. Comunque, questo si può evitare, per poi
passare a elaborare il file successivo specificato sulla riga dei comandi.
Questo controllo è fattibile controllando se la variabile ERRNO
è
diversa dalla stringa nulla; se è questo il caso, gawk
non è
riuscito ad aprire il file. In questo caso il programma può eseguire
un’istruzione nextfile
(vedi Istruzione nextfile). In questo modo gawk
salta
completamente l’elaborazione di quel file.
In caso contrario, gawk
termina come al solito con un
errore fatale.
gawk
inizi a elaborare il file.
(Questa è una funzionalità molto avanzata, usata al momento solo dal
progetto gawkextlib
.)
La regola ENDFILE
è chiamata quando gawk
ha finito di
elaborare l’ultimo record di un file in input. Per l’ultimo file in input,
è chiamata prima di ogni regola END
.
La regola ENDFILE
è eseguita anche per file in input vuoti.
Normalmente, se si verifica un errore di lettura durante il normale
ciclo di elaborazione dell’input,
questo è considerato fatale (il programma termina). Tuttavia, se è presente
una regola ENDFILE
, l’errore non è considerato fatale, ma viene
impostato ERRNO
. Ciò permette di intercettare ed elaborare errori
di I/O a livello di programma awk
.
L’istruzione next
(vedi Istruzione next) non è permessa all’interno
di una regola BEGINFILE
o ENDFILE
. L’istruzione nextfile
è consentita solo all’interno di una regola BEGINFILE
, non all’interno
di una regola ENDFILE
.
L’istruzione getline
(vedi Getline) è limitata all’interno sia di
BEGINFILE
che di ENDFILE
: solo le forme ridirette di
di getline
sono permesse.
BEGINFILE
ed ENDFILE
sono estensioni gawk
.
In molte altre implementazioni di awk
o se gawk
è in
modalità compatibile (vedi Opzioni), non sono regole speciali.
Precedente: BEGINFILE/ENDFILE, Su: Panoramica sui criteri di ricerca [Contenuti][Indice]
Un criterio di ricerca vuoto (cioè omesso) corrisponde a ogni record in input. Per esempio, il programma:
awk '{ print $1 }' mail-list
stampa il primo campo di ogni record.
Successivo: Panoramica sulle azioni, Precedente: Panoramica sui criteri di ricerca, Su: Criteri di ricerca e azioni [Contenuti][Indice]
I programmi awk
sono spesso usati come componenti di programmi più
ampi, scritti in un linguaggio di shell.
Per esempio, è molto comune usare una variabile di shell per
specificare un criterio di ricerca che il programma awk
deve poi
individuare.
Ci sono due modi per rendere disponibile il valore di una variabile di shell
all’interno di un programma awk
.
Un modo comune è quello di usare i doppi apici per sostituire il valore della
variabile nel progamma awk
contenuto nello script:
Per esempio, si consideri il programma seguente:
printf "Immettere il criterio di ricerca: " read criterio_di_ricerca awk "/$criterio_di_ricerca/ "'{ num_trov++ } END { print num_trov, "occorrenze trovate" }' /nome/file/dati
Il programma awk
consiste di due pezzi di testo tra apici
che, concatenati insieme, formano il programma.
La prima parte è tra doppi apici, per consentire la sostituzione della
variabile di shell criterio_di_ricerca
contenuta al loro interno.
La seconda parte è racchiusa tra apici singoli.
La sostituzione di variabile attraverso gli apici funziona, ma può facilmente generare difficoltà. Richiede una buona comprensione delle regole per l’uso degli apici nella shell (vedi Protezione), e spesso è difficile accoppiare i vari apici quando si legge il programma.
Un metodo migliore è quello di usare la funzionalità di assegnamento delle
variabili di awk
(vedi Opzioni di assegnamento)
per assegnare il valore di una variabile di shell a una variabile
di awk
.
Poi si possono usare regexp dinamiche come criterio di ricerca
(vedi Espressioni regolari calcolate).
Quanto segue mostra come sarebbe l’esempio precedente usando questa tecnica:
printf "Immettere il criterio di ricerca: " read criterio_di_ricerca awk -v crit="$criterio_di_ricerca" '$0 ~ crit { num_trov++ } END { print num_trov, "occorrenze trovate" }' /nome/file/dati
Adesso il programma awk
è solo una stringa tra apici semplici.
L’assegnamento ‘-v crit="$criterio_di_ricerca"’ richiede ancora doppi
apici, per il caso in cui uno spazio vuoto sia presente nel valore di
$criterio_di_ricerca
.
La variabile awk
crit
potrebbe avere come nome anche
criterio_di_ricerca
, ma ciò potrebbe essere causa di confusione.
Usare una variabile permette una maggiore flessibilità, poiché la variabile
può essere usata in ogni parte del
programma—per stamparla, per indicizzare un vettore, o per qualsiasi altro
scopo—senza che sia necessario l’artificio di doverla inserire usando gli
apici.
Successivo: Istruzioni, Precedente: Usare variabili di shell, Su: Criteri di ricerca e azioni [Contenuti][Indice]
Un programma o script awk
consiste in una serie di
regole e definizioni di funzione frammiste tra loro. (Le funzioni sono
descritte più avanti. Vedi Funzioni definite dall'utente.)
Una regola contiene un criterio di ricerca e un’azione; l’uno o l’altra
(ma non tutt’e due) possono essere omessi. Lo scopo di una azione è
di specificare cosa deve fare awk
quando si trova una corrispondenza
con il criterio di ricerca. Quindi, schematicamente, un programma
awk
è normalmente simile a questo:
[criterio di ricerca]{ azione }
criterio di ricerca [{ azione }
] …function nome(argomenti) { … }
…
Un’azione consiste di una o più istruzioni awk
, racchiuse
fra parentesi graffe (‘{…}’). Ogni istruzione specifica
una cosa da fare. Le istruzioni sono separate tra loro da dei ritorni a capo o
da dei punti e virgola.
Le parentesi graffe attorno a un’azione vanno usate anche se l’azione
contiene una sola istruzione o se non contiene alcuna istruzione.
Comunque, se si omette completamente l’azione, si possono omettere anche le
parentesi graffe. Un’azione omessa è equivalente a specificare
‘{ print $0 }’:
/pippo/ { } se si trovapippo
, non fare nulla — azione vuota /pippo/ se si trovapippo
, stampa il record — azione omessa
I seguenti tipi di istruzione sono disponibili in awk
:
Servono per chiamare funzioni o assegnare valori a variabili (vedi Espressioni). L’esecuzione di questo tipo di istruzione calcola semplicemente il valore dell’espressione. Ciò è utile quando l’espressione ha effetti collaterali (vedi Operatori di assegnamento).
Specificano il flusso di controllo dei programmi awk
.
Il linguaggio awk
utilizza dei costrutti simili a quelli del C,
(if
, for
, while
e do
), e anche alcune altre
di tipo speciale (vedi Istruzioni).
Sono una o più istruzioni racchiuse tra parentesi graffe. Un’istruzione
composta
è usata per riunire un gruppo di istruzioni all’interno di
un’istruzione if
, while
, do
o for
.
Usano il comando getline
(vedi Getline).
In awk
sono anche disponibili le istruzioni next
(vedi Istruzione next)
e nextfile
(vedi Istruzione nextfile).
Come print
e printf
.
Vedi Stampare.
Per eliminare elementi di vettori. Vedi Cancellazione.
Successivo: Variabili predefinite, Precedente: Panoramica sulle azioni, Su: Criteri di ricerca e azioni [Contenuti][Indice]
Le istruzioni di controllo, come if
, while
e così via,
regolano il flusso di esecuzione nei programmi awk
. Molte tra
le istruzioni di controllo di awk
sono modellate sulle
corrispondenti istruzioni in C.
Tutte le istruzioni di controllo iniziano con parole chiave speciali, come
if
e while
, per distinguerle dalle semplici espressioni.
Molte istruzioni di controllo contengono altre istruzioni. Per esempio,
l’istruzione if
contiene un’altra istruzione che può essere eseguita
oppure no. Le istruzioni contenute sono chiamate corpo.
Per includere più di un’istruzione nel corpo, queste vanno raggruppate
in una sola istruzione composta tra parentesi graffe, e separate tra
loro con dei ritorni a capo o dei punti e virgola.
• Istruzione if: | Eseguire in maniera condizionale
istruzioni awk .
| |
• Istruzione while: | Eseguire il ciclo finché è verificata una condizione. | |
• Istruzione do: | Eseguire l’azione specificata, continuare a eseguire il ciclo finché è verificata una condizione. | |
• Istruzione for: | Un’altra istruzione iterativa, che permette di specificare clausole iniziali e di incremento. | |
• Istruzione switch : | Valutazione di quale insieme di istruzioni eseguire, a seconda del valore assunto da una variabile. | |
• Istruzione break: | Uscire subito dal ciclo più interno in cui ci si trova. | |
• Istruzione continue: | Andare alla fine del ciclo più interno in cui ci si trova. | |
• Istruzione next: | Smettere di elaborare il record corrente. | |
• Istruzione nextfile: | Smettere di elaborare il file corrente. | |
• Istruzione exit: | Interrompere l’esecuzione di awk .
|
Successivo: Istruzione while, Su: Istruzioni [Contenuti][Indice]
if
-else
L’istruzione if
-else
è quella che serve in awk
per prendere decisioni. È simile
a questa:
if (condizione) se-vera-fai
[else se-falsa-fai
]
La condizione è un’espressione che controlla quel che fa il resto
dell’istruzione. Se la condizione è vera, viene eseguita la
parte se-vera-fai; altrimenti viene
eseguita la parte se-falsa-fai.
La parte else
dell’istruzione è
facoltativa. La condizione è considerata falsa se il suo valore è zero o
la stringa nulla; altrimenti, la condizione è vera.
Si consideri quanto segue:
if (x % 2 == 0) print "x è pari" else print "x è dispari"
In questo esempio, se l’espressione ‘x % 2 == 0’ è vera (cioè,
se il valore di x
è esattamente divisibile per due), allora viene
eseguita la prima istruzione
print
; altrimenti, viene eseguita la seconda istruzione print
.
Se la parola chiave else
sta sulla stessa riga di se-vera-fai
e se se-vera-fai non è un’istruzione composta (cioè, non è racchiusa
tra parentesi graffe), allora un punto e virgola deve separare
se-vera-fai dalla parola chiave else
.
Per chiarire questo, l’esempio precedente si può riscrivere come:
if (x % 2 == 0) print "x è pari"; else print "x è dispari"
Se il ‘;’ è omesso, awk
non può interpretare l’istruzione e
segnala un errore di sintassi. Non si dovrebbero scrivere programmi in
questo modo, perché a chi li legge potrebbe sfuggire la parola chiave
else
se non è la prima parola della riga.
Successivo: Istruzione do, Precedente: Istruzione if, Su: Istruzioni [Contenuti][Indice]
while
Nella programmazione, un ciclo è una parte di un programma che può
essere eseguita due o più volte consecutivamente.
L’istruzione while
è la più semplice istruzione iterativa in
awk
. Esegue ripetutamente un’istruzione finché una data
condizione è vera. Per esempio:
while (condizione) corpo-del-ciclo
corpo-del-ciclo è un’istruzione detta corpo del ciclo,
e condizione è un’espressione che controlla per quante volte il ciclo
deve continuare a essere ripetuto.
La prima cosa che l’istruzione while
fa è un controllo della
condizione.
Se la condizione è vera, viene eseguita l’istruzione
corpo-del-ciclo.
Dopo che le istruzioni in corpo-del-ciclo sono state eseguite,
condizione è controllata nuovamente, e se è ancora vera,
corpo-del-ciclo viene eseguito ancora. Questo processo è ripetuto
finché condizione rimane vera. Se la condizione è falsa fin
dall’inizio, il corpo del ciclo
non viene eseguito per nulla, e awk
continua con l’istruzione
che viene dopo il ciclo.
Questo esempio stampa i primi tre campi di ogni record in input, uno per
riga:
awk ' { i = 1 while (i <= 3) { print $i i++ } }' inventory-shipped
Il corpo di questo ciclo è un’istruzione composta racchiusa tra parentesi graffe,
che contiene due istruzioni.
Il ciclo funziona in questo modo: all’inizio, il valore di i
è
impostato a 1.
Poi, l’istruzione while
controlla se i
è minore o uguale a
tre. Ciò è vero quando i
è uguale a 1, quindi il campo
i
-esimo viene stampato. Quindi l’istruzione ‘i++’ incrementa il
valore di i
e il ciclo viene ripetuto. Il ciclo termina quando i
assume il
valore quattro.
Un ritorno a capo non è richiesto tra la condizione e il corpo del ciclo; tuttavia, se lo si mette, il programma è di più facile comprensione, a meno che il corpo del ciclo non sia un’istruzione composta, oppure se è qualcosa di molto semplice. Neppure il ritorno a capo dopo la parentesi graffa aperta che inizia l’istruzione composta è necessario, ma il programma è di lettura più difficile se lo si omette.
Successivo: Istruzione for, Precedente: Istruzione while, Su: Istruzioni [Contenuti][Indice]
do
-while
Il ciclo do
è una variazione dell’istruzione di ciclo while
.
Il ciclo do
esegue il corpo-del-ciclo una volta e poi ripete il
corpo-del-ciclo finché la condizione rimane vera. È simile a questo:
do corpo-del-ciclo while (condizione)
Anche se la condizione è falsa fin dall’inizio, il corpo-del-ciclo
viene eseguito almeno una volta (e solo una volta, a meno che
l’esecuzione di corpo-del-ciclo
non renda vera la condizione). Si confronti con il corrispondente
while
:
while (condizione) corpo-del-ciclo
Quest’istruzione non esegue il corpo-del-ciclo neppure una volta, se
la condizione è falsa fin dall’inizio. Il seguente è un esempio di
do
:
{ i = 1 do { print $0 i++ } while (i <= 10) }
Questo programma stampa ogni record in input per 10 volte. Non si tratta,
peraltro, di un esempio molto realistico, perché in questo caso un semplice
while
sarebbe sufficiente. Questa osservazione riflette un’esperienza
reale; solo occasionalmente è davvero necessario usare un do
.
Successivo: Istruzione switch, Precedente: Istruzione do, Su: Istruzioni [Contenuti][Indice]
for
L’istruzione for
rende più agevole contare le iterazioni di un ciclo.
La forma generale dell’istruzione for
è simile a questa:
for (inizializzazione; condizione; incremento) corpo-del-ciclo
La parti inizializzazione, condizione e incremento sono
espressioni awk
a piacere, e corpo-del-ciclo indica qualsiasi
istruzione awk
.
L’istruzione for
comincia con l’esecuzione di inizializzazione.
Poi, finché
la condizione è vera, esegue ripetutamente corpo-del-ciclo,
e quindi incremento. Tipicamente, inizializzazione imposta una
variabile a zero o a uno, incremento aggiunge uno alla stessa e
condizione ne confronta il valore rispetto al numero desiderato di
iterazioni.
Per esempio:
awk ' { for (i = 1; i <= 3; i++) print $i }' inventory-shipped
Questo programma stampa i primi tre campi di ogni record in input, mettendo un campo su ogni riga.
Non è possibile impostare
più di una variabile nella parte di
inizializzazione senza usare un’istruzione di assegnamento multiplo,
come ‘x = y = 0’. Ciò ha senso solo se tutti i valori iniziali
sono uguali. (Ma è possibile inizializzare ulteriori variabili scrivendo
i relativi assegnamenti come istruzioni separate prima del ciclo
for
.)
Lo stesso vale per la parte incremento. Se serve incrementare ulteriori
variabili, questo va fatto con istruzioni separate alla fine del ciclo.
L’espressione composta del linguaggio C, che usa l’operatore virgola [,]
del C, sarebbe
utile in questo contesto, ma non è
prevista in awk
.
Molto spesso, incremento è un’espressione di incremento, come nell’esempio precedente. Ma questo non è obbligatorio; può trattarsi di un’espressione qualsiasi. Per esempio, la seguente istruzione stampa tutte le potenze di due comprese tra 1 e 100:
for (i = 1; i <= 100; i *= 2) print i
Se non è necessaria, ognuna delle tre espressioni fra
parentesi che segue la parola chiave for
può essere omessa. Quindi,
‘for (; x > 0;)’ è equivalente a ‘while (x > 0)’. Se la
condizione è omessa del tutto, è ritenuta sempre vera, producendo
un ciclo infinito (cioè, un ciclo che non finisce mai).
Molto spesso, un ciclo for
è un’abbreviazione per un ciclo while
,
come si può vedere qui:
inizializzazione while (condizione) { corpo-del-ciclo incremento }
La sola eccezione è quando l’istruzione continue
(vedi Istruzione continue) è usata
all’interno del ciclo. Se si modifica un’istruzione for
sostituendola con un’istruzione while
ciò può cambiare l’effetto dell’istruzione continue
posta all’interno
del ciclo.
Il linguaggio awk
ha un’istruzione for
oltre all’istruzione
while
perché un ciclo for
è spesso più semplice da scrivere,
e viene in mente più naturalmente.
Contare il numero di iterazioni è
molto frequente nei cicli. Può essere più facile pensare a questo conto come
parte del ciclo, piuttosto che come qualcosa da fare all’interno del ciclo.
Esiste una versione alternativa al ciclo for
, per esaminare tutti
gli indici di un vettore:
for (i in vettore) fai qualcosa con vettore[i]
Vedi Visitare un intero vettore
per maggiori informazioni su questa versione del ciclo for
.
Successivo: Istruzione break, Precedente: Istruzione for, Su: Istruzioni [Contenuti][Indice]
switch
Questa sezione descrive una funzionalità disponibile solo in
gawk
.
Se gawk
è in modalità compatibile (vedi Opzioni),
la funzionalità non è disponibile.
L’istruzione switch
consente di valutare un’espressione e di
eseguire istruzioni se il valore trovato corrisponde a uno dei case
[casi] previsti.
Le istruzioni case
sono esaminate per cercare una corrispondenza
nell’ordine in cui i casi sono definiti nel programma. Se nessuno dei case
corrisponde al valore dell’espressione, viene eseguita la sezione
default
, se è stata specificata.
Ogni case
contiene una singola costante, che può essere un numero,
una stringa, o
una regexp. Viene valutata l’espressione switch
, e poi la
costante di ogni case
viene confrontata
di volta in volta con il valore risultante.
Il tipo di costante determina quale sarà il confronto: per i tipi numerici o
stringa si seguono le regole abituali. Per una costante regexp viene
effettuato un confronto tra l’espressione e il valore di tipo stringa
dell’espressione originale.
Il formato generale dell’istruzione switch
è simile a questo:
switch (espressione) { case valore o espressione regolare: corpo-del-caso default: corpo-del-default }
Il flusso di controllo
dell’istruzione switch
funziona come per il linguaggio C. Una volta
stabilita una corrispondenza con un dato caso, le istruzione che formano il
corpo del caso sono eseguite, fino a che non venga trovata un’istruzione
break
,
continue
, next
, nextfile
o exit
,
o fino alla fine dell’istruzione switch
medesima. Per esempio:
while ((c = getopt(ARGC, ARGV, "aksx")) != -1) { switch (c) { case "a": # stampa la dimensione di tutti i file all_files = TRUE; break case "k": BLOCK_SIZE = 1024 # in blocchi da 1 Kbyte break case "s": # fa solo le somme sum_only = TRUE break case "x": # non esce dal filesystem fts_flags = or(fts_flags, FTS_XDEV) break case "?": default: uso() break } }
Si noti che se nessuna delle istruzioni specificate qui arresta l’esecuzione
di un’istruzione case
per la quale è stata trovata una corrispondenza;
l’esecuzione continua fino al successivo case
finché
non viene interrotta. In questo esempio, il
case
per "?"
esegue quello di default
, che consiste nel
chiamare una funzione di nome uso()
.
(La funzione getopt()
qui chiamata è descritta in
Funzione getopt.)
Successivo: Istruzione continue, Precedente: Istruzione switch, Su: Istruzioni [Contenuti][Indice]
break
L’istruzione break
esce dal ciclo più interno for
,
while
o do
dentro al quale si trova. L’esempio seguente
trova, se esiste, il divisore più piccolo di un dato numero intero, oppure
dichiara che si tratta di un numero primo:
# trova il divisore più piccolo di num { num = $1 for (divisore = 2; divisore * divisore <= num; divisore++) { if (num % divisore == 0) break } if (num % divisore == 0) printf "Il più piccolo divisore di %d è %d\n", num, divisore else printf "%d è un numero primo\n", num }
Quando il resto della divisione è zero nella prima istruzione if
,
awk
immediatamente esce, a causa del break, dal ciclo
for
in cui è contenuto. Ciò vuol dire
che awk
prosegue immediatamente fino all’istruzione che viene dopo
il ciclo, e continua l’elaborazione. (L’istruzione break
è molto
differente dall’istruzione exit
,
la quale termina l’intero programma awk
.
Vedi Istruzione exit.)
Il seguente programma mostra come la condizione di un’istruzione
for
o while
potrebbe essere sostituita da un’istruzione
break
all’interno di un if
:
# trova il divisore più piccolo di num { num = $1 for (divisore = 2; ; divisore++) { if (num % divisore == 0) { printf "Il più piccolo divisore di %d è %d\n", num, divisore break } if (divisore * divisore > num) { printf "%d è un numero primo\n", num break } } }
L’istruzione break
è usata anche per terminare l’esecuzione di
un’istruzione switch
.
Questo argomento è trattato in Istruzione switch.
L’istruzione break
non ha significato se
usata fuori dal corpo di un ciclo o di un’istruzione switch
.
Tuttavia, anche se la cosa non è mai stata documentata,
le prime implementazioni di awk
consideravano l’istruzione break
esterna a un ciclo come un’istruzione next
(vedi Istruzione next).
(a.b.)
Versioni recenti di BWK awk
non consentono più un tale uso,
e lo stesso fa gawk
.
Successivo: Istruzione next, Precedente: Istruzione break, Su: Istruzioni [Contenuti][Indice]
continue
Analogamente a break
, l’istruzione continue
è usata solo
all’interno di cicli for
, while
e do
. L’istruzione
ignora il resto del corpo del ciclo, facendo sì che la successiva iterazione
del ciclo inizi immediatamente. Questo comportamento è differente da quello
di break
, che esce completamente dal ciclo.
L’istruzione continue
in un ciclo for
fa sì che awk
ignori il resto del corpo del ciclo e prosegua l’esecuzione con l’espressione
incremento dell’istruzione for
. Il seguente programma
è un esempio di ciò:
BEGIN { for (x = 0; x <= 20; x++) { if (x == 5) continue printf "%d ", x } print "" }
Questo programma stampa tutti i numeri da 0 a 20—tranne il 5, in cui
l’istruzione printf
è saltata. Siccome l’incremento ‘x++’
non viene saltato, x
non rimane fermo al valore 5. Si confronti il ciclo
for
dell’esempio precedente con il seguente ciclo while
:
BEGIN { x = 0 while (x <= 20) { if (x == 5) continue printf "%d ", x x++ } print "" }
Questo programma inizia un ciclo infinito dopo che x
ha assunto il
valore 5, poiché l’istruzione di incremento (‘x++’) non è mai raggiunta.
L’istruzione continue
non ha un significato speciale se appare in
un’istruzione switch
, e non ha alcun significato se usata fuori dal
corpo di un ciclo. Le prime versioni di awk
trattavano le
istruzioni continue
che erano fuori da un ciclo allo stesso modo con
cui trattavano l’istruzione break
fuori da un ciclo: come se fosse un’istruzione next
(vedi Istruzione next).
(a.b.)
Versioni recenti di BWK awk
non consentono più un tale uso,
e lo stesso vale per gawk
.
Successivo: Istruzione nextfile, Precedente: Istruzione continue, Su: Istruzioni [Contenuti][Indice]
next
L’istruzione next
fa sì che awk
termini immediatamente
l’elaborazione del record corrente e proceda a elaborare il record successivo.
Ciò vuol dire che nessuna delle eventuali regole successive viene eseguita
per il record corrente e che il resto delle azioni presenti nella
regola correntemente in esecuzione non viene eseguito.
Si confronti quanto sopra con quel che fa la funzione getline
(vedi Getline). Anch’essa fa sì che awk
legga il record
successivo immediatamente, ma non altera il flusso del controllo in alcun
modo (cioè, il resto dell’azione in esecuzione prosegue con il nuovo record
in input).
Al livello più alto, l’esecuzione di un programma awk
è un ciclo
che legge un record in input e quindi confronta il criterio di ricerca di
ciascuna regola con il record stesso. Se si vede questo ciclo come un
for
il cui corpo contiene le regole, l’istruzione next
è analoga
a un’istruzione continue
. Salta, cioè, alla fine del corpo di questo
ciclo implicito
ed esegue l’incremento (ovvero legge un altro record).
Per esempio, si supponga che un programma awk
agisca solo su record
che hanno quattro campi, e che non dovrebbe terminare con un errore se trova
dei record in input non validi. Per evitare di complicare il resto del
programma, si può scrivere una regola “filtro” a inizio programma, come
mostrato in questo esempio:
NF != 4 { printf("%s:%d: salto: NF != 4\n", FILENAME, FNR) > "/dev/stderr" next }
Siccome è presente un next
,
le regole successive del programma non elaboreranno i record non validi.
Il messaggio è ridiretto al flusso in output
standard error, sul quale vanno scritti i messaggi di errore.
Per maggiori dettagli, si veda
File speciali.
Se l’istruzione next
provoca il raggiungimento della fine del file
in input, vengono eseguite le eventuali regole END
presenti.
Vedi BEGIN/END.
L’istruzione next
non è consentita all’interno delle regole
BEGINFILE
ed ENDFILE
.
Vedi BEGINFILE/ENDFILE.
Secondo lo standard POSIX, il comportamento di awk
è indefinito
se next
è usato in una regola BEGIN
o END
.
gawk
considera questo come un errore di sintassi. Sebbene POSIX
non proibisca di usarlo,
molte altre implementazioni di awk
non consentono che l’istruzione
next
sia usata all’interno del corpo di funzioni.
(vedi Funzioni definite dall'utente).
Come tutte le altre istruzioni next
, un’istruzione next
all’interno del
corpo di una funzione legge il record successivo e inizia a elaborarlo
a partire dalla prima regola del programma.
Successivo: Istruzione exit, Precedente: Istruzione next, Su: Istruzioni [Contenuti][Indice]
nextfile
L’istruzione nextfile
è simile all’istruzione next
.
Tuttavia, invece di terminare l’elaborazione del record corrente, l’istruzione
nextfile
richiede ad awk
di terminare di elaborare il
file-dati corrente.
Alla fine dell’esecuzione dell’istruzione nextfile
,
FILENAME
è
aggiornato per contenere il nome del successivo file-dati elencato sulla riga
di comando, FNR
è reimpostato a uno, e l’elaborazione riparte con
la prima regola del programma.
Se l’istruzione nextfile
raggiunge la fine dei file in input,
vengono eseguite le eventuali regole END
presenti.
Un’eccezione a questo si ha se nextfile
è invocata durante
l’esecuzione di qualche istruzione all’interno di una regola END
;
in questo caso, il programma viene terminato immediatamente.
Vedi BEGIN/END.
L’istruzione nextfile
è utile quando ci sono parecchi file-dati da
elaborare, ma non è necessario elaborare ogni record in ogni file.
Senza nextfile
,
per passare al successivo file-dati, un programma
dovrebbe continuare a leggere i record che non gli servono. L’istruzione
nextfile
è una maniera molto più efficiente per ottenere lo stesso
risultato.
In gawk
, l’esecuzione di nextfile
produce ulteriori effetti:
le eventuali regole ENDFILE
sono eseguite se gawk
non
si trova correntemente all’interno di una regola END
o
BEGINFILE
; ARGIND
è
incrementato e le eventuali regole BEGINFILE
sono eseguite.
(ARGIND
non è stato ancora trattato.
Vedi Variabili predefinite.)
In gawk
, nextfile
è utile all’interno di una regola
BEGINFILE
per evitare di elaborare un file che altrimenti causerebbe
un errore fatale in gawk
.
In questo caso, le regole ENDFILE
non vengono eseguite.
Vedi BEGINFILE/ENDFILE.
Sebbene possa sembrare che ‘close(FILENAME)’ ottenga lo stesso
risultato di nextfile
, non è così. close()
può essere usato solo per chiudere file, pipe e coprocessi che siano
stati aperti tramite ridirezioni. Non ha niente a che vedere con
l’elaborazione principale che
awk
fa dei file elencati in ARGV
.
NOTA: Per molti anni,
nextfile
è stata un’estensione comune. A settembre 2012 si è deciso di includerla nello standard POSIX. Si veda il sito web dell’Austin Group.
Le versioni correnti di BWK awk
e mawk
entrambe prevedono nextfile
. Tuttavia, non sono consentite istruzioni
nextfile
all’interno del corpo delle funzioni
(vedi Funzioni definite dall'utente).
gawk
lo permette; una nextfile
all’interno del corpo di una
funzione legge il primo record del file
successivo e inizia l’elaborazione dello stesso
a partire dalla prima regola del programma, esattamente come farebbe
qualsiasi altra istruzione nextfile
.
Precedente: Istruzione nextfile, Su: Istruzioni [Contenuti][Indice]
exit
L’istruzione exit
fa sì che awk
termini immediatamente
l’esecuzione della regola corrente e che termini di elaborare l’input;
qualsiasi input ancora da elaborare è ignorato. L’istruzione exit
è
scritta come segue:
exit
[codice di ritorno]
Quando un’istruzione exit
è eseguita all’interno di una regola BEGIN
,
il programma termina completamente l’elaborazione. Nessun record in input
viene letto. Tuttavia, se una regola END
è presente, come parte
dell’esecuzione dell’istruzione exit
,
la regola END
viene eseguita
(vedi BEGIN/END).
Se exit
è usata nel corpo di una regola END
,
il programma termina immediatamente.
Un’istruzione exit
che non fa parte di una regola BEGIN
o END
termina l’esecuzione di qualsiasi ulteriore regola applicabile al record
corrente, salta la lettura di qualsiasi record in input, ed esegue
le eventuali regole END
. gawk
salta anche le eventuali regole
ENDFILE
, che non vengono eseguite.
In questo caso,
se non si desidera che la regola END
venga eseguita, si deve impostare
una variabile a un valore diverso da zero, prima di invocare l’istruzione
exit
e controllarne il valore nella regola END
.
Vedi Funzione assert
per un esempio di questo tipo.
Se si specifica un argomento all’istruzione exit
, il suo valore è
usato come codice di ritorno finale dell’elaborazione awk
. Se non
viene specificato alcun argomento,
exit
fa terminare awk
con un codice di ritorno di
“successo”.
Nel caso in cui un argomento
sia specificato in una prima istruzione exit
e poi exit
sia
chiamato una seconda volta all’interno di una regola END
senza alcun
argomento, awk
usa il valore di ritorno specificato in precedenza.
(a.b.)
Vedi Codice di ritorno per maggiori informazioni.
Per esempio, si supponga che si sia verificata una condizione di errore
difficile o impossibile da gestire. Convenzionalmente, i programmi la
segnalano terminando con un codice di ritorno diverso da zero. Un programma
awk
può farlo usando un’istruzione exit
con un argomento
diverso da zero, come mostrato nell’esempio seguente:
BEGIN { if (("date" | getline data_corrente) <= 0) { print "Non riesco a ottenere la data dal sistema" > "/dev/stderr" exit 1 } print "la data corrente è", data_corrente close("date") }
NOTA: Per una completa portabilità, i codici di ritorno dovrebbero essere compresi tra zero e 126, estremi compresi. Valori negativi e valori maggiori o uguali a 127, possono non generare risultati coerenti tra loro in sistemi operativi diversi.
Successivo: Sommario criteri e azioni, Precedente: Istruzioni, Su: Criteri di ricerca e azioni [Contenuti][Indice]
La maggior parte delle variabili awk
sono disponibili per essere
usate dall’utente secondo le proprie esigenze;
queste variabili non cambiano mai di valore a meno che il
programma non assegni loro dei valori, e non hanno alcuna influenza sul
programma a meno che non si decida di utilizzarle nel programma.
Tuttavia, alcune variabili in awk
hanno dei significati speciali
predefiniti.
awk
tiene conto di alcune di queste automaticamente, in modo da
rendere possibile la richiesta ad awk
di fare certe cose nella
maniera desiderata. Altre variabili sono impostate automaticamente da
awk
, in modo da poter comunicare al programma in esecuzione
informazioni sul modo di procedere interno di awk
.
Questa sezione documenta tutte le variabili predefinite di
gawk
; molte di queste variabili sono anche documentate nei
capitolo che descrivono le loro aree di influenza.
• Variabili modificabili dall'utente: | Variabili predefinite modificabili per
controllare awk .
| |
• Variabili auto-assegnate: | Variabili predefinite con cui
awk fornisce informazioni.
| |
• ARGC e ARGV: | Modi di usare ARGC e ARGV .
|
Successivo: Variabili auto-assegnate, Su: Variabili predefinite [Contenuti][Indice]
awk
La seguente è una lista alfabetica di variabili che è possibile modificare per
controllare come awk
gestisce alcuni compiti.
Le variabili che sono specifiche di gawk
sono contrassegnate da un
cancelletto (‘#’). Queste variabili sono estensioni gawk
.
In altre implementazioni di awk
, o se gawk
è eseguito in
modalità compatibile
(vedi Opzioni), non hanno un significato speciale. (Eventuali eccezioni
sono menzionate nella descrizione di ogni variabile.)
BINMODE #
Su sistemi non-POSIX, questa variabile specifica l’uso della modalità binaria
per tutto l’I/O. I valori numerici di uno, due o tre specificano che i file
in input, i file di output o tutti i file, rispettivamente, devono usare I/O
binario.
Un valore numerico inferiore a zero è trattato come zero e un valore
numerico maggiore di tre è trattato
come tre. Alternativamente, le stringhe "r"
o "w"
specificano
che i file in input e i file in output,
rispettivamente, devono usare
I/O binario. Una stringa "rw"
o "wr"
indica che tutti i file
devono usare I/O binario. Ogni altro valore di stringa è trattato come
"rw"
, ma gawk
genera un messaggio di avvertimento.
BINMODE
è descritto in maggior
dettaglio in Uso su PC. mawk
(vedi Altre versioni)
prevede questa variabile, ma consente solo valori numerici.
CONVFMT
Una stringa che controlla la conversione di numeri in
stringhe (vedi Conversione).
In effetti è la stringa passata come primo argomento alla funzione
sprintf()
(vedi Funzioni per stringhe).
Il suo valore di default è "%.6g"
.
CONVFMT
è stata introdotta dallo standard POSIX.
FIELDWIDTHS #
Una lista di posizioni di colonna, separate da spazi, per dire a
gawk
come dividere campi in input posti su colonne fisse.
Assegnando un valore a FIELDWIDTHS
, le variabili FS
e
FPAT
non vengono usate per effettuare la divisione in campi.
Vedi Dimensione costante per maggiori informazioni.
FPAT #
Un’espressione regolare (di tipo stringa) per dire a gawk
di creare i campi utilizzando come delimitatore il testo che corrisponde
all’espressione regolare.
Assegnando un valore a FPAT
le variabili FS
e FIELDWIDTHS
non vengono usate per effettuare la divisione in campi.
Vedi Separazione in base al contenuto per maggiori informazioni.
FS
Il separatore dei campi in input (vedi Separatori di campo).
Il valore può essere una stringa di un solo carattere o un’espressione
regolare composta da più caratteri che individua il separatore tra i campi
dei record in input. Se il suo valore
è la stringa nulla (""
),
ogni singolo carattere del record costituisce un campo.
(Questo comportamente è un’estensione gawk
. POSIX awk
non
specifica il comportamento quando FS
è la stringa nulla.
Nonostante questo, alcune altre versioni di awk
trattano ""
in modo speciale.)
Il valore di default è " "
, una stringa consistente in un singolo
spazio. In via eccezionale, questo valore significa che qualsiasi sequenza
di spazi, TAB, e/o ritorni a capo costituisce
un solo separatore.
Inoltre eventuali spazi, TAB e ritorni a capo all’inizio e alla fine
del record in input vengono ignorati.
Si può impostare il valore di FS
sulla riga dei comandi usando
l’opzione -F:
awk -F, 'programma' file-in-input
Se gawk
sta usando FIELDWIDTHS
o FPAT
per separare i campi,
assegnare un valore a FS
fa sì che gawk
torni alla
separazione dei campi normale, fatta utilizzando la variabile FS
.
Un modo semplice per fare questo
è semplicemente quello di scrivere l’istruzione
‘FS = FS’, aggiungendo magari un commento esplicativo.
IGNORECASE #
Se la variabile IGNORECASE
è diversa da zero o dalla stringa nulla,
tutti i confronti tra stringhe
e tutti i confronti tra espressioni regolari sono insensibili
alle differenze maiuscolo/minuscolo.
Questo vale per il confronto tra regexp
usando ‘~’ e ‘!~’, per le funzioni gensub()
,
gsub()
, index()
, match()
, patsplit()
,
split()
e sub()
,
per la determinazione della fine record con RS
e per la divisione
in campi con FS
e FPAT
.
Tuttavia, il valore di IGNORECASE
non influenza gli indici
dei vettori
e non influenza la separazione dei campi qualora si usi un separatore di campo
costituito da un unico carattere.
Vedi Maiuscolo-Minuscolo.
LINT #
Quando questa variabile è vera (non uguale a zero e non uguale alla stringa
nulla), gawk
si comporta come se fosse stata specificata sulla
riga di comando l’opzione --lint
(vedi Opzioni).
Con un valore di "fatal"
, gli avvertimenti di lint generano un errore
fatale.
Con un valore di "invalid"
, sono inviati solo gli avvertimenti
per cose che sono effettivamente non valide. (Questa parte non funziona
ancora perfettamente.)
Ogni altro valore vero stampa avvertimenti non fatali.
Se LINT
ha per valore falso nessun avvertimento lint viene
stampato.
Questa variabile è un’estensione gawk
. Non ha un valore speciale
per altre implementazioni di awk
. A differenza di altre variabili
speciali, modificare il valore di LINT
altera la produzione di
avvertimenti lint anche se gawk
è in modalità compatibile.
Analogamente a come le opzioni
--lint e --traditional controllano in maniera indipendente
diversi aspetti del comportamente di gawk
, il controllo
degli avvertimenti di lint durante l’esecuzione del programma è indipendente
dall’implementazione awk
in esecuzione.
OFMT
È questa la stringa che controlla la conversione di numeri in
stringhe (vedi Conversione) quando li
si stampa con l’istruzione
print
. Funziona passandola
come primo argomento alla funzione sprintf()
(vedi Funzioni per stringhe).
Il suo valore di default è "%.6g"
. Le prime versioni di awk
usavano OFMT
per specificare il formato da usare per convertire
numeri in stringhe in espressioni generali; questo compito è ora svolto
da CONVFMT
.
OFS
È il separatore dei campi in output (vedi Separatori di output). È ciò
che viene stampato in output per separare i campi stampati da un’istruzione print
.
Il suo valore di default è " "
, una stringa costituita da un solo
spazio.
ORS
Il separatore dei record in output. Viene stampato alla fine di ogni
istruzione print
. Il suo valore di default è "\n"
,
il carattere di ritorno a capo.
(Vedi Separatori di output.)
PREC #
La precisione disponibile nei numeri a virgola mobile a precisione arbitraria, per default 53 bit (vedi Impostare la precisione).
ROUNDMODE #
La modalità di arrotondamento da usare per operazioni aritmetiche a precisione
arbitraria svolte sui numeri, per default "N"
(roundTiesToEven
nello standard
IEEE 754; vedi Impostare modi di arrotondare).
RS
Il separatore tra record in input. Il suo valore di default è una stringa contenente il solo carattere di ritorno a capo, il che significa che un record in input consiste di una sola riga di testo. Il suo valore può essere anche la stringa nulla, nel qual caso i record sono separati da una o più righe vuote. Se invece è una regexp, i record sono separati da corrispondenze alla regexp nel testo in input. (Vedi Record.)
La possibilità che RS
sia un’espressione regolare
è un’estensione gawk
.
In molte altre implementazioni awk
, oppure
se gawk
è in modalità compatibile
(vedi Opzioni),
è usato solo il primo carattere del valore di RS
.
SUBSEP
Il separatore di indici. Ha il valore di default di
"\034"
ed è usato per separare le parti di cui sono composti gli indici
di un vettore multidimensionale. Quindi, l’espressione
‘pippo["A", "B"]’
in realtà accede a pippo["A\034B"]
(vedi Vettori multidimensionali).
TEXTDOMAIN #
Usata per l’internazionalizzazione di programmi a livello di
awk
. Imposta il dominio di testo (text domain) di default per costanti stringa
marcate in maniera speciale nel codice sorgente, e anche per le funzioni
dcgettext()
, dcngettext()
e bindtextdomain()
(vedi Internazionalizzazione).
Il valore di default di TEXTDOMAIN
è "messages"
.
Successivo: ARGC e ARGV, Precedente: Variabili modificabili dall'utente, Su: Variabili predefinite [Contenuti][Indice]
awk
fornisce informazioniQuella che segue è una lista in ordine alfabetico di variabili che
awk
imposta automaticamente in determinate situazioni per
fornire informazioni a un programma.
Le variabili specifiche di gawk
sono contrassegnate da un
cancelletto (‘#’). Queste variabili sono estensioni gawk
.
In altre implementazioni di awk
o se gawk
è in
modalità compatibile (vedi Opzioni), non hanno un significato speciale:
ARGC
, ARGV
Gli argomenti della riga di comando disponibili ai programmi awk
sono memorizzati in un vettore di nome
ARGV
. ARGC
è il numero di argomenti presenti sulla
riga di comando.
Vedi Altri argomenti.
A differenza di quasi tutti i vettori di awk
,
ARGV
è indicizzato da 0 a ARGC
- 1.
Lo si può vedere nell’esempio seguente:
$ awk 'BEGIN { > for (i = 0; i < ARGC; i++) > print ARGV[i] > }' inventory-shipped mail-list -| awk -| inventory-shipped -| mail-list
ARGV[0]
contiene ‘awk’, ARGV[1]
contiene ‘inventory-shipped’ e ARGV[2]
contiene
‘mail-list’. Il valore di ARGC
è tre, ossia uno in più
dell’indice dell’ultimo elemento di ARGV
, perché gli elementi sono
numerati a partire da zero.
I nomi ARGC
e ARGV
, come pure la convenzione di indicizzare
il vettore da 0 a ARGC
- 1, derivano dal modo in cui il
linguaggio C accede agli argomenti presenti sulla riga di comando.
Il valore di ARGV[0]
può variare da sistema a sistema.
Va anche notato che il programma non
è incluso in ARGV
, e non sono incluse neppure le eventuali opzioni di
awk
specificate sulla riga di comando.
Vedi ARGC e ARGV per informazioni
su come awk
usa queste variabili.
(a.b.)
ARGIND #
L’indice in ARGV
del file correntemente in elaborazione.
Ogni volta che gawk
apre un nuovo file-dati per elaborarlo, imposta
ARGIND
all’indice in ARGV
del nome-file.
Quando gawk
sta elaborando i file in input, il confronto
‘FILENAME == ARGV[ARGIND]’ è sempre verificato.
Questa variabile è utile nell’elaborazione dei file; consente di stabilire a che punto ci si trova nella lista di file-dati, e anche di distinguere tra successive occorrenze dello stesso nome-file sulla riga dei comandi.
Anche se è possibile modificare il valore di ARGIND
all’interno del
programma awk
, gawk
automaticamente lo imposta a un nuovo
valore quando viene aperto il file successivo.
ENVIRON
Un vettore associativo contenente i valori delle variabili d’ambiente.
Gli indici del vettore sono i nomi delle variabili d’ambiente; gli elementi
sono i valori della specifica variabile d’ambiente. Per esempio,
ENVIRON["HOME"]
potrebbe valere /home/arnold
.
Per POSIX awk
, le modifiche a questo vettore non cambiano
le variabili d’ambiente passate a qualsivoglia programma che awk
può richiamare tramite una ridirezione
o usando la funzione system()
.
Tuttavia, a partire dalla versione 4.2, se non si è in
modalità compatibile POSIX, gawk
aggiorna le proprie variabili
d’ambiente, quando si modifica ENVIRON
, e in questo modo sono
modificate anche le variabili d’ambiente disponibili ai programmi richiamati.
Un’attenzione speciale dovrebbe essere prestata alla modifica di
ENVIRON["PATH"]
, che è il percorso di ricerca usato per trovare
i programmi eseguibili.
Queste modifiche possono anche influire sul programma gawk
, poiché
alcune funzioni predefinite possono tener conto di certe
variabili d’ambiente.
L’esempio più notevole di una tale situazione è mktime()
(vedi Funzioni di tempo)
che, in molti sistemi, tiene conto del valore della
variabile d’ambiente TZ
.
Alcuni sistemi operativi possono non avere variabili d’ambiente.
In tali sistemi, il vettore ENVIRON
è vuoto (tranne che per
le variabili
ENVIRON["AWKPATH"]
e
ENVIRON["AWKLIBPATH"]
eventualmente presenti;
vedi AWKPATH (Variabile) e
vedi AWKLIBPATH (Variabile)).
ERRNO #
Se si verifica un errore di sistema durante una ridirezione per getline
,
durante una lettura per getline
o durante un’operazione di
close()
, la variabile
ERRNO
contiene una stringa che descrive l’errore.
Inoltre, gawk
annulla ERRNO
prima di aprire ogni
file in input presente sulla riga di comando. Questo consente di controllare
se il file è accessibile
all’interno di un criterio di ricerca BEGINFILE
(vedi BEGINFILE/ENDFILE).
Per il resto, ERRNO
si comporta analogamente alla variabile C
errno
.
Tranne nel caso sopra accennato, gawk
non annulla mai
ERRNO
(lo imposta a zero o a ""
). Quindi, ci si deve
aspettare che il suo valore sia significativo solo quando un’operazione
di I/O restituisce un valore che indica il fallimento dell’operazione, come
per esempio quando getline
restituisce -1. Si è, naturalmente,
liberi di annullarla prima di effettuare un’operazione di I/O.
Se il valore di ERRNO
corrisponde a un errore di sistema della
variabile C errno
, PROCINFO["errno"]
sarà impostato al valore
di errno
. Per errori non di sistema, PROCINFO["errno"]
sarà
impostata al valore zero.
FILENAME
Il nome del file in input corrente. Quando non ci sono file-dati
sulla riga dei comandi, awk
legge dallo standard input e
FILENAME
è impostato a "-"
. FILENAME
cambia ogni
volta che si legge un nuovo file
(vedi Leggere file).
All’interno di una
regola BEGIN
, il valore di FILENAME
è ""
, perché non si
sta elaborando alcun file in input.39
(a.b.) Si noti, tuttavia,
che l’uso di getline
(vedi Getline) all’interno di una regola
BEGIN
può implicare l’assegnamento di un valore a FILENAME
.
FNR
Il numero del record corrente nel file corrente. awk
incrementa
FNR
ogni volta che legge un nuovo record (vedi Record).
awk
imposta nuovamente a zero FNR
ogni volta che inizia a
leggere un nuovo file in input.
NF
Il numero di campi nel corrente record in input.
NF
è impostato ogni volta che si legge un nuovo record,
quando un nuovo campo viene creato,
o quando si modifica $0
(vedi Campi).
A differenza di molte altre variabili descritte in questa sottosezione,
l’assegnamento di un valore a NF
può potenzialmente influenzare
il funzionamento interno di awk
. In particolare, assegnamenti
a NF
si possono usare per aggiungere o togliere campi dal
record corrente. Vedi Cambiare i campi.
FUNCTAB #
Un vettore i cui indici e i corrispondenti valori sono i nomi di tutte le funzioni predefinite, definite dall’utente ed estese, presenti nel programma.
NOTA: Il tentativo di usare l’istruzione
delete
per eliminare il vettoreFUNCTAB
genera un errore fatale. Genera un errore fatale anche ogni tentativo di impostare un elemento diFUNCTAB
.
NR
Il numero di record in input che awk
ha elaborato dall’inizio
dell’esecuzione del programma
(vedi Record).
awk
incrementa NR
ogni volta che legge un nuovo record.
PROCINFO #
Gli elementi di questo vettore danno accesso a informazioni sul
programma awk
in esecuzione.
I seguenti elementi (elencati in ordine alfabetico)
sono sicuramente sempre disponibili:
PROCINFO["egid"]
Il valore restituito dalla chiamata di sistema getegid()
.
PROCINFO["errno"]
Il valore della variabile C ERRNO
quando ERRNO
è impostato
al messaggio di errore a essa associato.
PROCINFO["euid"]
Il valore restituito dalla chiamata di sistema geteuid()
.
PROCINFO["FS"]
Questo elemento vale
"FS"
se è in uso la separazione in campi con FS
,
"FIELDWIDTHS"
se è in uso quella con FIELDWIDTHS
,
oppure "FPAT"
se è in uso l’individuazione di campo con FPAT
.
PROCINFO["gid"]
Il valore restituito dalla chiamata di sistema getgid()
.
PROCINFO["identifiers"]
Un sottovettore, indicizzato dai nomi di tutti gli identificativi usati
all’interno del programma awk
. Un identificativo è
semplicemente il nome di una variabile
(scalare o vettoriale), una funzione predefinita, una funzione definita
dall’utente, o una funzione contenuta in un’estensione.
Per ogni identificativo, il valore dell’elemento è uno dei seguenti:
"array"
L’identificativo è un vettore.
"builtin"
L’identificativo è una funzione predefinita.
"extension"
L’identificativo è una funzione in un’estensione caricata tramite
@load
o con l’opzione -l.
"scalar"
L’identificativo è uno scalare.
"untyped"
L’identificativo non ha ancora un tipo (potrebbe essere usato come scalare o
come vettore; gawk
non è ancora in grado di dirlo).
"user"
L’identificativo è una funzione definita dall’utente.
I valori riportano ciò che gawk
sa sugli identificativi
dopo aver finito l’analisi iniziale del programma; questi valori non
vengono più aggiornati durante l’esecuzione del programma.
PROCINFO["pgrpid"]
Il ID di gruppo del processo del programma corrente.
PROCINFO["pid"]
Il process ID del programma corrente.
PROCINFO["ppid"]
Il ID di processo del padre del programma corrente.
PROCINFO["strftime"]
La stringa col formato di default usato per la funzione strftime()
.
Assegnando un nuovo valore a questo elemento si cambia quello di default.
Vedi Funzioni di tempo.
PROCINFO["uid"]
Il valore restituito dalla chiamata di sistema getuid()
.
PROCINFO["version"]
La versione di gawk
.
I seguenti elementi addizionali del vettore
sono disponibili per fornire informazioni sulle librerie MPFR e GMP,
se la versione in uso di gawk
consente il calcolo con precisione
arbitraria
(vedi Calcolo con precisione arbitraria):
PROCINFO["gmp_version"]
La versione della libreria GNU MP.
PROCINFO["mpfr_version"]
La versione della libreria GNU MPFR.
PROCINFO["prec_max"]
La massima precisione consentita da MPFR.
PROCINFO["prec_min"]
La precisione minima richiesta da MPFR.
I seguenti elementi addizionali del vettore
sono disponibili per fornire
informazioni sulla versione dell’estensione API, se la versione
di gawk
prevede il caricamento dinamico di funzioni di estensione
(vedi Estensioni dinamiche):
PROCINFO["api_major"]
La versione principale dell’estensione API.
PROCINFO["api_minor"]
La versione secondaria dell’estensione API.
Su alcuni sistemi, ci possono essere elementi nel vettore, da "group1"
fino a "groupN"
. N è il numero di
gruppi supplementari che il processo [Unix] possiede. Si usi l’operatore
in
per verificare la presenza di questi elementi
(vedi Visitare elementi).
I seguenti elementi consentono di modificare il comportamento di
gawk
:
Se questo elemento esiste, gli errori di I/O per tutte le ridirezioni consentono la prosecuzizone del programma. Vedi Continuazione dopo errori.
Gli errori in output per il file nome_output consentono la prosecuzizone del programma. Vedi Continuazione dopo errori.
PROCINFO["comando", "pty"]
Per una comunicazione bidirezionale con comando, si usi una pseudo-tty invece di impostare una pipe bidirezionale. Vedi I/O bidirezionale per ulteriori informazioni.
PROCINFO["input_name", "READ_TIMEOUT"]
Imposta un tempo limite per leggere dalla ridirezione di input input_name. Vedi Timeout in lettura per ulteriori informazioni.
PROCINFO["sorted_in"]
Se questo elemento esiste in PROCINFO
, il suo valore controlla
l’ordine in cui gli indici dei vettori saranno elaborati nei cicli
‘for (indice in vettore)’.
Questa è una funzionalità avanzata, la cui descrizione completa sarà vista
più avanti; si veda
Visitare un intero vettore.
RLENGTH
La lunghezza della sottostringa individuata dalla funzione
match()
(vedi Funzioni per stringhe).
RLENGTH
viene impostata quando si richiama la funzione match()
.
Il suo
valore è la lunghezza della stringa individuata, oppure -1 se non è
stata trovata alcuna corrispondenza.
RSTART
L’indice, in caratteri, da cui parte la sottostringa che è individuata dalla
funzione match()
(vedi Funzioni per stringhe).
RSTART
viene impostata quando si richiama la funzione match()
.
Il suo
valore è la posizione nella stringa da cui inizia la sottostringa
individuata, oppure zero, se non è stata trovata alcuna corrispondenza.
RT #
Il testo in input che corrisponde al testo individuato da RS
,
il separatore di record. Questa variabile viene impostata dopo aver letto
ciascun record.
SYMTAB #
Un vettore i cui indici sono i nomi di tutte le variabili globali e i vettori
definiti nel programma. SYMTAB
rende visibile al
programmatore awk
la tabella dei simboli di gawk
.
Viene preparata nella fase di analisi iniziale del programma gawk
ed è completata prima di cominciare a eseguire il programma.
Il vettore può essere usato per accedere indirettamente, in lettura o in scrittura, al valore di una variabile:
pippo = 5 SYMTAB["pippo"] = 4 print pippo # stampa 4
La funzione isarray()
(vedi Funzioni per i tipi) si può usare per
controllare se un elemento in SYMTAB
è un vettore.
Inoltre, non è possibile usare l’istruzione delete
con il vettore
SYMTAB
.
È possibile aggiungere a SYMTAB
un elemento che non sia un
identificativo già esistente:
SYMTAB["xxx"] = 5 print SYMTAB["xxx"]
Il risultato è quello previsto: in questo caso SYMTAB
si comporta
come un normale vettore. La sola differenza è che non è poi possibile
cancellare SYMTAB["xxx"]
.
Il vettore SYMTAB
è più interessante di quel che sembra. Andrew
Schorr fa notare che effettivamente consente di ottenere dei puntatori ai dati
in awk
. Si consideri quest’esempio:
# Moltiplicazione indiretta di una qualsiasi variabile per un # numero a piacere e restituzione del risultato function multiply(variabile, numero) { return SYMTAB[variabile] *= numero }
Si potrebbe usare in questo modo:
BEGIN { risposta = 10.5 multiply("risposta", 4) print "La risposta è", risposta }
Eseguendo, il risultato è:
$ gawk -f risposta.awk -| La risposta è 42
NOTA: Per evitare seri paradossi temporali, 40 né
FUNCTAB
néSYMTAB
sono disponibili come elementi all’interno del vettoreSYMTAB
.
Modificare
NR e FNR
$ echo '1 > 2 > 3 > 4' | awk 'NR == 2 { NR = 17 } > { print NR }' -| 1 -| 17 -| 18 -| 19 Prima che |
Precedente: Variabili auto-assegnate, Su: Variabili predefinite [Contenuti][Indice]
ARGC
e ARGV
Variabili auto-assegnate
conteneva il programma seguente che visualizzava le informazioni contenute
in ARGC
e ARGV
:
$ awk 'BEGIN { > for (i = 0; i < ARGC; i++) > print ARGV[i] > }' inventory-shipped mail-list -| awk -| inventory-shipped -| mail-list
In questo esempio, ARGV[0]
contiene ‘awk’, ARGV[1]
contiene ‘inventory-shipped’ e ARGV[2]
contiene
‘mail-list’.
Si noti che il nome del programma awk
non è incluso in ARGV
.
Le altre opzioni della riga di comando, con i relativi argomenti,
sono parimenti non presenti, compresi anche gli assegnamenti di
variabile fatti tramite l’opzione -v
(vedi Opzioni).
I normali assegnamenti di variabile sulla riga dei comandi sono
trattati come argomenti e quindi inseriti nel vettore ARGV
.
Dato il seguente programma in un file di nome vediargomenti.awk:
BEGIN { printf "A=%d, B=%d\n", A, B for (i = 0; i < ARGC; i++) printf "\tARGV[%d] = %s\n", i, ARGV[i] } END { printf "A=%d, B=%d\n", A, B }
la sua esecuzione produce il seguente risultato:
$ awk -v A=1 -f vediargomenti.awk B=2 /dev/null -| A=1, B=0 -| ARGV[0] = awk -| ARGV[1] = B=2 -| ARGV[2] = /dev/null -| A=1, B=2
Un programma può modificare ARGC
e gli elementi di ARGV
.
Ogni volta che awk
arriva alla fine di un file in input, usa
il successivo elemento nel vettore ARGV
come nome del successivo file
in input. Cambiando il contenuto di quella stringa, un programma
può
modificare la lista dei file che sono letti.
Si usi "-"
per rappresentare lo standard input. Assegnando ulteriori
elementi e incrementando ARGC
verranno letti ulteriori file.
Se il valore di ARGC
viene diminuto, vengono ignorati i file in input
posti alla fine della lista. Memorizzando il valore originale di ARGC
da qualche altra parte, un programma può gestire gli argomenti
ignorati come se fossero qualcosa di diverso dai nome-file.
Per eliminare un file che sia in mezzo alla lista, si imposti in ARGV
la stringa nulla
(""
) al posto del nome del file in questione. Come funzionalità
speciale, awk
ignora valori di nome-file che siano stati
rimpiazzati con la stringa nulla.
Un’altra possibilità è quella
di usare l’istruzione delete
per
togliere elementi da ARGV
(vedi Cancellazione).
Tutte queste azioni sono tipicamente eseguite nella regola BEGIN
,
prima di iniziare l’elaborazione vera e propria dell’input.
Vedi Programma split e
vedi Programma tee
per esempi
su ognuno dei modi per togliere elementi dal vettore ARGV
.
Per passare direttamente delle opzioni a un programma scritto in
awk
, si devono terminare le opzioni di awk
con
-- e poi inserire le opzioni destinate al programma awk
,
come mostrato qui di seguito:
awk -f mio_programma.awk -- -v -q file1 file2 …
Il seguente frammento di programma ispeziona ARGV
per esaminare, e
poi rimuovere, le opzioni sulla riga di comando viste sopra:
BEGIN { for (i = 1; i < ARGC; i++) { if (ARGV[i] == "-v") verbose = 1 else if (ARGV[i] == "-q") debug = 1 else if (ARGV[i] ~ /^-./) { e = sprintf("%s: opzione non riconosciuta -- %c", ARGV[0], substr(ARGV[i], 2, 1)) print e > "/dev/stderr" } else break delete ARGV[i] } }
Terminare le opzioni di awk
con -- non è
necessario in gawk
. A meno che non si specifichi --posix,
gawk
inserisce, senza emettere messaggi, ogni opzione non
riconosciuta
nel vettore ARGV
perché sia trattata dal programma awk
.
Dopo aver trovato un’opzione non riconosciuta, gawk
non cerca
ulteriori opzioni, anche se ce ne fossero di riconoscibili.
La riga dei comandi precedente sarebbe
con gawk
:
gawk -f mio_programma.awk -q -v file1 file2 …
Poiché -q non è un’opzione valida per gawk
, quest’opzione
e l’opzione -v che segue sono passate al programma awk
.
(Vedi Funzione getopt per una funzione di libreria awk
che analizza le opzioni della riga di comando.)
Nel progettare un programma, si dovrebbero scegliere opzioni che non
siano in conflitto con quelle di gawk
, perché ogni opzioni
accettata da gawk
verrà elaborata prima di passare il resto
della riga dei comandi al programma awk
.
Usare ‘#!’ con l’opzione -E può essere utile in questo caso
(vedi Script eseguibili
e
vedi Opzioni).
Precedente: Variabili predefinite, Su: Criteri di ricerca e azioni [Contenuti][Indice]
awk
. I criteri di ricerca possono essere espressioni
normali, espressioni di intervallo, o costanti regexp; possono anche
essere i criteri speciali BEGIN
, END
,
BEGINFILE
o ENDFILE
; o essere omessi. L’azione viene eseguita
se il record corrente soddisfa il criterio di ricerca. Criteri di ricerca
vuoti (omessi) corrispondono a
tutti i record in input.
BEGIN
ed END
ha alcuni vincoli.
Questo vale a maggior ragione per le regole BEGINFILE
ed
ENDFILE
. Queste ultime due forniscono degli “agganci” per interagire
con l’elaborazione dei file fatta da gawk
,
consentendo di risolvere situazioni che altrimenti genererebbero degli
errori fatali (ad esempio per un file che non si è autorizzati
a leggere).
awk
prestando la dovuta attenzione all’uso degli apici.
È più facile passare una variabile di shell ad
awk
usando l’opzione -v e una variabile awk
.
awk
sono if
-else
,
while
, for
e do
-while
. gawk
aggiunge l’istruzione switch
. Ci sono due tipi di istruzione
for
: uno per eseguire dei cicli, e l’altro per esaminare un vettore.
break
e continue
permettono di uscire
velocemente da un ciclo, o di passare alla successiva iterazione dello
stesso (o di uscire da un’istruzione switch
).
next
e nextfile
permettono, rispettivamente,
di passare al record successivo, ricominciando l’elaborazione dalla prima
regola del programma, o di passare al successivo file in input, sempre
ripartendo dalla prima regola del programma.
exit
termina il programma. Quando è eseguita
dall’interno di un’azione (o nel corpo di una funzione), trasferisce
il controlla alle eventuali istruzioni END
. Se è eseguita nel corpo
di un’istruzione END
, il programma è terminato
immediatamente. È possibile specificare un valore numerico da usare come
codice di ritorno di awk
.
awk
,
principalmente per l’I/O. Altre variabili trasmettono informazioni
da awk
al programma.
ARGC
e ARGV
rendono disponibili al programma gli
argomenti della riga di comando. Una loro modifica all’interno di una regola
BEGIN
permette di controllare come awk
elaborerà i file-dati
in input.
Successivo: Funzioni, Precedente: Criteri di ricerca e azioni, Su: Top [Contenuti][Indice]
awk
Un vettore è una tabella di valori chiamati elementi. Gli elementi di un vettore sono individuati dai loro indici. Gli indici possono essere numeri o stringhe.
Questo capitolo descrive come funzionano i vettori in awk
,
come usare gli elementi di un vettore, come visitare tutti gli elementi
di un vettore, e come rimuovere elementi da un vettore.
Descrive anche come awk
simula vettori multidimensionali,
oltre ad alcuni aspetti meno ovvii sull’uso dei vettori.
Il capitolo prosegue illustrando la funzionalità di ordinamento dei
vettori di gawk
, e termina con una breve descrizione della capacità
di gawk
di consentire veri vettori di vettori.
• Fondamenti sui vettori: | Informazioni di base sui vettori. | |
• Indici numerici di vettore: | Come usare numeri come indici in
awk .
| |
• Indici non inizializzati: | Usare variabili non inizializzate come indici. | |
• Cancellazione: | L’istruzione delete toglie un elemento
da un vettore.
| |
• Vettori multidimensionali: | Emulare vettori multidimensionali in
awk .
| |
• Vettori di vettori: | Vettori multidimensionali veri. | |
• Sommario dei vettori: | Sommario dei vettori. |
Successivo: Indici numerici di vettore, Su: Vettori [Contenuti][Indice]
Questa sezione espone le nozioni fondamentali: elaborare gli elementi di un vettore uno alla volta, e visitare sequenzialmente tutti gli elementi di un vettore.
• Introduzione ai vettori: | Introduzione ai vettori | |
• Visitare elementi: | Come esaminare un elemento di un vettore. | |
• Impostare elementi: | Come cambiare un elemento di un vettore. | |
• Esempio di vettore: | Esempio semplice di vettore | |
• Visitare un intero vettore: | Variazione dell’istruzione for . Esegue
un ciclo attraverso gli indici degli elementi
contenuti in un vettore.
| |
• Controllare visita: | Controllare l’ordine in cui i vettori sono visitati. |
Successivo: Visitare elementi, Su: Fondamenti sui vettori [Contenuti][Indice]
Visitare sequenzialmente un vettore associativo è come tentare di lapidare qualcuno usando una mitragliatrice Uzi carica.
Il linguaggio awk
mette a disposizione vettori monodimensionali per
memorizzare gruppi di stringhe o di numeri correlati fra loro. Ogni vettore di
awk
deve avere un nome. I nomi dei vettori hanno la stessa sintassi
dei nomi di variabile; qualsiasi nome valido di variabile potrebbe essere anche
un valido nome di vettore. Un nome però non può essere usato in entrambi i
modi (come vettore e come variabile) nello stesso programma awk
.
I vettori in awk
assomigliano superficialmente ai vettori in altri
linguaggi di programmazione, ma ci sono differenze fondamentali. In
awk
, non è necessario specificare la dimensione di un vettore prima
di iniziare a usarlo. In più, qualsiasi numero o stringa può essere usato
come indice di un vettore, non solo numeri interi consecutivi.
Nella maggior parte degli altri linguaggi, i vettori devono essere dichiarati prima dell’uso, specificando quanti elementi o componenti contengono. In questi linguaggi, la dichiarazione causa l’allocazione, per questi elementi, di un blocco di memoria contiguo. Normalmente, un indice di un vettore dev’essere un intero non negativo. Per esempio, l’indice zero specifica il primo elemento nel vettore, che è effettivamente memorizzato all’inizio di un blocco di memoria. L’indice uno specifica il secondo elemento, che è memorizzato subito dopo il primo elemento, e così via. È impossibile aggiungere ulteriori elementi al vettore, perché esso può contenere solo il numero di elementi dichiarato. (Alcuni linguaggi consentono indici iniziali e finali arbitrari—p.es., ‘15 .. 27’—però la dimensione del vettore rimane fissa una volta che il vettore sia stato dichiarato.)
Un vettore contiguo di quattro elementi potrebbe essere come quello in
Figura 8.1,
concettualmente, se i valori degli elementi sono 8, "pippo"
,
""
e 30.
Vengono memorizzati solo i valori; gli indici sono definiti implicitamente dall’ordine dei valori. Qui, 8 è il valore il cui indice è zero, perché 8 appare nella posizione con zero elementi prima di essa.
I vettori in awk
non sono di questo tipo: sono invece
associativi.
Ciò significa che ogni vettore è un insieme di coppie, ognuna costituita
da un indice e dal corrispondente valore dell’elemento del vettore:
Indice | Valore | |
---|---|---|
3 | 30 | |
1 | "pippo" | |
0 | 8 | |
2 | "" |
Le coppie sono elencate in ordine casuale perché il loro ordinamento è irrilevante.41
Un vantaggio dei vettori associativi è che si possono aggiungere nuove coppie
in qualsiasi momento. Per esempio, supponiamo di aggiungere al vettore un
decimo elemento il cui valore sia "numero dieci"
. Il risultato sarà:
Indice | Valore | |
---|---|---|
10 | "numero dieci" | |
3 | 30 | |
1 | "pippo" | |
0 | 8 | |
2 | "" |
Ora il vettore è sparso, il che significa semplicemente che non sono usati alcuni indici. Ha gli elementi 0, 1, 2, 3 e 10, ma mancano gli elementi 4, 5, 6, 7, 8 e 9.
Un’altra caratteristica dei vettori associativi è che gli indici non devono essere necessariamente interi non negativi. Qualsiasi numero, o anche una stringa, può essere un indice. Per esempio, il seguente è un vettore che traduce delle parole dall’inglese all’italiano:
Indice | Valore | |
---|---|---|
"dog" | "cane" | |
"cat" | "gatto" | |
"one" | "uno" | |
1 | "uno" |
Qui abbiamo deciso di tradurre il numero uno sia nella forma letterale che in
quella numerica, per illustrare che un singolo vettore può avere come indici
sia numeri che stringhe.
(In effetti, gli indici dei vettori sono sempre stringhe.
Ci sono alcune sottigliezze su come funzionano i numeri quando sono usati come
indici dei vettori; questo verrà trattato in maggior dettaglio nella
Indici numerici di vettore.)
Qui sopra, il numero 1
non è tra doppi apici, perché awk
lo converte automaticamente in una stringa.
Il valore di IGNORECASE
non ha alcun effetto sull’indicizzazione dei
vettori. Lo stesso valore di stringa usato per memorizzare un elemento di un
vettore può essere usato per richiamarlo.
Quando awk
crea un vettore (p.es., con la funzione predefinita
split()
), gli indici di quel vettore sono numeri interi consecutivi
a partire da uno.
(Vedi Funzioni per stringhe.)
I vettori di awk
sono efficienti: il tempo necessario per accedere a
un elemento è indipendente dal numero di elementi nel vettore.
Successivo: Impostare elementi, Precedente: Introduzione ai vettori, Su: Fondamenti sui vettori [Contenuti][Indice]
Il modo principale di usare un vettore è quello di esaminare uno dei suoi elementi. Un riferimento al vettore è un’espressione come questa:
vettore[espressione-indice]
Qui, vettore è il nome di un vettore. L’espressione espressione-indice è l’indice dell’elemento del vettore che si vuol esaminare.
Il valore del riferimento al vettore è il valore corrente di quell’elemento
del vettore. Per esempio, pippo[4.3]
è un’espressione che richiama
l’elemento del vettore pippo
il cui indice è ‘4.3’.
Un riferimento a un elemento di un vettore il cui indice non esiste ancora
restituisce un valore uguale a ""
, la stringa nulla. Questo comprende
elementi a cui non è stato assegnato un valore ed elementi che sono stati
eliminati (vedi Cancellazione).
NOTA: Un riferimento a un elemento inesistente crea automaticamente quell’elemento di vettore, con la stringa nulla come valore. (In certi casi, ciò è indesiderabile, perché potrebbe sprecare memoria all’interno di
awk
.)I programmatori principianti di
awk
fanno spesso l’errore di verificare se un elemento esiste controllando se il valore è vuoto:# Verifica se "pippo" esiste in a: Non corretto! if (a["pippo"] != "") …Questo è sbagliato per due motivi. Primo, crea
a["pippo"]
se ancora non esisteva! Secondo, assegnare a un elemento di un vettore la stringa vuota come valore è un’operazione valida (anche se un po’ insolita).
Per determinare se un elemento con un dato indice esiste in un vettore, si usi la seguente espressione:
indice in vettore
Quest’espressione verifica se lo specifico indice indice esiste, senza
l’effetto collaterale di creare quell’elemento nel caso che esso non sia
presente. L’espressione ha il valore uno (vero) se
vettore[indice]
esiste e zero (falso) se non esiste.
Per esempio, quest’istruzione verifica se il vettore frequenze
contiene l’indice ‘2’:
if (2 in frequenze) print "L'indice 2 è presente."
Si noti che questo non verifica se il vettore
frequenze
contiene un elemento il cui valore è 2.
Il solo modo far questo è quello di passare in rassegna tutti gli
elementi. Inoltre, questo non crea frequenze[2]
, mentre la
seguente alternativa (non corretta) lo fa:
if (frequenze[2] != "") print "L'indice 2 è presente."
Successivo: Esempio di vettore, Precedente: Visitare elementi, Su: Fondamenti sui vettori [Contenuti][Indice]
Agli elementi di un vettore possono essere assegnati valori proprio come
alle variabili di awk
:
vettore[espressione-indice] = valore
vettore è il nome di un vettore. L’espressione espressione-indice è l’indice dell’elemento del vettore a cui è assegnato il valore. L’espressione valore è il valore da assegnare a quell’elemento del vettore.
Successivo: Visitare un intero vettore, Precedente: Impostare elementi, Su: Fondamenti sui vettori [Contenuti][Indice]
Il seguente programma prende una lista di righe, ognuna delle quali inizia con un numero di riga, e le stampa in ordine di numero di riga. I numeri di riga non sono ordinati al momento della lettura, ma sono invece in ordine sparso. Questo programma ordina le righe mediante la creazione di un vettore che usa i numeri di riga come indici. Il programma stampa poi le righe ordinate secondo il loro numero. È un programma molto semplice e non è in grado di gestire numeri ripetuti, salti di riga o righe che non iniziano con un numero:
{ if ($1 > massimo) massimo = $1 vett[$1] = $0 } END { for (x = 1; x <= massimo; x++) print vett[x] }
La prima regola tiene traccia del numero di riga più grande visto
durante la lettura;
memorizza anche ogni riga nel vettore vett
, usando come indice
il numero di riga.
La seconda regola viene eseguita dopo che è stato letto tutto l’input, per
stampare tutte le righe.
Quando questo programma viene eseguito col seguente input:
5 Io sono l'uomo Cinque 2 Chi sei? Il nuovo numero due! 4 . . . E quattro a terra 1 Chi è il numero uno? 3 Sei il tre.
Il suo output è:
1 Chi è il numero uno? 2 Chi sei? Il nuovo numero due! 3 Sei il tre. 4 . . . E quattro a terra 5 Io sono l'uomo Cinque
Se un numero di riga appare più di una volta, l’ultima riga con quel dato
numero prevale sulle altre.
Le righe non presenti nel vettore
si possono saltare con un semplice perfezionamento della
regola END
del programma, in questo modo:
END { for (x = 1; x <= massimo; x++) if (x in vett) print vett[x] }
Successivo: Controllare visita, Precedente: Esempio di vettore, Su: Fondamenti sui vettori [Contenuti][Indice]
Nei programmi che usano vettori, è spesso necessario usare un ciclo che
esegue un’azione su ciascun elemento di un vettore. In altri linguaggi, dove
i vettori sono contigui e gli indici sono limitati ai numeri interi
non negativi,
questo è facile: tutti gli indici validi possono essere visitati partendo
dall’indice più basso e arrivando a quello più alto. Questa tecnica non
è applicabile in awk
, perché qualsiasi numero o stringa può
fare da indice in un vettore. Perciò awk
ha un tipo speciale di
istruzione for
per visitare un vettore:
for (variabile in vettore) corpo
Questo ciclo esegue corpo una volta per ogni indice in vettore che il programma ha usato precedentemente, con la variabile variabile impostata a quell’indice.
Il seguente programma usa questa forma dell’istruzione for
. La
prima regola visita i record in input e tiene nota di quali parole appaiono
(almeno una volta) nell’input, memorizzando un 1 nel vettore usate
con
la parola come indice. La seconda regola visita gli elementi di
usate
per trovare tutte le parole distinte che appaiono nell’input.
Il programma stampa poi ogni parola che è più lunga di 10 caratteri e
anche il numero di tali parole.
Vedi Funzioni per stringhe
per maggiori informazioni sulla funzione predefinita length()
.
# Registra un 1 per ogni parola usata almeno una volta { for (i = 1; i <= NF; i++) usate[$i] = 1 } # Trova il numero di parole distinte lunghe più di 10 caratteri END { for (x in usate) { if (length(x) > 10) { ++numero_parole_lunghe print x } } print numero_parole_lunghe, "parole più lunghe di 10 caratteri" }
Vedi Programma utilizzo parole per un esempio di questo tipo più dettagliato.
L’ordine nel quale gli elementi del vettore sono esaminati da quest’istruzione
è determinato dalla disposizione interna degli elementi del vettore all’interno
di awk
e nell’awk
standard non può essere controllato
o cambiato. Questo può portare a dei problemi se vengono aggiunti nuovi
elementi al vettore dall’istruzione eseguendo il corpo del ciclo;
non è prevedibile se il ciclo for
li potrà raggiungere. Similmente,
modificare variabile all’interno del ciclo potrebbe produrre strani
risultati. È meglio evitare di farlo.
Di sicuro, gawk
imposta la lista di elementi su cui eseguire
l’iterazione prima che inizi il ciclo, e non la cambia in corso d’opera.
Ma non tutte le versioni di awk
fanno così. Si consideri questo
programma, chiamato vediciclo.awk:
BEGIN { a["questo"] = "questo" a["è"] = "è" a["un"] = "un" a["ciclo"] = "ciclo" for (i in a) { j++ a[j] = j print i } }
Ecco quel che accade quando viene eseguito con gawk
(e mawk
):
$ gawk -f vediciclo.awk -| questo -| ciclo -| un -| è
Se si usa invece BWK awk
:
$ nawk -f vediciclo.awk -| ciclo -| questo -| è -| un -| 1
Precedente: Visitare un intero vettore, Su: Fondamenti sui vettori [Contenuti][Indice]
gawk
Questa sottosezione descrive una funzionalità disponibile solo in
gawk
.
Per default, quando un ciclo for
visita un vettore, l’ordine
è indeterminato, il che vuol dire che l’implementazione di awk
determina l’ordine in cui il vettore viene attraversato.
Quest’ordine normalmente è basato sull’implementazione interna dei vettori
e varia da una versione di awk
alla successiva.
Spesso, tuttavia, servirebbe fare qualcosa di semplice, come
“visitare il vettore confrontando gli indici in ordine crescente,”
o “visitare il vettore confrontando i valori in ordine decrescente.”
gawk
fornisce due meccanismi che permettono di farlo.
PROCINFO["sorted_in"]
un valore a scelta fra
alcuni valori predefiniti.
Si vedano più sotto i valori ammessi.
PROCINFO["sorted_in"]
al nome di una funzione definita
dall’utente, da usare per il confronto tra gli elementi di un vettore. Questa
funzionalità avanzata verrà descritta in seguito in Ordinamento di vettori.
Sono disponibili i seguenti valori speciali per PROCINFO["sorted_in"]
:
"@unsorted"
Lasciare gli elementi del vettore in ordine arbitrario
(questo è il comportamento di default di awk
).
"@ind_str_asc"
Ordinare in ordine crescente di indice, confrontando tra loro gli indici
come stringhe; questo è l’ordinamento più normale.
(Internamente, gli indici dei vettori sono sempre stringhe, per cui con
‘a[2*5] = 1’ l’indice è la stringa "10"
e non il numero 10.)
"@ind_num_asc"
Ordinare in ordine crescente di indice, ma nell’elaborazione gli indici vengono trattati come numeri. Gli indici con valore non numerico verranno posizionati come se fossero uguali a zero.
"@val_type_asc"
Ordinare secondo il valore degli elementi in ordine crescente (invece che in base agli indici). L’ordinamento è in base al tipo assegnato all’elemento (vedi Tipi di variabile e confronti). Tutti i valori numerici precedono tutti i valori di tipo stringa, che a loro volta vengono prima dei sottovettori. (I sottovettori non sono ancora stati descritti; vedi Vettori di vettori.)
"@val_str_asc"
Ordinare secondo il valore degli elementi in ordine crescente (invece che in base agli indici). I valori scalari sono confrontati come stringhe. I sottovettori, se presenti, vengono per ultimi.
"@val_num_asc"
Ordinare secondo il valore degli elementi in ordine crescente (invece che in
base agli indici). I valori scalari sono confrontati come numeri.
I sottovettori, se presenti, vengono per ultimi.
Quando i valori numerici coincidono, vengono usati i valori di tipo stringa
per stabilire un ordinamento: ciò garantisce risultati coerenti tra differenti
versioni della funzione C qsort()
,42 che gawk
usa internamente
per effettuare l’ordinamento.
"@ind_str_desc"
Ordinare come fa "@ind_str_asc"
, ma gli
indici di tipo stringa sono ordinati dal più alto al più basso.
"@ind_num_desc"
Ordinare come fa "@ind_num_asc"
, ma gli
indici numerici sono ordinati dal più alto al più basso.
"@val_type_desc"
Ordinare come fa "@val_type_asc"
, ma i valori
degli elementi, a seconda del tipo, sono ordinati dal più alto al più basso.
I sottovettori, se presenti, vengono per primi.
"@val_str_desc"
Ordinare come fa "@val_str_asc"
, ma i valori degli
elementi, trattati come stringhe, sono ordinati dal più alto al più basso.
I sottovettori, se presenti, vengono per primi.
"@val_num_desc"
Ordinare come fa "@val_num_asc"
, ma i valori degli
elementi, trattati come numeri, sono ordinati dal più alto al più basso.
I sottovettori, se presenti, vengono per primi.
L’ordine in cui il vettore è visitato viene determinato prima di iniziare
l’esecuzione del ciclo for
. Cambiare PROCINFO["sorted_in"]
all’interno del corpo del ciclo non influisce sul ciclo stesso.
Per esempio:
$ gawk ' > BEGIN { > a[4] = 4 > a[3] = 3 > for (i in a) > print i, a[i] > }' -| 4 4 -| 3 3 $ gawk ' > BEGIN { > PROCINFO["sorted_in"] = "@ind_str_asc" > a[4] = 4 > a[3] = 3 > for (i in a) > print i, a[i] > }' -| 3 3 -| 4 4
Quando si ordina un vettore in base al valore dei suoi elementi, se viene trovato un valore che è un sottovettore, questo è considerato più grande di qualsiasi stringa o valore numerico, indipendentemente da quel che contiene lo stesso sottovettore, e tutti i sottovettori sono trattati come se fossero l’uno uguale all’altro. Il loro ordine reciproco è determinato dai loro indici, visti come stringhe.
Di seguito sono elencati alcuni punti da tener presenti sulla visita ordinata dei vettori:
PROCINFO["sorted_in"]
è globale. Cioè, ha effetto su tutti
i cicli for
relativi a qualsiasi vettore. Se si deve cambiarlo
all’interno del proprio codice, si dovrebbe vedere se era già stato
definito in precedenza, e salvare il valore relativo, per ripristinarlo
successivamente:
… if ("sorted_in" in PROCINFO) { ordine_salvato = PROCINFO["sorted_in"] PROCINFO["sorted_in"] = "@val_str_desc" # o qualcos'altro } … if (ordine_salvato) PROCINFO["sorted_in"] = ordine_salvato
"@unsorted"
. Si può ottenere il comportamento di
default anche assegnando la stringa nulla a PROCINFO["sorted_in"]
o
semplicemente eliminando l’elemento "sorted_in"
dal vettore
PROCINFO
con l’istruzione delete
.
(L’istruzione delete
non è stata ancora descritta; vedi Cancellazione.)
Inoltre, gawk
fornisce funzioni predefinite per l’ordinamento
dei vettori; si veda Funzioni di ordinamento di vettori.
Successivo: Indici non inizializzati, Precedente: Fondamenti sui vettori, Su: Vettori [Contenuti][Indice]
Un aspetto importante da ricordare riguardo ai vettori è che
gli indici dei vettori sono sempre stringhe.
Quando un valore numerico è usato come indice,
viene convertito in un valore di tipo stringa prima di essere usato per
l’indicizzazione (vedi Conversione).
Ciò vuol dire che il valore della variabile predefinita CONVFMT
può
influire su come un programma ha accesso agli elementi di un vettore.
Per esempio:
xyz = 12.153 dati[xyz] = 1 CONVFMT = "%2.2f" if (xyz in dati) printf "%s è in dati\n", xyz else printf "%s non è in dati\n", xyz
Il risultato è ‘12.15 non è in dati’. La prima istruzione dà a
xyz
un valore numerico. L’assegnamento a dati[xyz]
indicizza dati
col valore di tipo stringa "12.153"
(usando il valore di conversione di default CONVFMT
, "%.6g"
).
Quindi, all’elemento del vettore dati["12.153"]
è assegnato il valore
uno. Il programma cambia poi
il valore di CONVFMT
. La verifica ‘(xyz in dati)’ genera un nuovo
valore di stringa da xyz
—questa volta "12.15"
—perché il
valore di CONVFMT
consente solo due cifre decimali. Questa
verifica dà esito negativo, perché "12.15"
è diverso da "12.153"
.
Secondo le regole di conversione
(vedi Conversione), i valori numerici interi
vengono convertiti in stringhe sempre come interi, indipendentemente dal
valore che potrebbe avere CONVFMT
. E infatti il caso
seguente produce il risultato atteso:
for (i = 1; i <= maxsub; i++) fa qualcosa con vettore[i]
La regola “i valori numerici interi si convertono sempre in stringhe intere”
ha un’altra conseguenza per l’indicizzazione dei vettori.
Le costanti ottali ed esadecimali
(vedi Numeri non-decimali)
vengono convertite internamente in numeri, e la loro forma originale
non viene più ricordata. Ciò significa, per esempio, che
vettore[17]
,
vettore[021]
e
vettore[0x11]
fanno riferimento tutti allo stesso
elemento!
Come molte cose in awk
, molto spesso le cose
funzionano come ci si aspetta. È utile comunque avere una
conoscenza precisa delle regole applicate, poiché a volte possono avere
effetti difficili da individuare sui programmi.
Successivo: Cancellazione, Precedente: Indici numerici di vettore, Su: Vettori [Contenuti][Indice]
Supponiamo che sia necessario scrivere un programma per stampare i dati di input in ordine inverso. Un tentativo ragionevole per far ciò (con qualche dato di prova) potrebbe essere qualcosa di questo tipo:
$ echo 'riga 1 > riga 2 > riga 3' | awk '{ l[righe] = $0; ++righe } > END { > for (i = righe - 1; i >= 0; i--) > print l[i] > }' -| riga 3 -| riga 2
Sfortunatamente, la prima riga di dati in input non appare nell’output!
A prima vista, verrebbe da dire che questo programma avrebbe dovuto
funzionare. La variabile righe
non è inizializzata, e le variabili non inizializzate hanno il valore numerico
zero. Così, awk
dovrebbe aver stampato il valore l[0]
.
Qui il problema è che gli indici per i vettori di awk
sono
sempre stringhe. Le variabili non inizializzate, quando sono usate come
stringhe, hanno il valore ""
, e non zero. Quindi, ‘riga 1’
finisce per essere memorizzata in l[""]
.
La seguente variante del programma funziona correttamente:
{ l[righe++] = $0 } END { for (i = righe - 1; i >= 0; i--) print l[i] }
Qui, ‘++’ forza righe
a essere di tipo numerico, rendendo
quindi il “vecchio valore” uno zero numerico. Questo viene poi convertito
in "0"
come l’indice del vettore.
Anche se la cosa può sembrare strana, la stringa nulla
(""
) è un indice di vettore valido.
(a.b.)
Se viene fornita l’opzione --lint sulla riga di comando
vedi Opzioni), gawk
avvisa quando la stringa nulla viene usata
come indice.
Successivo: Vettori multidimensionali, Precedente: Indici non inizializzati, Su: Vettori [Contenuti][Indice]
delete
Per rimuovere un singolo elemento da un vettore si usa l’istruzione
delete
:
delete vettore[espressione-indice]
Una volta che un elemento di un vettore è stato eliminato, il valore che aveva quell’elemento non è più disponibile. È come se quell’elemento non sia mai stato referenziato oppure come se non gli sia mai stato assegnato un valore. Il seguente è un esempio di eliminazione di elementi da un vettore:
for (i in frequenze) delete frequenze[i]
Quest’esempio rimuove tutti gli elementi dal vettore frequenze
. Una
volta che un elemento è stato eliminato, una successiva istruzione for
che visiti il vettore non troverà quell’elemento, e l’uso dell’operatore
in
per controllare la presenza di quell’elemento restituisce zero (cioè
falso):
delete pippo[4] if (4 in pippo) print "Questo non verrà mai stampato"
È importante notare che eliminare un elemento non è la stessa cosa
che assegnargli un valore nullo (la stringa vuota, ""
).
Per esempio:
pippo[4] = "" if (4 in pippo) print "Questo viene stampato, anche se pippo[4] è vuoto"
Non è un errore eliminare un elemento che non esiste.
Tuttavia, se viene data l’opzione --lint sulla riga di comando
(vedi Opzioni),
gawk
emette un messaggio di avvertimento quando viene eliminato un
elemento che non è presente in un vettore.
Tutti gli elementi di un vettore possono essere eliminati con una singola
istruzione omettendo l’indice nell’istruzione delete
,
in questo modo:
delete vettore
L’uso di questa versione dell’istruzione delete
è circa tre volte più
efficiente dell’equivalente ciclo che elimina gli elementi uno
alla volta.
Questa forma dell’istruzione delete
è ammessa anche
da BWK awk
e da mawk
, e anche da
diverse altre implementazioni.
NOTA: Per molti anni, l’uso di
delete
senza un indice era un’estensione comune. A settembre 2012 si è deciso di includerla nello standard POSIX. Si veda il sito dell’Austin Group.
La seguente istruzione fornisce un modo portabile, anche se non evidente, per svuotare un vettore:43
split("", vettore)
La funzione split()
(vedi Funzioni per stringhe)
dapprima svuota il vettore indicato. La chiamata chiede di dividere
la stringa nulla. Poiché non c’è nulla da dividere, la
funzione si limita a svuotare il vettore e poi termina.
ATTENZIONE: L’eliminazione di tutti gli elementi di un vettore non cambia il suo tipo; non si può svuotare un vettore e poi usare il nome del vettore come scalare (cioè, come una variabile semplice). Per esempio, questo non è consentito:
a[1] = 3 delete a a = 3
Successivo: Vettori di vettori, Precedente: Cancellazione, Su: Vettori [Contenuti][Indice]
• Visitare vettori multidimensionali: | Visitare vettori multidimensionali. |
Un vettore multidimensionale è un vettore in cui un elemento è
identificato da un insieme di indici invece che da un indice singolo. Per
esempio, un vettore bidimenisonale richiede due indici. Il modo consueto (in
molti linguaggi, compreso awk
) per far riferimento a un elemento di
un vettore multidimensionale chiamato griglia
è con
griglia[x,y]
.
I vettori multidimensionali sono resi disponibili in awk
attraverso
la concatenazione di più indici in una stringa;
awk
converte gli indici in stringhe
(vedi Conversione) e
le concatena assieme, inserendo un separatore tra ognuna di loro. Ne
risulta una sola stringa che include i valori di ogni indice. La
stringa così composta viene usata come un singolo indice in un vettore
ordinario monodimensionale. Il separatore usato è il valore della variabile
predefinita SUBSEP
.
Per esempio, supponiamo di valutare l’espressione ‘pippo[5,12] = "valore"’
quando il valore di SUBSEP
è "@"
. I numeri 5 e 12 vengono
convertiti in stringhe che sono poi
concatenate con un ‘@’ tra di essi, dando "5@12"
; di conseguenza,
l’elemento del vettore pippo["5@12"]
è impostato a "valore"
.
Una volta che il valore dell’elemento è memorizzato, awk
ignora se sia stato memorizzato con un solo indice o con una
serie di indici. Le due espressioni ‘pippo[5,12]’ e
‘pippo[5 SUBSEP 12]’ sono sempre equivalenti.
Il valore di default di SUBSEP
è la stringa "\034"
,
che contiene un carattere non stampabile che difficilmente appare in
un programma di awk
e nella maggior parte dei dati di input.
Il vantaggio di scegliere un carattere improbabile discende dal fatto che i
valori degli indici che contengono una stringa corrispondente a SUBSEP
possono portare a stringhe risultanti ambigue. Supponendo che
SUBSEP
valga "@"
, ‘pippo["a@b", "c"]’ e
‘pippo["a", "b@c"]’ risultano indistinguibili perché entrambi
sarebbero in realtà memorizzati come ‘pippo["a@b@c"]’.
Per verificare se una determinata sequenza di indici esiste in un vettore
multidimensionale, si usa lo stesso operatore (in
) che viene usato
per i vettori monodimensionali. Si scrive l’intera sequenza di indici tra
parentesi, separati da virgole, come operando di sinistra:
if ((indice1, indice2, …) in vettore) …
Qui vediamo un esempio, avendo in input un vettore bidimensionale di campi, ruota questo vettore di 90 gradi in senso orario e stampa il risultato. Si suppone che tutte le righe in input contengano lo stesso numero di elementi:
{ if (max_nf < NF) max_nf = NF max_nr = NR for (x = 1; x <= NF; x++) vettore[x, NR] = $x } END { for (x = 1; x <= max_nf; x++) { for (y = max_nr; y >= 1; --y) printf("%s ", vettore[x, y]) printf("\n") } }
Dato l’input:
1 2 3 4 5 6 2 3 4 5 6 1 3 4 5 6 1 2 4 5 6 1 2 3
il programma produce il seguente output:
4 3 2 1 5 4 3 2 6 5 4 3 1 6 5 4 2 1 6 5 3 2 1 6
Non c’è un’istruzione for
particolare per visitare un
vettore “multidimensionale”. Non ce ne può essere una, perché
awk
in realtà non ha
vettori o elementi multidimensionali: c’è solo una modalità
multidimensionale per accedere a un vettore.
Comunque, se un programma ha un vettore al quale si accede sempre in
modalità multidimensionale, si può ottenere il risultato di visitarlo
combinando l’istruzione di visita for
(vedi Visitare un intero vettore) con la funzione
interna split()
(vedi Funzioni per stringhe).
Si procede nel seguente modo:
for (indice_combinato in vettore) { split(indice_combinato, indici_separati, SUBSEP) … }
Questo imposta la variabile indice_combinato
a ogni
concatenazione di indici contenuta nel vettore, e la suddivide
nei singoli indici separandoli
in corrispondenza del valore di
SUBSEP
. I singoli indici diventano poi gli elementi
del vettore indici_separati
.
Perciò, se un valore è stato precedentemente memorizzato in
vettore[1, "pippo"]
, esiste in vettore
un elemento con indice
"1\034pippo"
(ricordare che il valore di default di SUBSEP
è il carattere con codice ottale 034).
Prima o poi, l’istruzione for
trova quell’indice e fa un’iterazione
con la variabile indice_combinato
impostata a "1\034pippo"
.
Poi viene chiamata la funzione split()
in questo modo:
split("1\034pippo", indici_separati, "\034")
Il risultato è quello di impostare indici_separati[1]
a "1"
e
indici_separati[2]
a "pippo"
. Ecco fatto!
La sequenza originale degli indici separati è ripristinata.
Successivo: Sommario dei vettori, Precedente: Vettori multidimensionali, Su: Vettori [Contenuti][Indice]
gawk
migliora l’accesso ai vettori multidimensionali di
awk
standard e mette a disposizione dei veri vettori di vettori.
Agli elementi di un sottovettore si fa riferimento tramite il loro indice
racchiuso tra parentesi quadre, proprio come gli elementi del vettore
principale. Per esempio, quel che segue crea un sottovettore con due elementi
all’indice 1
del vettore principale a
:
a[1][1] = 1 a[1][2] = 2
Questo simula un vero vettore bidimensionale. Ogni elemento di un sottovettore
può contenere un altro sottovettore come valore, che a sua volta può
contenere anche ulteriori vettori. In questo modo, si possono creare vettori
di tre o più dimensioni.
Gli indici possono essere costituiti da qualunque espressione di
awk
, compresi dei
valori scalari separati da virgole (cioè, un indice multidimensionale simulato
di awk
). Quindi, la seguente espressione è valida in
gawk
:
a[1][3][1, "nome"] = "barney"
Ogni sottovettore e il vettore principale possono essere di diversa lunghezza.
Di fatto, gli elementi di un vettore o un suo sottovettore non devono essere
necessariamente tutti dello stesso tipo. Ciò significa che il vettore
principale come anche uno qualsiasi dei suoi sottovettori può essere
non rettangolare,
o avere una struttura frastagliata. Si può assegnare un valore scalare
all’indice 4
del vettore principale a
, anche se a[1]
è esso stesso un vettore e non uno scalare:
a[4] = "Un elemento in un vettore frastagliato"
I termini dimensione, riga e colonna sono privi di
significato quando sono applicati
a questo tipo di vettore, ma d’ora in poi useremo “dimensione” per indicare
il numero massimo di indici necessario per far riferimento a un elemento
esistente. Il tipo di ogni elemento che è già stato assegnato non può essere
cambiato assegnando un valore di tipo diverso. Prima si deve eliminare
l’elemento corrente, per togliere completamente dalla memoria di
gawk
ogni riferimento a quell’indice:
delete a[4] a[4][5][6][7] = "Un elemento in un vettore quadridimensionale"
Le due istruzioni rimuovono il valore scalare dall’indice 4
e
inseriscono poi un
sottovettore interno a tre indici contenente uno scalare. Si può anche
eliminare un intero sottovettore o un sottovettore di sottovettori:
delete a[4][5] a[4][5] = "Un elemento nel sottovettore a[4]"
Si deve però ricordare che non è consentito eliminare il vettore principale
a
e poi usarlo come scalare.
Le funzioni predefinite che accettano come argomenti dei vettori possono
essere usate
anche con i sottovettori. Per esempio, il seguente frammento di codice usa
length()
(vedi Funzioni per stringhe)
per determinare il numero di elementi nel vettore principale a
e nei suoi sottovettori:
print length(a), length(a[1]), length(a[1][3])
Il risultato per il nostro vettore principale a
è il seguente:
2, 3, 1
L’espressione ‘indice in vettore’
(vedi Visitare elementi) funziona allo stesso modo sia per
i vettori regolari in stile awk
che per i vettori di vettori. Per esempio, le espressioni ‘1 in a’,
‘3 in a[1]’ e ‘(1, "nome") in a[1][3]’ risultano tutte di valore
uno (vero) per il nostro vettore a
.
L’istruzione ‘for (elemento in vettore)’ (vedi Visitare un intero vettore) può essere nidificata per visitare tutti gli elementi di un vettore di vettori che abbia una struttura rettangolare. Per stampare il contenuto (valori scalari) di un vettore di vettori bidimensionale (cioè nel quale ogni elemento di primo livello è esso stesso un vettore, non necessariamente di lunghezza uguale agli altri) si può usare il seguente codice:
for (i in vettore) for (j in vettore[i]) print vettore[i][j]
La funzione isarray()
(vedi Funzioni per i tipi)
permette di verificare se un elemento di un vettore è esso stesso un vettore:
for (i in vettore) { if (isarray(vettore[i]) { for (j in vettore[i]) { print vettore[i][j] } } else print vettore[i] }
Se la struttura di un vettore di vettori frastagliato è nota in anticipo,
si può spesso trovare il modo per visitarlo usando istruzioni di controllo.
Per esempio,
il seguente codice stampa gli elementi del nostro vettore principale a
:
for (i in a) { for (j in a[i]) { if (j == 3) { for (k in a[i][j]) print a[i][j][k] } else print a[i][j] } }
Vedi Visitare vettori per una funzione definita dall’utente che “visita” un vettore di vettori di dimensioni arbitrarie.
Si ricordi che un riferimento a un elemento di un vettore non
inizializzato genera un elemento con valore uguale a ""
, la stringa
nulla. Questo ha
un’importante implicazione quando s’intende usare un sottovettore come
argomento di una funzione, come illustrato nel seguente esempio:
$ gawk 'BEGIN { split("a b c d", b[1]); print b[1][1] }' error→ gawk: riga com.:1: fatale: split: secondo argomento error→ non-vettoriale
Il modo per aggirare quest’ostacolo è quello di definire prima b[1]
come vettore creando un indice arbitrario:
$ gawk 'BEGIN { b[1][1] = ""; split("a b c d", b[1]); print b[1][1] }' -| a
Precedente: Vettori di vettori, Su: Vettori [Contenuti][Indice]
awk
standard dispone di vettori associativi monodimensionali
(vettori indicizzati da valori di tipo stringa). Tutti i vettori sono
associativi; gli indici numerici vengono convertiti automaticamente in
stringhe.
vettore[indice]
. Fare riferimento a un elemento lo
crea se questo non esiste ancora.
in
: ‘indice in vettore’.
awk
e varia a seconda
dell’implementazione. gawk
consente di controllare l’ordinamento
di visita
assegnando speciali valori predefiniti a PROCINFO["sorted_in"]
.
awk
.
awk
standard simula vettori multidimensionali ammettendo più indici
separati da virgole. I loro valori sono concatenati in un’unica
stringa, separati dal valore di SUBSEP
. Il modo di creazione
dell’indice non viene immagazzinato; così,
cambiare SUBSEP
potrebbe avere conseguenze inaspettate. Si può usare
‘(sub1, sub2, …) in vettore’ per vedere se
un certo indice multidimensionale esiste in vettore.
gawk
consente di avere a disposizione veri vettori di vettori.
Si usa una coppia
di parentesi quadre per ogni dimensione in tali vettori:
dati[riga][colonna]
, per esempio. Gli elementi del vettore possono
poi essere valori scalari (numeri o stringhe) o altri vettori.
isarray()
per determinare se un elemento
di un vettore è esso stesso un sottovettore.
Successivo: Funzioni di libreria, Precedente: Vettori, Su: Top [Contenuti][Indice]
Questo capitolo descrive le funzioni predefinite di awk
,
che sono di tre tipi: numeriche, di stringa, e di I/O.
gawk
mette a disposizione ulteriori tipi di funzioni
per gestire valori che rappresentano marcature temporali, per manipolare bit, per
ordinare vettori, per fornire informazioni sui tipi di variabile,
per internazionalizzare e localizzare i programmi.44
Oltre alle funzioni predefinite, awk
consente di
scrivere nuove funzioni utilizzabili all’interno di un programma.
La seconda metà di questo capitolo descrive le funzioni
definite dall’utente.
Vengono infine descritte le chiamate indirette a una funzione, un’estensione
specifica di gawk
che consente di stabilire durante l’esecuzione del
programma quale funzione chiamare.
• Funzioni predefinite: | Riepilogo delle funzioni predefinite. | |
• Funzioni definite dall'utente: | Descrizione dettagliata delle funzioni definite dall’utente. | |
• Chiamate indirette: | Scegliere la funzione da chiamare in fase di esecuzione del programma. | |
• Sommario delle funzioni: | Sommario delle funzioni. |
Successivo: Funzioni definite dall'utente, Su: Funzioni [Contenuti][Indice]
Le funzioni predefinite sono sempre disponibili per essere chiamate
da un programma awk
. Questa sezione definisce tutte le
funzioni predefinite di awk
; di alcune di queste si fa menzione
in altre sezioni,
ma sono comunque riassunte anche qui per comodità.
• Chiamare funzioni predefinite: | Come chiamare funzioni predefinite. | |
• Funzioni numeriche: | Funzioni che trattano numeri, comprese
int() , sin() e rand() .
| |
• Funzioni per stringhe: | Funzioni di manipolazione di stringhe,
come split() , match()
e sprintf() .
| |
• Funzioni di I/O: | Funzioni per i file e per i comandi della shell. | |
• Funzioni di tempo: | Funzione per gestire marcature temporali. | |
• Funzioni a livello di bit: | Funzioni per operazioni di manipolazione bit. | |
• Funzioni per i tipi: | Funzioni per informazioni sul tipo di una variabile. | |
• Funzioni di internazionalizzazione: | Funzioni per tradurre stringhe. |
Successivo: Funzioni numeriche, Su: Funzioni predefinite [Contenuti][Indice]
Per chiamare una delle funzioni predefinite di awk
,
si scrive il nome della funzione seguito dai suoi argomenti racchiusi
tra parentesi. Per esempio, ‘atan2(y + z, 1)’
è una chiamata alla funzione atan2()
e ha due argomenti.
La presenza di spazi bianchi tra il nome della funzione predefinita e la parentesi aperta è consentita, ma è buona norma quella di evitare di inserire spazi bianchi in quella posizione. Le funzioni definite dall’utente non consentono che vi siano spazi bianchi fra nome funzione e aperta parentesi, ed è più semplice evitare errori seguendo una semplice convenzione che resta sempre valida: non inserire spazi dopo il nome di una funzione.
Ogni funzione predefinita accetta un certo numero di argomenti.
In alcuni casi, gli argomenti possono essere omessi. I valori di default per
gli argomenti omessi variano
da funzione a funzione e sono descritti insieme a
ciascuna funzione. In alcune implementazioni di awk
, gli
eventuali argomenti in più specificati per le funzioni predefinite sono
ignorati. Tuttavia, in gawk
,
è un errore fatale fornire argomenti in più a una funzione predefinita.
Quando si richiama una funzione viene calcolato, prima di effettuare la chiamata, il valore assunto dalle espressioni che descrivono i parametri da passare alla funzione. Per esempio, nel seguente frammento di codice:
i = 4 j = sqrt(i++)
la variabile i
è incrementata al valore cinque prima di chiamare
la funzione sqrt()
alla quale viene fornito come parametro il valore
quattro.
L’ordine di valutazione delle espressioni usate come parametri per la
funzione è indefinito. Per questo motivo, si deve evitare di scrivere
programmi che presuppongono che i parametri siano valutati da sinistra a
destra o da destra a sinistra. Per esempio:
i = 5 j = atan2(++i, i *= 2)
Se l’ordine di valutazione è da sinistra a destra, i
assume dapprima
il valore 6, e quindi il valore 12, e la funzione atan2()
è chiamata
con i due argomenti 6 e 12. Ma se l’ordine di valutazione è da destra a
sinistra, i
assume dapprima il valore 10, e poi il valore 11, e la
funzione atan2()
è chiamata con i due argomenti 11 e 10.
Successivo: Funzioni per stringhe, Precedente: Chiamare funzioni predefinite, Su: Funzioni predefinite [Contenuti][Indice]
La seguente lista descrive tutte le funzioni predefinite che hanno a che fare con i numeri. I parametri facoltativi sono racchiusi tra parentesi quadre ([ ]):
atan2(y, x)
Restituisce l’arcotangente di y / x
in radianti.
Si può usare ‘pi = atan2(0, -1)’ per ottenere il valore di
pi greco.
cos(x)
Restituisce il coseno di x, con x in radianti.
exp(x)
Restituisce l’esponenziale di x (e ^ x
) o un messaggio
di errore se x è fuori dall’intervallo consentito.
L’intervallo entro il quale può variare x
dipende dalla rappresentazione dei numeri in virgola mobile nella macchina in
uso.
int(x)
Restituisce l’intero più vicino a x, situato tra x e zero,
troncato togliendo i decimali.
Per esempio, int(3)
è 3, int(3.9)
è 3, int(-3.9)
è -3, e int(-3)
è ancora -3.
intdiv(numeratore, denominatore, risultato)
Esegue una divisione tra numeri interi, simile alla funzione standard C
che ha lo stesso nome. Dapprima, il numeratore
e il
denominatore
vengono troncati, eliminando la parte decimale,
per trasformarli in numeri interi.
Il vettore risultato
viene dapprima svuotato, e poi viene impostato
l’elemento risultato["quotient"]
al risultato della divisione
‘numeratore / denominatore’, troncato a numero intero
mediante l’eliminazione dei decimali,
e viene impostato l’elemento risultato["remainder"]
al
risultato dell’operazione ‘numeratore % denominatore’, troncato a
numero intero allo stesso modo del risultato. Questa funzione è
rivolta principalmente a chi usa numeri interi di lunghezza arbitraria;
consente di evitare la creazione di numeri in virgola mobile
di precisione arbitaria usando la funzionalità MPFR
(vedi Interi a precisione arbitraria).
Questa funzione è un’estensione gawk
. Non è disponibile in
modalità compatibile (vedi Opzioni).
log(x)
Restituisce il logaritmo naturale di x, se x è positivo;
altrimenti, restituisce NaN
(“not a number”) sui sistemi che
implementano lo standard IEEE 754.
Inoltre, gawk
stampa un messaggio di avvertimento qualora x
sia negativo.
rand()
Restituisce un numero casuale. I valori di rand()
sono
uniformemente distribuiti tra zero e uno.
Il valore potrebbe essere zero ma non è mai uno.45
Spesso servono dei numeri casuali interi invece che frazionari. La seguente funzione definita dall’utente può essere usata per ottenere un numero casuale non negativo inferiore a n:
function randint(n) { return int(n * rand()) }
La moltiplicazione produce un numero casuale maggiore o uguale a zero e
minore di n
. Tramite int()
, questo risultato diventa
un intero tra zero e n
- 1, estremi inclusi.
Il seguente esempio usa una funzione simile per generate interi casuali fra uno e n. Il programma stampa un numero casuale per ogni record in input:
# funzione per simulare un tiro di dado. function roll(n) { return 1 + int(rand() * n) } # Tira 3 dadi a sei facce e # stampa il numero di punti. { printf("%d punteggio\n", roll(6) + roll(6) + roll(6)) }
ATTENZIONE: Nella maggior parte delle implementazioni di
awk
, compresogawk
,rand()
inizia a generare numeri casuali partendo sempre dallo stesso numero, o seme, per ogni invocazione diawk
.46 È per questo motivo che un programma genera sempre gli stessi risultati ogni volta che lo si esegue. I numeri sono casuali all’interno di una singola esecuzione diawk
ma "prevedibili" in ogni successiva esecuzione. Ciò torna utile in fase di test, ma se si desidera che un programma generi sequenze differenti di numeri casuali ogni volta che è chiamato, occorre impostare il seme a un valore che cambi per ogni esecuzione. Per fare questo, è prevista la funzionesrand()
.
sin(x)
Restituisce il seno di x, con x espresso in radianti.
sqrt(x)
Restituisce la radice quadrata positiva di x.
gawk
stampa un messaggio di avvertimento
se x è un numero negativo. Quindi, sqrt(4)
vale 2.
srand(
[x])
Imposta al valore x il numero di partenza, o seme, utilizzato per generare numeri casuali.
Ogni seme genera una sequenza particolare di numeri casuali.47 Quindi, impostando il seme allo stesso valore una seconda volta, viene prodotta ancora la stessa sequenza di numeri casuali.
ATTENZIONE: Differenti implementazioni di
awk
usano internamente differenti generatori di numeri casuali. Non si deve dare per scontato che lo stesso programmaawk
generi la stessa serie di numeri casuali se viene eseguito da differenti versioni diawk
.
Se si omette l’argomento x, scrivendo ‘srand()’, viene usato come seme la data e ora corrente. È questo il modo per ottenere numeri casuali che sono veramente imprevedibili.
Il valore restituito da srand()
è quello del seme precedente.
Questo per facilitare il monitoraggio dei semi, nel caso occorra riprodurre
in maniera coerente delle sequenze di numeri casuali.
POSIX non specifica quale debba essere il seme iniziale, che quindi varia
a seconda delle implementazioni awk
.
Successivo: Funzioni di I/O, Precedente: Funzioni numeriche, Su: Funzioni predefinite [Contenuti][Indice]
Le funzioni in questa sezione leggono o modificano il testo di una o più stringhe.
gawk
implementa la localizzazione
(vedi Localizzazioni) ed effettua
ogni manipolazione di stringhe trattando ogni singolo carattere, non
ogni singolo byte.
Questa distinzione è particolarmente importante da comprendere per
quelle localizzazioni in cui un singolo carattere può essere rappresentato
da più di un byte.
Quindi, per esempio, la funzione length()
restituisce il numero di
caratteri in una stringa, e non il numero di byte usato per rappresentare quei
caratteri. Allo stesso modo, index()
restituisce indici di caratteri, e
non indici di byte.
ATTENZIONE: Un certo numero di funzioni riguarda indici all’interno di stringhe. Per queste funzioni, il primo carattere di una stringa è alla posizione (all’indice) uno. Questo comportamento è differente da quello del C e dei linguaggi che da esso discendono, nei quali il primo carattere è alla posizione zero. È importante ricordarlo quando si fanno calcoli sugli indici, in particolare se si ha familiarità con il linguaggio C.
Nella lista seguente, i parametri facoltativi sono racchiusi tra parentesi
quadre ([ ]).
Parecchie funzioni operano sostituzioni in una stringa; la spiegazione
completa di ciò è contenuta nella descrizione della funzione sub()
,
che si trova quasi alla fine di questa lista, ordinata alfabeticamente.
Le funzioni specifiche di gawk
sono contrassegnate col simbolo
del cancelletto (‘#’). Tali funzioni non sono disponibili in modalità
compatibile (vedi Opzioni):
• Dettagli ostici: | Più di quel che si vorrebbe sapere su ‘\’
e ‘&’ con sub() , gsub() , e
gensub() .
|
asort(
sorgente [,
destinazione [,
come ] ]) #
asorti(
sorgente [,
destinazione [,
come ] ]) #
Queste due funzioni sono abbastanza simili, e quindi sono descritte insieme.
NOTA: La seguente descrizione ignora il terzo argomento, come, perché richiede la conoscenza di funzionalità di cui non si è ancora parlato. Per questo motivo la seguente trattazione è volutamente semplificata. (In seguito l’argomento verrà trattato in maniera più esauriente; si veda Funzioni di ordinamento di vettori per la descrizione completa.)
Entrambe le funzioni restituiscono il numero di elementi nel vettore sorgente.
Con asort()
, gawk
ordina i valori di sorgente
e rimpiazza gli indici dei valori ordinati di sorgente con
numeri interi sequenziali, a partire da uno. Se si specifica il vettore
opzionale destinazione,
sorgente è copiato in destinazione. destinazione
viene quindi ordinato, lasciando immodificati gli indici di sorgente.
Nel confronto tra stringhe, la variabile IGNORECASE
influenza
l’ordinamento
(vedi Funzioni di ordinamento di vettori). Se il vettore
sorgente contiene sottovettori come valori
(vedi Vettori di vettori), questi saranno alla fine, dopo tutti i valori
scalari.
I sottovettori non vengono ordinati ricorsivamente.
Per esempio, se i contenuti del vettore a
sono i seguenti:
a["ultimo"] = "de" a["primo"] = "sac" a["mediano"] = "cul"
Una chiamata a asort()
:
asort(a)
genera i seguenti contenuti di a
:
a[1] = "cul" a[2] = "de" a[3] = "sac"
La funzione asorti()
si comporta in maniera simile ad asort()
;
tuttavia l’ordinamento avviene in base agli indici, e non in base ai
valori. Quindi, nell’esempio seguente, a partire dallo stesso insieme iniziale
di indici e valori nel vettore a
, la chiamata di ‘asorti(a)’
produrrebbe:
a[1] = "mediano" a[2] = "primo" a[3] = "ultimo"
gensub(regexp, rimpiazzo, come
[, obiettivo
]) #
Ricerca nella stringa obiettivo delle corrispondenze
all’espressione regolare regexp.
Se come è una stringa che inizia
con ‘g’ o ‘G’ (abbreviazione di “global”), sostituisce
ogni occorrenza di regexp con la stringa
rimpiazzo. Altrimenti, come è visto come un numero che indica
quale corrispondenza di regexp va rimpiazzata. Se non si specifica
il nome dell’obiettivo, si
opera su $0
. La funzione restituisce come risultato la stringa
modificata, e la stringa originale di partenza non viene modificata.
gensub()
è una funzione generale di sostituzione. Mira a fornire
più funzionalità rispetto alle funzioni standard sub()
e
gsub()
.
gensub()
prevede una funzionalità ulteriore, non disponibile in
sub()
o gsub()
: la possibilità di specificare componenti di
una regexp nel testo da sostituire. Questo è fatto utilizzando delle
parentesi nella regexp per designare i componenti, e quindi inserendo
‘\N’ nel testo di rimpiazzo, dove N è una cifra da 1 a 9.
Per esempio:
$ gawk ' > BEGIN { > a = "abc def" > b = gensub(/(.+) (.+)/, "\\2 \\1", "g", a) > print b > }' -| def abc
Come con sub()
, occorre battere due barre inverse, per ottenerne
una come componente della stringa.
Nel testo di rimpiazzo, la sequenza ‘\0’ rappresenta l’intero testo
corrispondente, e lo stesso vale per
il carattere ‘&’.
Il seguente esempio mostra come è possibile usare il terzo argomento per controllare quale corrispondenza della regexp sia da modificare:
$ echo a b c a b c | > gawk '{ print gensub(/a/, "AA", 2) }' -| a b c AA b c
In questo caso, $0
è la stringa obiettivo di default.
gensub()
restituisce la nuova stringa come risultato, e questa
è passata direttamente a print
per essere stampata.
Se l’argomento come è una stringa che non inizia con ‘g’ o
‘G’, o se è un numero minore o uguale a zero, si effettua solo una
sostituzione. Se come è zero, gawk
emette
un messaggio di avvertimento.
Se regexp non viene trovata in obiettivo, il valore
restituito da gensub()
è il valore originale e non modificato di obiettivo.
gsub(regexp, rimpiazzo
[, obiettivo
])
Ricerca in obiettivo
tutte le sottostringhe corrispondenti al criterio di ricerca, le
più lunghe possibili partendo da sinistra, non sovrapposte tra loro,
e le sostituisce con rimpiazzo.
La lettera ‘g’ in gsub()
significa
“global”, e richiede di sostituire dappertutto. Per esempio:
{ gsub(/Inghilterra/, "Regno Unito"); print }
sostituisce tutte le occorrenze della stringa ‘Inghilterra’ con ‘Regno Unito’ in tutti i record in input.
La funzione gsub()
restituisce il numero di sostituzioni effettuate.
Se la variabile da cercare e modificare (obiettivo) è omessa,
viene usato l’intero record in input.
Come in sub()
, i caratteri ‘&’ e ‘\’ sono speciali,
e il terzo argomento dev’essere modificabile.
index(dove, cosa)
Ricerca nella stringa dove la prima occorrenza della stringa cosa, e restituisce la posizione in caratteri dell’inizio di quest’occorrenza nella stringa dove. Si consideri il seguente esempio:
$ awk 'BEGIN { print index("noccioline", "oli") }' -| 6
Se cosa non viene trovato, index()
restituisce zero.
In BWK awk
e gawk
,
è un errore fatale usare una costante regexp per cosa.
Altre implementazioni lo consentono, considerando semplicemente
la costante regexp come un’espressione che significa
‘$0 ~ /regexp/’. (a.b.)
length(
[stringa])
Restituisce il numero di caratteri in stringa. Se
stringa è un numero, viene restituita la lunghezza della stringa
di cifre che rappresenta quel numero. Per esempio, length("abcde")
è
cinque.
Invece, length(15 * 35)
restituisce tre. In questo esempio,
15 * 35 = 525,
e 525 è quindi convertito alla stringa "525"
, che è composta da
tre caratteri.
Se non si specifica alcun argomento, length()
restituisce la
lunghezza di $0
.
NOTA: In alcune delle prime versioni di
awk
, la funzionelength()
poteva essere richiamata senza alcuna parentesi. Farlo è considerata una cattiva abitudine, sebbene il POSIX standard 2008 lo consenta esplicitamente, per compatibilità con la vecchia prassi. Per garantire la massima portabilità ai programmi, è meglio mettere sempre le parentesi.
Se length()
è chiamata con una variabile che non è stata usata,
gawk
considera la variabile come uno scalare. Altre
implementazioni di awk
non assegnano nessun tipo alla variabile.
(a.b.)
Si consideri:
$ gawk 'BEGIN { print length(x) ; x[1] = 1 }' -| 0 error→ gawk: riga com.:1: fatale: tentativo di usare error→ scalare 'x' come vettore $ nawk 'BEGIN { print length(x) ; x[1] = 1 }' -| 0
Se --lint è
stato specificato sulla riga di comando, gawk
emette un
avvertimento a questo riguardo.
In gawk
e in parecchie altre implementazioni awk
,
se l’argomento è un vettore, la funzione length()
restituisce il numero
di elementi nel vettore. (e.c.)
Ciò è meno utile di quel che sembra a prima vista, in quanto
non è affatto detto che il vettore abbia come indici i numeri da 1 al
numero di elementi che contiene.
Se --lint è
stato specificato sulla riga di comando,
(vedi Opzioni),
gawk
avvisa che l’uso di un vettore come argomento non
è portabile.
Se si specifica l’opzione --posix, l’uso di un vettore come
argomento genera un errore fatale
(vedi Vettori).
match(stringa, regexp
[, vettore
])
Ricerca in stringa la sottostringa più lunga, a partire da sinistra, che corrisponde all’espressione regolare regexp e restituisce la posizione del carattere (indice) con cui inizia la sottostringa (uno, se la corrispondenza parte dall’inizio di stringa). Se non viene trovata alcuna corrispondenza, restituisce zero.
L’argomento regexp può essere sia una costante regexp
(/
…/
) che una costante stringa ("
…"
).
In quest’ultimo caso, la stringa è trattata come una regexp
per la quale cercare una corrispondenza.
Vedi Espressioni regolari calcolate per una
spiegazione sulla differenza tra le due forme e sulle loro
implicazioni riguardo al modo per scrivere correttamente un programma.
L’ordine dei primi due argomenti è l’opposto di molte altre funzioni che
trattano stringhe e che hanno a che fare con espressioni regolari, come
sub()
e gsub()
. Potrebbe essere di aiuto ricordare che
per match()
, l’ordine è lo stesso che per l’operatore ‘~’ :
‘stringa ~ regexp’.
La funzione match()
imposta la variabile predefinita RSTART
all’indice.
Imposta anche la variabile predefinita RLENGTH
alla
lunghezza in caratteri della sottostringa individuata. Se non viene
trovata alcuna corrispondenza, RSTART
è impostata a zero, e
RLENGTH
a -1.
Per esempio:
{ if ($1 == "TROVA") regexp = $2 else { dove = match($0, regexp) if (dove != 0) print "Corrispondenza di", regexp, "alla posiz.", \ dove, "in", $0 } }
Questo programma ricerca delle righe che corrispondono all’espressione
regolare contenuta nella variabile
regexp
. Quest’espressione regolare può essere modificata. Se la
prima parola in una riga è ‘TROVA’, regexp
diventa la
seconda parola su quella riga. Quindi, dato:
TROVA or+e Il mio programma corre ma non troppo velocemente TROVA Melvin JF+KM Questa riga appartiene a Reality Engineering Co. Melvin è passato da qui.
awk
stampa:
Corrispondenza di or+e alla posiz. 19 in Il mio programma corre Corrispondenza di Melvin alla posiz. 1 in Melvin è passato da qui.
Se vettore esiste già, viene cancellato, e quindi l’elemento numero zero di vettore è impostato all’intera parte di stringa individuata da regexp. Se regexp contiene parentesi, gli elementi aventi per indici numeri interi in vettore sono impostati per contenere ognuno la parte di stringa individuata dalla corrispondente sottoespressione delimitata da parentesi. Per esempio:
$ echo pippoooopaperpluttttttt | > gawk '{ match($0, /(pippo+).+(plut*)/, vett) > print vett[1], vett[2] }' -| pippoooo pluttttttt
Inoltre, sono disponibili indici multidimensionali che contengono la posizione di partenza e la lunghezza di ogni sottoespressione individuata:
$ echo pippoooopaperpluttttttt | > gawk '{ match($0, /(pippo+).+(plut*)/, vett) > print vett[1], vett[2] > print vett[1, "start"], vett[1, "length"] > print vett[2, "start"], vett[2, "length"] > }' -| pippoooo pluttttttt -| 1 8 -| 14 10
Possono non esserci indici che individuino inizio e posizione
per ogni sottoespressione
fra parentesi, perché non tutte potrebbero aver individuato del testo;
quindi, andrebbero esaminati usando l’operatore in
(vedi Visitare elementi).
L’argomento vettore di match()
è un’estensione
gawk
. In modalità compatibile
(vedi Opzioni),
l’impiego di un terzo argomento causa un errore fatale.
patsplit(stringa, vettore
[, regexpdelim
[, separatori
] ]) #
Divide
stringa in parti definite da regexpdelim
e memorizza i pezzi in vettore e le stringhe di separazione nel
vettore separatori. Il primo pezzo è memorizzato in
vettore[1]
, il secondo pezzo in vettore[2]
, e
così via. Il terzo argomento, regexpdelim, è
una regexp che descrive i campi in stringa (allo stesso modo in
cui FPAT
è una regexp che descrive i campi nei record
in input).
Può essere una costante regexp o una stringa.
Se regexpdelim è omesso, viene usato il valore di FPAT
.
patsplit()
restituisce il numero di elementi creati.
separatori[i]
è
la stringa che separa
l’elemento vettore[i]
e vettore[i+1]
.
Ogni separatore iniziale sarà in separatori[0]
.
La funzione patsplit()
divide delle stringhe in pezzi in modo
simile a quello con cui le righe in input vengono divise in campi
usando FPAT
(vedi Separazione in base al contenuto).
Prima di dividere la stringa, patsplit()
cancella ogni elemento che
fosse eventualmente presente
nei vettori vettore e separatori.
split(stringa, vettore
[, separacampo
[, separatori
] ])
Divide stringa in pezzi separati da separacampo
e memorizza i pezzi in vettore e le stringhe di separazione nel
vettore separatori. Il primo pezzo è memorizzato in
vettore[1]
, il secondo pezzo in vettore[2]
, e
così via. Il valore della stringa specificata nel terzo argomento,
separacampo, è una regexp che indica come dividere stringa
(analogamente a come FS
può essere un regexp che indica dove
dividere i record in input).
Se separacampo è omesso, si usa il valore di FS
.
split()
restituisce il numero di elementi creati.
separatori è un’estensione gawk
, in cui
separatori[i]
è la stringa che separa vettore[i]
e
vettore[i+1]
.
Se separacampo è uno spazio bianco, ogni eventuale spazio bianco
a inizio stringa viene messo in separatori[0]
e ogni
eventuale spazio bianco a fine stringa viene messo in
separatori[n]
, dove n è il valore restituito da
split()
(cioè il numero di elementi in vettore).
La funzione split()
divide le stringhe in pezzi in modo simile
a quello con cui le righe in input sono divise in campi. Per esempio:
split("cul-de-sac", a, "-", separatori)
divide la stringa "cul-de-sac"
in tre campi usando ‘-’ come
separatore. Il vettore a
ha i seguenti contenuti:
a[1] = "cul" a[2] = "de" a[3] = "sac"
e imposta il contenuto del vettore separatori
come segue:
seps[1] = "-" seps[2] = "-"
Il valore restituito da questa chiamata a split()
è tre.
Come nella divisione in campi dei record in input, quando il valore di
separacampo è " "
, gli spazi bianchi a inizio e fine stringa
vengono ignorati nell’assegnare valori agli elementi di vettore ma non nel
vettore separatori, e gli elementi sono separati da uno o più spazi
bianchi. Inoltre, come nel caso della divisione dei record in input, se
separacampo è la stringa nulla, ogni singolo carattere nella stringa
costituisce un elemento del vettore.
(e.c.)
Si noti, tuttavia, che RS
non influisce sul comportamento di
split()
.
Anche se ‘RS = ""’ fa sì che il carattere di ritorno a capo sia un
separatore di campo,
questo non influenza il modo in cui split()
divide le stringhe.
Recenti implementazioni di awk
, incluso gawk
,
consentono che il terzo argomento sia una costante regexp
(/
…/
)
o anche una stringa. (a.b.)
Anche lo standard POSIX permette questo.
Vedi Espressioni regolari calcolate per la spiegazione della differenza
tra l’uso di una costante stringa e l’uso di una costante regexp,
sulle loro implicazioni riguardo a come scrivere correttamente un programma.
Prima di dividere la stringa, split()
cancella ogni elemento
eventualmente già presente
nei vettori vettore e separatori.
Se stringa è la stringa nulla, il vettore non ha elementi. (Quindi, in questo modo si può cancellare un intero vettore con una sola istruzione). Vedi Cancellazione.)
Se in stringa non viene trovato separacampo (ma la stringa non è la stringa nulla), vettore ha solo un elemento. Il valore di quell’elemento è la stringa originale.
In modalità POSIX (vedi Opzioni), il quarto argomento non è disponibile.
sprintf(formato, espressione1, …)
Restituisce (senza stamparla) la stringa che printf
avrebbe
stampato con gli stessi argomenti
(vedi Printf).
Per esempio:
pival = sprintf("pi = %.2f (approx.)", 22/7)
assegna la stringa ‘pi = 3.14 (approx.)’ alla variabile pival
.
strtonum(stringa) #
Esamina stringa e restituisce il suo valore numerico. Se
stringa inizia con la cifra ‘0’, strtonum()
presuppone
che stringa sia un numero ottale. Se stringa inizia con
‘0x’ o ‘0X’, strtonum()
presuppone che stringa sia un
numero esadecimale.
Per esempio:
$ echo 0x11 | > gawk '{ printf "%d\n", strtonum($1) }' -| 17
Usare la funzione strtonum()
non è lo stesso che aggiungere
zero al valore di una stringa;
la conversione automatica di stringhe in numeri
si applica solo a dati decimali, non a quelli ottali o
esadecimali.48
Si noti anche che strtonum()
usa il separatore decimale della
localizzazione corrente per riconoscere i numeri
(vedi Localizzazioni).
sub(regexp, rimpiazzo
[, obiettivo
])
Ricerca in obiettivo, che è visto come una stringa, la prima sottostringa più lunga possibile, a partire da sinistra, che corrisponde all’espressione regolare regexp. Modifica l’intera stringa sostituendo il testo individuato con rimpiazzo. La stringa così modificata diventa il nuovo valore di obiettivo. Restituisce il numero di sostituzioni fatte (zero o uno).
L’argomento regexp può essere o una costante regexp
(/
…/
) o una constante stringa ("
…"
).
In quest’ultimo caso, la stringa è trattata come una regexp
da individuare.
Vedi Espressioni regolari calcolate per la spiegazione della differenza tra
le due forme, delle loro implicazioni riguardo al modo di scrivere
correttamente un programma.
Questa funzione è particolare perché obiettivo non è semplicemente usato
per calcolare un valore, e non basta che sia un’espressione qualsiasi:
dev’essere una variabile, un campo, o un elemento di vettore in cui
sub()
possa memorizzare un valore modificato. Se questo argomento è
omesso, il comportamento di default è
quello di usare e modificare
$0
.49 Per
esempio:
str = "acqua, acqua dappertutto" sub(/cqu/, "vari", str)
modifica stringa
facendola divenire
‘avaria, acqua dappertutto’,
rimpiazzando l’occorrenza più lunga,
a partire da sinistra, di ‘cqu’ con ‘vari’.
Se il carattere speciale ‘&’ compare in rimpiazzo, designa l’esatta sottostringa individuata da regexp. (Se regexp può individuare più di una stringa, questa sottostringa può assumere valori diversi.) Per esempio:
{ sub(/candidato/, "& e sua moglie"); print }
cambia la prima occorrenza di ‘candidato’ a ‘candidato e sua moglie’ in ogni riga in input. Ecco un altro esempio:
$ awk 'BEGIN { > str = "daabaaa" > sub(/a+/, "C&C", str) > print str > }' -| dCaaCbaaa
questo mostra come ‘&’ possa rappresentare una stringa variabile e illustra anche la regola “a partire da sinistra, la più lunga” nell’individuazione di regexp (vedi Più lungo da sinistra).
L’effetto di questo carattere speciale (‘&’) può essere neutralizzato anteponendogli una barra inversa nella stringa. Come al solito, per inserire una barra inversa nella stringa, occorre scrivere due barre inverse. Quindi, occorre scrivere ‘\\&’ in una costante stringa per includere un carattere ‘&’ nel rimpiazzo. Per esempio, quanto segue mostra come rimpiazzare il primo ‘|’ su ogni riga con un ‘&’:
{ sub(/\|/, "\\&"); print }
Come già accennato, il terzo argomento di sub()
dev’essere
una variabile, un campo, o un elemento di vettore.
Alcune versioni di awk
accettano come terzo argomento
un’espressione che non è un lvalue. In tal caso, sub()
cerca ugualmente l’espressione e restituisce zero o uno, ma il risultato
della sostituzione (se ce n’è uno) viene scartato perché non c’è un posto
dove memorizzarlo. Tali versioni di awk
accettano espressioni
come le seguente:
sub(/USA/, "Stati Uniti", "gli USA e il Canada")
Per compatibilità storica, gawk
accetta un tale codice erroneo.
Tuttavia, l’uso di qualsiasi altra espressione non modificabile
come terzo parametro causa un errore fatale, e il programma
non viene portato a termine.
Infine, se la regexp non è una costante regexp, è convertita in una stringa, e quindi il valore di quella stringa è trattato come la regexp da individuare.
substr(stringa, inizio
[, lunghezza
])
Restituisce una sottostringa di stringa lunga lunghezza caratteri,
iniziando dal carattere numero inizio. Il primo carattere di una
stringa è il carattere numero uno.50
Per esempio, substr("Washington", 5, 3)
restituisce "ing"
.
Se lunghezza non è presente, substr()
restituisce l’intero
suffisso di
stringa a partire dal carattere numero inizio. Per esempio,
substr("Washington", 5)
restituisce "ington"
. L’intero
suffisso è restituito anche
se lunghezza è maggiore del numero di caratteri disponibili
nella stringa, a partire dal carattere inizio.
Se inizio è minore di uno, substr()
lo tratta come se
fosse uno. (POSIX non specifica cosa fare in questo caso:
BWK awk
si comporta così, e quindi gawk
fa lo stesso.)
Se inizio è maggiore del numero di caratteri
nella stringa, substr()
restituisce la stringa nulla.
Analogamente, se lunghezza è presente ma minore o uguale a zero,
viene restituita la stringa nulla.
La stringa restituita da substr()
non può essere
assegnata. Quindi, è un errore tentare di modificare una porzione di
una stringa, come si vede nel seguente esempio:
stringa = "abcdef" # tentare di ottenere "abCDEf", non è possibile substr(stringa, 3, 3) = "CDE"
È anche un errore usare substr()
come terzo argomento
di sub()
o gsub()
:
gsub(/xyz/, "pdq", substr($0, 5, 20)) # SBAGLIATO
(Alcune versioni commerciali di awk
consentono un tale uso di
substr()
, ma un tale codice non è portabile.)
Se si devono sostituire pezzi di una stringa,
si combini substr()
con una concatenazione di stringa, nel modo seguente:
stringa = "abcdef" … stringa = substr(stringa, 1, 2) "CDE" substr(stringa, 6)
tolower(stringa)
Restituisce una copia di stringa, con ogni carattere maiuscolo
nella stringa rimpiazzato dal suo corrispondente carattere minuscolo.
I caratteri non alfabetici non vengono modificati. Per esempio,
tolower("MaIuScOlO MiNuScOlO 123")
restituisce
"maiuscolo minuscolo 123"
.
toupper(stringa)
Restituisce una copia di stringa, con ogni carattere minuscolo
nella stringa rimpiazzato dal suo corrispondente carattere maiuscolo.
I caratteri non alfabetici non vengono modificati. Per esempio,
tolower("MaIuScOlO MiNuScOlO 123")
restituisce
"MAIUSCOLO MINUSCOLO 123"
.
Su: Funzioni per stringhe [Contenuti][Indice]
sub()
, gsub()
e gensub()
ATTENZIONE: Si dice che questa sottosezione possa causare dei mal di testa. In prima lettura può essere benissimo saltata.
Quando si usa sub()
, gsub()
o gensub()
, e si
desidera includere delle
barre inverse e delle "e commerciali" (&
) nel testo da sostituire
è necessario ricordare che ci sono parecchi livelli di
protezione caratteri in gioco.
Anzitutto, vi è il livello lessicale, quello in cui awk
legge un programma e ne costruisce una copia interna da eseguire.
Poi c’è il momento dell’esecuzione, quello in cui awk
esamina effettivamente la stringa da sostituire, per determinare cosa
fare.
In entrambi i livelli, awk
ricerca un dato insieme di caratteri
che possono venire dopo una
barra inversa. A livello lessicale, cerca le sequenze di protezione
elencate in Sequenze di protezione.
Quindi, per ogni ‘\’ che awk
elabora al momento
dell’esecuzione, occorre immetterne due a livello lessicale.
Quando un carattere che non ha necessità di una sequenza di protezione
segue una ‘\’, sia BWK awk
che gawk
semplicemente
rimuovono la ‘\’ stessa e
mettono il carattere seguente nella stringa. Quindi, per esempio,
"a\qb"
è trattato come se si fosse scritto "aqb"
.
Al momento dell’esecuzione, le varie funzioni gestiscono sequenze di
‘\’ e ‘&’ in maniera differente. La situazione è (purtroppo)
piuttosto complessa.
Storicamente, le funzioni sub()
e gsub()
trattavano la
sequenza di due caratteri ‘\&’ in maniera speciale; questa sequenza
era rimpiazzata nel testo
generato da un singolo carattere ‘&’. Ogni altra ‘\’ contenuta
nella stringa rimpiazzo che non era posta prima di una ‘&’ era
lasciata passare senza modifiche.
Questo è illustrato nella Tabella 9.1.
Immissionesub()
vedesub()
genera ————— ————- —————\&
&
Il testo individuato\\&
\&
La lettera ‘&’\\\&
\&
La lettera ‘&’\\\\&
\\&
Le lettere ‘\&’\\\\\&
\\&
Le lettere ‘\&’\\\\\\&
\\\&
Le lettere ‘\\&’\\q
\q
Le lettere ‘\q’
Tabella 9.1: Elaborazione storica delle sequenze di protezione per sub()
e gsub()
Questa tabella mostra l’elaborazione a livello lessicale, in cui
un numero dispari di barre inverse diventa un numero pari al momento
dell’esecuzione,
e mostra anche l’elaborazione in fase di esecuzione fatta da sub()
.
(Per amor di semplicità le tavole che ancora seguono mostrano solo il caso
di un numero pari di barre inverse immesso a livello lessicale.)
Il problema con l’approccio storico è che non c’è modo di ottenere un carattere ‘\’ seguito dal testo individuato.
Parecchie edizioni dello standard POSIX hanno provato a risolvere questo problema, senza riuscirci. I dettagli sono irrilevanti in questo contesto.
A un certo punto, il manutentore di gawk
ha presentato una
proposta per una revisione dello standard per tornare
a regole che corrispondano più da vicino alla prassi originalmente seguita.
Le regole proposte hanno dei casi speciali che rendono possibile
produrre una ‘\’ prima del
testo individuato. Questo si può vedere nella
Tabella 9.2.
Immissionesub()
vedesub()
genera ——— ———- —————\\\\\\&
\\\&
Il carattere ‘\&’\\\\&
\\&
Il carattere ‘\’, seguito dal testo individuato\\&
\&
Il carattere ‘&’\\q
\q
I caratteri ‘\q’\\\\
\\
\\
Tabella 9.2: Regole gawk
per sub()
e barra inversa
In breve, al momento dell’esecuzione, ci sono ora tre sequenze speciali di caratteri (‘\\\&’, ‘\\&’, e ‘\&’) mentre tradizionalmente ce n’era una sola. Tuttavia, come nel caso storico, ogni ‘\’ che non fa parte di una di queste tre sequenze non è speciale e appare nell’output così come è scritto.
gawk
3.0 e 3.1 seguono queste regole per sub()
e
gsub()
. La revisione dello standard POSIX ha richiesto molto più tempo
di quel che ci si attendeva. Inoltre, la proposta del manutentore di
gawk
è andata persa durante il processo di standardizzazione. Le
regole finali risultanti sono un po’ più semplici. I risultati sono simili,
tranne che in un caso.
Le regole POSIX stabiliscono che ‘\&’ nella stringa di rimpiazzo produca il carattere ‘&’, ‘\\’ produce il carattere ‘\’, e che ‘\’ seguito da qualsiasi carattere non è speciale; la ‘\’ è messa direttamente nell’output. Queste regole sono presentate nella Tabella 9.3.
Immissionesub()
vedesub()
genera ——— ———- —————\\\\\\&
\\\&
I caratteri ‘\&’\\\\&
\\&
Il carattere ‘\’, seguito dal testo individuato\\&
\&
Il carattere ‘&’\\q
\q
I caratteri ‘\q’\\\\
\\
\
Tabella 9.3: Regole POSIX per sub()
e gsub()
Il solo caso in cui la differenza è rilevante è l’ultimo: ‘\\\\’ è visto come ‘\\’ e produce ‘\’ invece che ‘\\’.
A partire dalla versione 3.1.4, gawk
ha seguito le regole
POSIX quando si specifica --posix (vedi Opzioni). Altrimenti, ha
continuato a seguire le regole proposte [a POSIX], poiché questa è stato il
comportamento seguito per parecchi anni.
Quando la versione 4.0.0 è stata rilasciata, il manutentore di
gawk
ha stabilito come default le regole POSIX, interrompendo così oltre
un decennio di compatibilità
all’indietro.51
Inutile dire che questa non è stata una buona idea, e quindi dalla
versione 4.0.1, gawk
ha ripreso il suo comportamento
tradizionale, seguendo le regole POSIX solo quando si specifica l’opzione
--posix.
Le regole per gensub()
sono molto più semplici. Al momento
dell’esecuzione, quando gawk
vede una ‘\’, se il carattere
seguente è una cifra,
il testo individuato dalla corrispondente sottoespressione tra parentesi
è inserito nell’output generato. Altrimenti, qualsiasi carattere segua la
‘\’ viene inserito nel testo generato, mentre la ‘\’ va persa,
come si vede nella Tabella 9.4.
Immissionegensub()
vedegensub()
genera ——— ————- ——————&
&
Il testo individuato\\&
\&
Il carattere ‘&’\\\\
\\
Il carattere ‘\’\\\\&
\\&
Il carattere ‘\’, seguito dal testo individuato\\\\\\&
\\\&
I caratteri ‘\&’\\q
\q
Il carattere ‘q’
Tabella 9.4: Elaborazione sequenze di protezione in gensub()
A causa della complessità dell’elaborazione a livello lessicale e in fase
di esecuzione, e dei casi speciali di sub()
e gsub()
,
si raccomanda l’uso di gawk
e di gensub()
quando ci siano
da fare delle sostituzioni.
Successivo: Funzioni di tempo, Precedente: Funzioni per stringhe, Su: Funzioni predefinite [Contenuti][Indice]
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 |
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.
Situazione | Valore codice di ritorno da system() |
---|---|
--traditional | Valore dalla funzione C system() /256 |
--posix | Valore dalla funzione C system() |
Uscita normale dal comando | Codice di ritorno del comando |
Terminazione da un segnale | 256 + numero segnale "assassino" |
Terminazione da un segnale con dump memoria | 512 + numero segnale "assassino" |
Qualsiasi tipo di errore | -1 |
Tabella 9.5: Valori codici di ritorno da chiamata a system()
Successivo: Funzioni a livello di bit, Precedente: Funzioni di I/O, Su: Funzioni predefinite [Contenuti][Indice]
I programmi awk
sono frequentemente usati per elaborare file di
registro [file con estensione .log], che contengono l’informazione sulla data e
l’ora (marcatura temporale) in cui un particolare record è stato registrato sul log.
Molti programmi registrano questa informazione nel formato restituito
dalla chiamata di sistema time()
, la quale misura il numero di secondi
trascorsi a partire da una certa data iniziale (Epoca). Nei sistemi aderenti
allo standard POSIX, questo è il numero di secondi a partire dal primo gennaio
1970, ora di Greenwich (1970-01-01 00:00:00 UTC), senza includere i secondi
intercalari.54
Tutti i sistemi noti aderenti allo standard POSIX gestiscono le marcature
temporali da 0 fino a
231 - 1,
il che è sufficiente per rappresentare date e ore fino a inizio 2038
(2038-01-19 03:14:07 UTC). Molti sistemi supportano una maggiore estensione
di date, compresi dei valori negativi per rappresentare delle date
anteriori all’Epoca.
Per facilitare l’elaborazione di tali file di registro, e per produrre
dei rapporti utili, gawk
prevede le seguenti funzioni per
lavorare con le marcature temporali. Si tratta di estensioni gawk
;
non sono previste nello standard POSIX.55 Tuttavia, anche versioni recenti di mawk
(vedi Altre versioni) prevedono queste funzioni. I parametri facoltativi
sono racchiusi tra parentesi quadre ([ ]):
mktime(specifiche_data
[, utc-flag
])
Trasforma specifiche_data in una marcatura temporale nello stesso formato
restituito da systime()
. È simile alla funzione omonima
in ISO C. L’argomento, specifiche_data, è una stringa della forma
"AAAA MM GG HH MM SS [DST]"
.
La stringa consiste di sei o sette numeri che rappresentano,
rispettivamente,
l’anno in quattro cifre, il mese da 1 a 12, il giorno del mese
da 1 a 31, l’ora del giorno da 0 a 23, il minuto da 0 a
59, il secondo da 0 a 60,56
e un’indicazione opzionale relativa all’ora legale.
I valori di questi numeri possono non essere negli intervalli specificati; per
esempio, un’ora di -1 sta a indicare 1 ora prima di mezzanotte.
Viene adottato il calendario gregoriano con l’origine posta all’anno zero,
con l’anno 0 che viene prima dell’anno 1 e l’anno -1 che viene prima
dell’anno 0. Se il flag utc-flag è specificato ed è diverso da zero
e dalla stringa nulla, si suppone che l’ora sia quella del fuso orario UTC;
altrimenti l’ora è considerata essere quella del fuso orario locale. Se
l’indicatore dell’ora legale è positivo, si presuppone che l’ora sia quella
legale; se è 0, l’ora considerata è quella di Greenwich (standard time); se
invece è negativo (questo è il default), mktime()
tenta di
determinare se è in vigore l’ora legale o no, nel momento specificato.
Se specifiche_data non contiene elementi in numero sufficiente, o se
la data e ora risultante sono fuori dall’intervallo previsto,
mktime()
restituisce -1.
strftime(
[formato [,
data_e_ora [,
utc] ] ])
Formatta la data e ora specificata da data_e_ora in base alle indicazioni
contenute nella stringa formato e restituisce il risultato.
È simile alla funzione omonima in ISO C.
Se utc è presente ed è diverso da zero o dalla stringa nulla,
il valore è formattato come UTC (Tempo Coordinato Universale,
già noto come GMT o Tempo Medio di Greenwich).
Altrimenti, il valore è formattato per il fuso orario locale.
La stringa data_e_ora è nello stesso formato del valore restituito
dalla funzione systime()
. Se non si specifica l’argomento
data_e_ora, gawk
usa l’ora del giorno corrente per la
formattazione.
Omettendo l’argomento formato, strftime()
usa
il valore di PROCINFO["strftime"]
come stringa di formattazione
(vedi Variabili predefinite).
Il valore di default della stringa è
"%a %b %e %H:%M:%S %Z %Y"
. Questa stringa di formattazione
produce lo stesso output del programma di utilità equivalente
date
.
Si può assegnare un nuovo valore a PROCINFO["strftime"]
per
modificare la formattazione di default; si veda
la lista che segue per le varie direttive di formattazione.
systime()
Restituisce l’ora corrente come numero di secondi a partire dall’Epoca del sistema. Sui sistemi aderenti allo standard POSIX, questo è il numero di secondi trascorsi a partire dal primo gennaio 1970, ora di Greenwich (1970-01-01 00:00:00 UTC), senza includere i secondi intercalari.
La funzione systime()
consente di confrontare una marcatura temporale
in un file di registro con la data e ora correnti. In particolare, è facile
determinare quanto tempo prima un particolare record è stato registrato.
È anche possibile produrre record di registro usando il formato
“secondi a partire dall’Epoca”.
La funzione mktime()
consente di convertire una rappresentazione in
forma testuale di una data e ora in una marcatura temporale.
Questo semplifica i confronti prima/dopo tra differenti date e ore, in
particolare quando si abbia a che fare con date e ore provenienti da una
fonte esterna, come un file di registro.
La funzione strftime()
permette di trasformare facilmente una marcatura
temporale in un’informazione intelligibile. È analoga come tipo alla funzione
sprintf()
(vedi Funzioni per stringhe), nel senso che copia
letteralmente ciò che non è una specifica di formato nella stringa che viene
restituita, mentre sostituisce i valori di data e ora a seconda delle
specifiche di formato contenute nella stringa formato.
Per strftime()
lo standard
1999 ISO C57
consente le seguenti specifiche di formattazione delle date:
%a
Il nome abbreviato del giorno della settimana nella lingua locale.
%A
Il nome completo del giorno della settimana nella lingua locale.
%b
Il nome abbreviato del mese dell’anno nella lingua locale.
%B
Il nome completo del mese dell’anno nella lingua locale.
%c
Il formato “appropriato” della rappresentazione della data e ora
nella lingua locale.
(Questo è ‘%A %B %d %T %Y’ per la localizzazione "C"
.)
%C
La parte che designa il secolo nell’anno corrente. Si ottiene dividendo per 100 l’anno, e troncando verso il basso all’intero più vicino.
%d
Il giorno del mese come numero decimale (01–31).
%D
Equivale a specificare ‘%m/%d/%y’.
%e
Il giorno del mese, preceduto da uno spazio se di tratta di una cifra sola.
%F
Equivale a specificare ‘%Y-%m-%d’. Questo è il formato ISO 8601 della data.
%g
L’anno (ultime due cifre) ricavato prendendo il resto della divisione per 100 dell’anno a cui appartiene la settimana, secondo ISO 8601, come numero decimale (00–99). Per esempio, il primo gennaio 2012, fa parte della settimana 53 del 2011. Quindi, l’anno relativo al numero di settimana ISO di quella data è 2011 (ossia 11), anche se la data in sé è nel 2012. Analogamente, il 31 dicembre 2012, è nella prima settimana del 2013. Quindi, l’anno relativo al numero di settimana ISO di quella data è 2013 (ossia 13), anche se la data in sé è nel 2012.
%G
L’anno intero relativo al numero di settimana ISO, come numero decimale.
%h
Equivalente a ‘%b’.
%H
L’ora (in un orologio a 24 ore) come numero decimale (00–23).
%I
L’ora (in un orologio a 12 ore) come numero decimale (01–12).
%j
Il giorno dell’anno come numero decimale (001–366).
%m
Il mese come numero decimale (01–12).
%M
Il minuto come numero decimale (00–59).
%n
Un carattere di ritorno a capo (ASCII LF).
%p
L’equivalente nella lingua locale delle designazioni AM/PM (mattino/pomerigggio) associate a un orologio a 12 ore.
%r
L’ora locale nel formato a 12 ore.
(Questo è ‘%I:%M:%S %p’ nella localizzazione "C"
.)
%R
Equivalente a specificare ‘%H:%M’.
%S
Il secondo come numero decimale (00–60).
%t
Un carattere di tabulazione [TAB].
%T
Equivalente a specificare ‘%H:%M:%S’.
%u
Il numero del giorno della settimana come numero decimale (1–7). Lunedì è il giorno numero 1.
%U
Il numero di settimana dell’anno (con la prima domenica dell’anno presa come primo giorno della prima settimana) come numero decimale (00–53).
%V
Il numero di settimana dell’anno (con il primo lunedì dell’anno preso come primo giorno della prima settimana) come numero decimale (01–53). Il metodo per determinare il numero di settimana è quello specificato dallo standard ISO 8601. (In pratica: se la settimana che contiene il primo gennaio ha quattro o più giorni nel nuovo anno, allora è la settimana numero uno; altrimenti è l’ultima settimana [52 o 53] dell’anno precedente, e la settimana successiva è la settimana numero uno.)
%w
Il giorno della settimana come numero decimale (0–6). Domenica è il giorno zero.
%W
Il numero di settimana dell’anno (con il primo lunedì come primo giorno della settimana numero uno) come numero decimale (00–53).
%x
Il formato “appropriato” della rappresentazione della data
nella lingua locale.
(Questo è ‘%A %B %d %Y’ nella localizzazione "C"
.)
%X
Il formato “appropriato” della rappresentazione della data.
(Questo è ‘%T’ nella localizzazione "C"
.)
%y
L’anno modulo 100 (le ultime due cifre) come numero decimale (00–99).
%Y
L’anno come numero decimale (p.es., 2015).
%z
La differenza di fuso orario [rispetto all’ora di Greenwich] in formato ‘+OOMM’ (p.es., il formato necessario per produrre intestazioni di data conformi agli standard RFC 822/RFC 1036).
%Z
Il nome o l’abbreviazione della zona di fuso orario (time zone); se il fuso orario non è determinabile, è impostata alla stringa nulla.
%Ec %EC %Ex %EX %Ey %EY %Od %Oe %OH
%OI %Om %OM %OS %Ou %OU %OV %Ow %OW %Oy
“notazioni alternative” di specifica
in cui solo la seconda lettera (‘%c’, ‘%C’ e così via) è
significativa.58
(Queste facilitano la compatibilità con il programma di utilità
POSIX date
.)
%%
Un singolo carattere ‘%’.
Se uno specificatore di conversione non è tra quelli elencati sopra, il comportamento è indefinito.59
Per sistemi che non aderiscono completamente agli standard
gawk
utilizza una copia di
strftime()
dalla libreria C di GNU.
Sono disponibili tutte le specifiche di formato sopra elencate.
Se la detta versione è
usata per compilare gawk
(vedi Installazione),
sono disponibili anche le seguenti ulteriori specifiche di formato:
%k
L’ora (in un orologio a 24 ore) come numero decimale (0–23). I numeri di una sola cifra sono preceduti da uno spazio bianco.
%l
L’ora (in un orologio a 12 ore) come numero decimale (1–12). I numeri di una sola cifra sono preceduti da uno spazio bianco.
%s
L’ora espressa in numero di secondi a partire dall’Epoca.
In aggiunta a ciò, le notazioni alternative sono riconosciute, ma al loro posto sono usate quelle normali.
Il seguente esempio è un’implementazione awk
del
programma di utilità POSIX date
.
Normalmente, il programma di utilità date
stampa la
data e l’ora corrente nel formato ben noto. Tuttavia, se si
specifica al comando un argomento che inizia con un ‘+’, date
copia i caratteri che non sono specifiche di formato nello standard output
e interpreta l’ora corrente secondo gli specificatori di formato
contenuti nella stringa. Per esempio:
$ date '+Oggi è %A, %d %B %Y.' -| Oggi è lunedì, 22 settembre 2014.
Ecco la versione gawk
del programma di utilità date
.
È all’interno di uno script di shell per gestire l’opzione -u,
che richiede che date
sia eseguito come se il fuso orario
fosse impostato a UTC:
#! /bin/sh # # date --- simula il comando POSIX 'date' case $1 in -u) TZ=UTC0 # usare UTC export TZ shift ;; esac gawk 'BEGIN { formato = PROCINFO["strftime"] codice_di_ritorno = 0 if (ARGC > 2) codice_di_ritorno = 1 else if (ARGC == 2) { formato = ARGV[1] if (formato ~ /^\+/) formato = substr(formato, 2) # togli il + iniziale } print strftime(formato) exit codice_di_ritorno }' "$@"
Successivo: Funzioni per i tipi, Precedente: Funzioni di tempo, Su: Funzioni predefinite [Contenuti][Indice]
Io posso spiegarlo per te, ma non posso capirlo per te.
Molti linguaggi consentono di eseguire operazioni bit a bit su due numeri interi. In altre parole, l’operazione è eseguita su ogni successiva coppia di bit presi da ognuno dei due operandi. Tre operazioni comuni sono AND, OR e XOR bit a bit. Queste operazioni sono descritte nella Tabella 9.6.
Operatore booleano | AND | OR | XOR |---+---+---+---+---+--- Operandi | 0 | 1 | 0 | 1 | 0 | 1 ----------+---+---+---+---+---+--- 0 | 0 0 | 0 1 | 0 1 1 | 0 1 | 1 1 | 1 0
Tabella 9.6: Operazioni a livello di bit
Come si vede, il risultato di un’operazione di AND è 1 solo quando entrambi i bit sono 1. Il risultato di un’operazione di OR è 1 se almeno un bit è 1. Il risultato di un’operazione di XOR è 1 se l’uno o l’altro bit è 1, ma non tutti e due. La successiva operazione è il complemento; il complemento di 1 è 0 e il complemento di 0 è 1. Quindi, quest’operazione “inverte” tutti i bit di un dato valore.
Infine, due altre operazioni comuni consistono nello spostare i bit
a sinistra o a destra.
Per esempio, se si ha una stringa di bit ‘10111001’ e la si sposta
a destra di tre bit, si ottiene ‘00010111’.60
Partendo nuovamente da ‘10111001’ e spostandolo a sinistra di tre
bit, si ottiene ‘11001000’. La lista seguente descrive
le funzioni predefinite di gawk
che rendono disponibili
le operazioni a livello di bit.
I parametri facoltativi sono racchiusi tra parentesi quadre ([ ]):
and(
v1,
v2 [,
…])
Restituisce l’AND bit a bit degli argomenti. Gli argomenti devono essere almeno due.
compl(val)
Restituisce il complemento bit a bit di val.
lshift(val, contatore)
Restituisce il valore di val, spostato a sinistra di contatore bit.
or(
v1,
v2 [,
…])
Restituisce l’OR bit a bit degli argomenti. Gli argomenti devono essere almeno due.
rshift(val, contatore)
Restituisce il valore di val, spostato a destra di contatore bit.
xor(
v1,
v2 [,
…])
Restituisce il XOR bit a bit degli argomenti. Gli argomenti devono essere almeno due.
ATTENZIONE: A partire dalla versione di
gawk
versione 4.2, gli operandi negativi non sono consentiti per nessuna di queste funzioni. Un operando negativo produce un errore fatale. Si veda la nota a lato “Attenzione. Non è tutto oro quel che luccica!” per maggiori informazioni sul perché.
Ecco una funzione definita dall’utente (vedi Funzioni definite dall'utente) che illustra l’uso di queste funzioni:
# bits2str --- decodifica un byte in una serie di 0/1 leggibili function bits2str(byte, dati, maschera) { if (byte == 0) return "0" maschera = 1 for (; byte != 0; stringa = rshift(stringa, 1)) dati = (and(byte, maschera) ? "1" : "0") dati while ((length(dati) % 8) != 0) dati = "0" dati return dati }
BEGIN { printf "123 = %s\n", bits2str(123) printf "0123 = %s\n", bits2str(0123) printf "0x99 = %s\n", bits2str(0x99) comp = compl(0x99) printf "compl(0x99) = %#x = %s\n", comp, bits2str(comp) shift = lshift(0x99, 2) printf "lshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift) shift = rshift(0x99, 2) printf "rshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift) }
Questo programma produce il seguente output quando viene eseguito:
$ gawk -f testbits.awk -| 123 = 01111011 -| 0123 = 01010011 -| 0x99 = 10011001 -| compl(0x99) = 0x3fffffffffff66 = 001111111111111111111111111111111 -| 11111111111111101100110 -| lshift(0x99, 2) = 0x264 = 0000001001100100 -| rshift(0x99, 2) = 0x26 = 00100110
La funzione bits2str()
trasforma un numero binario in una stringa.
Inizializzando maschera
a uno otteniamo
un valore binario in cui il bit più a destra è impostato a
uno. Usando questa maschera,
la funzione continua a controllare il bit più a destra.
l’operazione di AND tra la maschera e il valore indica se il
bit più a destra è uno oppure no. Se questo è il caso, un "1"
è concatenato all’inizio della stringa.
Altrimenti, è concatenato uno "0"
.
Il valore è quindi spostato a destra di un bit e il ciclo continua
finché non ci sono più bit.
Se il valore iniziale è zero, viene restituito semplicemente uno "0"
.
Altrimenti, alla fine, al valore ottenuto vengono aggiunti degli zeri a
sinistra, per arrivare a stringhe
di lunghezza multipla di 8, ossia contenenti un numero intero di byte.
Questo è tipico dei computer moderni.
Il codice principale nella regola BEGIN
mostra la differenza tra
i valori decimale e ottale dello stesso numero.
(vedi Numeri non-decimali),
e poi mostra i risultati delle funzioni
compl()
, lshift()
e rshift()
.
Attenzione. Non è tutto oro quel che luccica!
In altri linguaggi, le operazioni "bit a bit" sono eseguite su valori interi, non su valori a virgola mobile. Come regola generale, tali operazioni funzionano meglio se eseguite su interi senza segno.
In una normale operazione, per tutte queste funzioni, prima il valore a virgola
mobile a doppia precisione viene convertito nel tipo intero senza segno di C
più ampio, poi viene eseguita l’operazione "bit a bit". Se il risultato non
può essere rappresentato esattamente come un tipo Comunque, quando si usa il calcolo con precisione arbitraria con l’opzione
-M (vedi Calcolo con precisione arbitraria), il risultato può
essere diverso. Questo è particolarmente evidente con la funzione $ gawk 'BEGIN { print compl(42) }' -| 9007199254740949 $ gawk -M 'BEGIN { print compl(42) }' -| -43 Quel che avviene diventa chiaro quando si stampano i risultati in notazione esadecimale: $ gawk 'BEGIN { printf "%#x\n", compl(42) }' -| 0x1fffffffffffd5 $ gawk -M 'BEGIN { printf "%#x\n", compl(42) }' -| 0xffffffffffffffd5 Quando si usa l’opzione -M, nel dettaglio, In breve, usare |
Successivo: Funzioni di internazionalizzazione, Precedente: Funzioni a livello di bit, Su: Funzioni predefinite [Contenuti][Indice]
gawk
prevede due funzioni che permettono di conoscere
il tipo di una variabile.
Questo è necessario per scrivere del codice che visiti ogni elemento di un
vettore di vettori
(vedi Vettori di vettori) e in altri contesti.
isarray(x)
Restituisce il valore ’vero’ se x è un vettore. Altrimenti, restituisce ’falso’.
typeof(x)
Restituisce una delle stringhe seguenti, a seconda del tipo di x:
"array"
x è un vettore.
"regexp"
x è una regexp fortemente tipizzata (vedi Costanti regexp forti).
"number"
x è un numero.
"string"
x è una stringa.
"strnum"
x è un numero che ha avuto origine da un input dell’utente,
come un campo o il risultato di una chiamata a split()
.
(Cioè, x ha l’attributo strnum;
vedi Tipi di variabile.)
"unassigned"
x è una variabile scalare a cui non è ancora stato assegnato un valore. Per esempio:
BEGIN { # crea a[1] ma non gli attribuisce alcun valore a[1] print typeof(a[1]) # unassigned }
"untyped"
x non è stata usata per nulla; può diventare uno scalare o un vettore. Per esempio:
BEGIN { print typeof(x) # x non è mai stato usato --> untyped mk_arr(x) print typeof(x) # x ora è un vettore --> array } function mk_arr(a) { a[1] = 1 }
isarray()
torna utile in due occasioni. La prima è quando
si visita un vettore multidimensionale: si può stabilire se un elemento è
un vettore oppure no. La seconda è all’interno del corpo di una funzione
definita dall’utente (argomento non ancora trattato;
vedi Funzioni definite dall'utente), per determinare se un parametro
è un vettore oppure no.
NOTA: Usare
isarray()
a livello globale per controllare le variabili non ha alcun senso. Si suppone infatti che chi scrive il programma sappia se una variabile è un vettore oppure no. E in effetti, per come funzionagawk
, se si passa una variabile che non sia stata usata in precedenza aisarray()
,gawk
la crea al volo, assegnandole il tipo scalare.
La funzione typeof()
è generale; consente di determinare
se una variabile o un parametro di funzione è uno scalare, un vettore,
o una regexp fortemente tipizzata.
L’uso di isarray()
è deprecato; si dovrebbe usare typeof()
al suo posto. Si dovrebbe sostituire ogni uso esistente di
‘isarray(var)’ nei programmi esistenti con
‘typeof(var) == "array"’.
Precedente: Funzioni per i tipi, Su: Funzioni predefinite [Contenuti][Indice]
gawk
prevede strumenti per internazionalizzare i programmi
awk
.
Questi sono costituiti dalle funzioni descritte nella lista seguente.
Le descrizioni sono volutamente concise.
Vedi Internazionalizzazione,
per un’esposizione completa.
I parametri facoltativi sono racchiusi tra parentesi quadre ([ ]):
bindtextdomain(directory
[,
dominio])
Imposta la directory in cui
gawk
trova i file di traduzione dei messaggi, nel caso in cui
non siano o non possano essere messi nelle directory “standard”
(p.es., durante la fase di test di un programma).
Restituisce la directory alla quale dominio è “connesso.”
Il default per dominio è il valore di TEXTDOMAIN
.
Se directory è la stringa nulla (""
),
bindtextdomain()
restituisce la connessione corrente per il
dominio dato.
dcgettext(stringa
[,
dominio [,
categoria] ])
Restituisce la traduzione di stringa nel
dominio linguistico dominio per la categoria di localizzazione
categoria.
Il valore di default per dominio è il valore corrente di TEXTDOMAIN
.
Il valore di default per categoria è "LC_MESSAGES"
.
dcngettext(stringa1, stringa2, numero
[,
dominio [,
categoria] ])
Restituisce la forma plurale usata per numero nella
traduzione di stringa1 e stringa2 nel dominio di testo
dominio per la categoria di localizzazione categoria.
stringa1 è la variante al singolare in inglese di un messaggio e
stringa2 è la variante al plurare in inglese dello stesso messaggio.
Il valore di default per dominio è il valore corrente di TEXTDOMAIN
.
Il valore di default per categoria è "LC_MESSAGES"
.
Successivo: Chiamate indirette, Precedente: Funzioni predefinite, Su: Funzioni [Contenuti][Indice]
Programmi awk
complessi spesso possono essere semplificati
definendo delle apposite funzioni personali.
Le funzioni definite dall’utente sono richiamate allo stesso modo di quelle
predefinite
(vedi Chiamate di funzione),
ma dipende dall’utente la loro definizione
(cioè, dire ad awk
cosa dovrebbero fare queste funzioni).
• Sintassi delle definizioni: | Come scrivere definizioni e cosa significano. | |
• Esempio di funzione: | Un esempio di definizione di funzione e spiegazione della stessa. | |
• Precisazioni sulle funzioni: | Cose a cui prestare attenzione. | |
• Istruzione return: | Specificare il valore che una funzione restituisce. | |
• Variabili di tipo dinamico: | Come cambiare tipo a una variabile in fase di esecuzione del programma. |
Successivo: Esempio di funzione, Su: Funzioni definite dall'utente [Contenuti][Indice]
Risponde al vero affermare che la sintassi di awk per la definizione di variabili locali è semplicemente atroce.
Definizioni di funzioni possono stare in una posizione qualsiasi tra le regole
di un programma awk
. Quindi, la forma generale di un
programma awk
è estesa per
permettere l’inclusione di regole e la definizione di funzioni
create dall’utente.
Non è necessario che la definizione di una funzione sia posta prima
del richiamo della stessa. Questo dipende dal fatto che awk
legge l’intero programma, prima di iniziare ad eseguirlo.
La definizione di una funzione chiamata nome è simile a questa:
function
nome(
[lista-parametri])
{
corpo-della-funzione}
Qui, nome è il nome della funzione da definire. Un nome di funzione
valido è come un nome di variabile valido: una sequenza di
lettere, cifre e trattini bassi che non inizia con una cifra.
Anche qui, solo le 52 lettere inglesi maiuscole e minuscole possono
essere usate in un nome di funzione.
All’interno di un singolo programma awk
, un dato nome può essere
usato una sola volta: per una variabile, o per un vettore,
o per una funzione.
lista-parametri è una lista opzionale degli argomenti della funzione e dei nomi delle variabili locali, separati da virgole. Quando la funzione viene chiamata, i nomi degli argomenti sono usati per contenere il valore degli argomenti passati con la chiamata.
Una funzione non può avere due parametri con lo stesso nome, e neanche un parametro con lo stesso nome della funzione stessa.
ATTENZIONE: Secondo lo standard POSIX, i parametri di funzione non possono avere lo stesso nome di una delle speciali variabili predefinite (vedi Variabili predefinite), e un parametro di funzione non può avere lo stesso nome di un’altra funzione. Non tutte le versioni di
awk
applicano queste limitazioni.gawk
applica solo la prima di queste restrizioni. Se viene specificata l’opzione --posix (vedi Opzioni), anche la seconda restrizione viene applicata.
Le variabili locali si comportano come la stringa vuota se vengono utilizzate dove è richiesto il valore di una stringa, e valgono zero se utilizzate dove è richiesto un valore numerico. Questo è lo stesso comportamento delle variabili regolari a cui non sia stato ancora assegnato un valore. (Ci sono ulteriori informazioni riguardo alle variabili locali; vedi Variabili di tipo dinamico.)
Il corpo-della-funzione è composto da istruzioni awk
.
Questa è la parte più importante della definizione, perché dice quello che
la funzione dovrebbe realmente
fare. I nomi di argomento esistono per consentire al corpo della
funzione di gestire gli argomenti;
le variabili locali esistono per consentire al corpo della funzione di
memorizzare dei valori temporanei.
I nomi di argomento non sono sintatticamente distinti da quelli delle variabili locali. Invece, il numero di argomenti forniti quando la funzione viene chiamata determina quanti degli argomenti passati sono delle variabili. Quindi, se tre valori di argomento sono specificati, i primi tre nomi in lista-parametri sono degli argomenti e i rimanenti sono delle variabili locali.
Ne consegue che se il numero di argomenti richiesto non è lo stesso in tutte le chiamate alla funzione, alcuni dei nomi in lista-parametri possono essere in alcuni casi degli argomenti e in altri casi delle variabili locali. Un’altra angolatura da cui guardare questo fatto è che gli argomenti omessi assumono come valore di default la stringa nulla.
Solitamente, quando si scrive una funzione, si sa quanti nomi si intendono usare per gli argomenti e quanti si vogliono usare come variabili locali. È una convenzione in uso quella di aggiungere alcuni spazi extra tra gli argomenti e le variabili locali, per documentare come va utilizzata quella funzione.
Durante l’esecuzione del corpo della funzione, gli argomenti e i valori
delle variabili locali
nascondono, o oscurano, qualsiasi variabile dello stesso nome usata
nel resto del programma. Le variabili oscurate non sono accessibili
nel corpo della funzione, perché non c’è modo di accedere a esse
mentre i loro nomi sono stati "occupati" dagli argomenti e dalla variabili
locali. Tutte le altre variabili usate nel programma awk
possono essere accedute o impostate normalmente nel corpo della funzione.
Gli argomenti e le variabili locali esistono solo finché il corpo della funzione è in esecuzione. Una volta che l’esecuzione è terminata, ritornano accessibili le variabili che erano oscurate durante l’esecuzione della funzione.
Il corpo della funzione può contenere espressioni che chiamano altre funzioni. Tali espressioni possono perfino chiamare direttamente, o indirettamente tramite un’altra funzione, la funzione stessa. Quando questo succede, la funzione è detta ricorsiva. Il fatto che una funzione richiami se stessa è detto ricorsione.
Tutte le funzioni predefinite restituiscono un valore al loro chiamante.
Anche le funzioni definite dall’utente possono farlo, usando
l’istruzione return
,
che è descritta in dettaglio nella Istruzione return.
Molti dei successivi esempi in questa sezione usano
l’istruzione return
.
In molte implementazioni di awk
, compreso gawk
,
la parola chiave function
può essere
abbreviata come func
. (e.c.)
Tuttavia, POSIX specifica solo l’uso della parola chiave
function
. Questo ha alcune implicazioni di carattere pratico.
Se gawk
è in modalità POSIX-compatibile
(vedi Opzioni), la seguente
istruzione non definisce una funzione:
func foo() { a = sqrt($1) ; print a }
Invece, definisce una regola che, per ogni record, concatena il valore
della variabile ‘func’ con il valore restituito dalla funzione ‘foo’.
Se la stringa risultante è diversa dalla stringa nulla, l’azione viene eseguita.
Questo non è con ogni probabilità quello che si desidera.
(awk
accetta questo input come
sintatticamente valido, perché le funzioni, nei programmi awk
possono essere usate prima che siano state definite.62)
Per essere certi che un programma awk
sia portabile,
va sempre usata la parola chiave
function
per definire una funzione.
Successivo: Precisazioni sulle funzioni, Precedente: Sintassi delle definizioni, Su: Funzioni definite dall'utente [Contenuti][Indice]
Ecco un esempio di funzione definita dall’utente, di nome
stampa_num()
, che
ha come input un numero e lo stampa in un formato specifico:
function stampa_num(numero) { printf "%6.3g\n", numero }
Per comprenderne il funzionamento, ecco una regola awk
che usa
la funzione stampa_num()
:
$3 > 0 { stampa_num($3) }
Questo programma stampa, nel nostro formato speciale, tutti i terzi campi nei record in input che contengono un numero positivo. Quindi, dato il seguente input:
1.2 3.4 5.6 7.8 9.10 11.12 -13.14 15.16 17.18 19.20 21.22 23.24
questo programma, usando la nostra funzione per formattare i risultati, stampa:
5.6 21.2
La funzione seguente cancella tutti gli elementi in un vettore (si ricordi che gli spazi bianchi in soprannumero stanno a indicare l’inizio della lista delle variabili locali):
function cancella_vettore(a, i) { for (i in a) delete a[i] }
Quando si lavora con vettori, è spesso necessario cancellare
tutti gli elementi in un vettore e ripartire con una nuova lista di elementi
(vedi Cancellazione).
Invece di dover ripetere
questo ciclo ogni volta che si deve cancellare
un vettore, un programma può limitarsi a effettuare una chiamata
a cancella_vettore()
.
(Questo garantisce la portabilità. L’uso di ‘delete vettore’
per cancellare
il contenuto di un intero vettore è un’aggiunta relativamente
recente63
allo standard POSIX.)
Quello che segue è un esempio di una funzione ricorsiva. Prende come parametro di input una stringa e restituisce la stringa in ordine inverso. Le funzioni ricorsive devono sempre avere un test che interrompa la ricorsione. In questo caso, la ricorsione termina quando la stringa in input è già vuota:
function rev(stringa) { if (stringa == "") return "" return (rev(substr(stringa, 2)) substr(stringa, 1, 1)) }
Se questa funzione è in un file di nome rev.awk, si può provare così:
$ echo "Non v'allarmate!" | > gawk -e '{ print rev($0) }' -f rev.awk -| !etamralla'v noN
La funzione C ctime()
prende una marcatura temporale e la restituisce
come una stringa,
formattata come già sappiamo.
Il seguente esempio usa la funzione predefinita strftime()
(vedi Funzioni di tempo)
per creare una versione awk
di ctime()
:
# ctime.awk # # versione awk della funzione C ctime(3) function ctime(ts, format) { format = "%a %e %b %Y, %H.%M.%S, %Z" if (ts == 0) ts = systime() # usare data e ora correnti per default return strftime(format, ts) }
Si potrebbe pensare che la funzione ctime()
possa usare
PROCINFO["strftime"]
come stringa di formato. Sarebbe un errore, perché si suppone che
ctime()
restituisca data e ora formattati in maniera standard,
e qualche codice a livello utente potrebbe aver modificato in precedenza
PROCINFO["strftime"]
.
Successivo: Istruzione return, Precedente: Esempio di funzione, Su: Funzioni definite dall'utente [Contenuti][Indice]
Chiamare una funzione significa richiedere l’esecuzione di una funzione, la quale svolge il compito per cui è stata scritta. La chiamata di una funzione è un’espressione e il suo valore è quello restituito dalla funzione.
• Chiamare una funzione: | Non usare spazi. | |
• Campo di validità variabili: | Variabili locali e globali. | |
• Parametri per valore/riferimento: | Passaggio parametri. |
Successivo: Campo di validità variabili, Su: Precisazioni sulle funzioni [Contenuti][Indice]
Una chiamata di funzione consiste nel nome della funzione seguito dagli
argomenti racchiusi tra parentesi. Gli argomenti specificati nella chiamata
sono costituiti da espressioni awk
. Ogni volta che si esegue una
chiamata queste espressioni vengono ricalcolate, e i loro valori diventano gli
argomenti passati alla funzione. Per esempio, ecco una chiamata a
pippo()
con tre argomenti (il primo dei quali è una concatenazione di
stringhe):
pippo(x y, "perdere", 4 * z)
ATTENZIONE: Caratteri bianchi (spazi e TAB) non sono permessi tra il nome della funzione e la parentesi aperta che apre la lista degli argomenti. Se per errore si lasciano dei caratteri bianchi,
awk
li interpreterebbe come se s’intendesse concatenare una variabile con un’espressione tra parentesi. Tuttavia, poiché si è usato un nome di funzione e non un nome di variabile, verrebbe emesso un messaggio di errore.
Successivo: Parametri per valore/riferimento, Precedente: Chiamare una funzione, Su: Precisazioni sulle funzioni [Contenuti][Indice]
Diversamente da molti altri linguaggi, non c’è modo di
rendere locale una variabile in un blocco {
… }
di
awk
, ma si può rendere locale una variabile di una funzione. È
buona norma farlo quando una variabile serve solo all’interno di quella
particolare funzione.
Per rendere locale una variabile per una funzione, basta dichiarare la
variabile come argomento della funzione dopo gli argomenti richiesti dalla
funzione (vedi Sintassi delle definizioni). Si consideri il seguente
esempio, dove la variabile i
è una variabile globale usata sia dalla
funzione pippo()
che dalla funzione pluto()
:
function pluto() { for (i = 0; i < 3; i++) print "in pluto i=" i } function pippo(j) { i = j + 1 print "in pippo i=" i pluto() print "in pippo i=" i } BEGIN { i = 10 print "in BEGIN i=" i pippo(0) print "in BEGIN i=" i }
L’esecuzione di questo script produce quanto segue, perché la stessa
variabile i
è usata sia nelle
funzioni pippo()
e pluto()
sia a livello della
regola BEGIN
:
in BEGIN i=10 in pippo i=1 in pluto i=0 in pluto i=1 in pluto i=2 in pippo i=3 in BEGIN i=3
Se si vuole che i
sia una variabile locale sia per
pippo()
che per pluto()
, occorre procedere in questo modo
(gli spazi extra prima della i
sono una convenzione di codifica
che serve a ricordare che i
è una variabile locale, non
un argomento):
function pluto( i) { for (i = 0; i < 3; i++) print "in pluto i=" i } function pippo(j, i) { i = j + 1 print "in pippo i=" i pluto() print "in pippo i=" i } BEGIN { i = 10 print "in BEGIN i=" i pippo(0) print "in BEGIN i=" i }
L’esecuzione della versione corretta dello script produce il seguente output:
in BEGIN i=10 in pippo i=1 in pluto i=0 in pluto i=1 in pluto i=2 in pippo i=1 in BEGIN i=10
Oltre a valori scalari (stringhe e numeri), si possono usare anche
vettori locali. Usando come parametro il nome di un vettore, awk
lo considera come tale, e lo tratta come locale alla funzione.
Inoltre, chiamate ricorsive creano nuovi vettori.
Si consideri questo esempio:
function qualche_funz(p1, a) { if (p1++ > 3) return a[p1] = p1 qualche_funz(p1) printf("Al livello %d, indice %d %s trova in a\n", p1, (p1 - 1), (p1 - 1) in a ? "si" : "non si") printf("Al livello %d, indice %d %s trova in a\n", p1, p1, p1 in a ? "si" : "non si") print "" } BEGIN { qualche_funz(1) }
Quando viene eseguito, questo programma produce il seguente output:
Al livello 4, indice 3 non si trova in a Al livello 4, indice 4 si trova in a Al livello 3, indice 2 non si trova in a Al livello 3, indice 3 si trova in a Al livello 2, indice 1 non si trova in a Al livello 2, indice 2 si trova in a
Precedente: Campo di validità variabili, Su: Precisazioni sulle funzioni [Contenuti][Indice]
In awk
, quando si definisce una funzione, non c’è modo di
dichiarare esplicitamente se gli argomenti sono passati per valore
o per riferimento.
Invece, il modo con cui i parametri sono passati è determinato durante l’esecuzione del programma, quando la funzione è chiamata, nel rispetto della regola seguente: se l’argomento è una variabile di tipo vettoriale, questa è passata per riferimento. Altrimenti, l’argomento è passato per valore.
Passare un argomento per valore significa che quando una funzione è chiamata, le viene fornita una copia del valore di quell’argomento. Il chiamante può usare una variabile il cui valore calcolato viene passato come argomento, ma la funzione chiamata non la riconosce come variabile; riconosce solo il valore assunto dall’argomento. Per esempio, scrivendo il seguente codice:
pippo = "pluto" z = mia_funzione(pippo)
non si deve pensare che l’argomento passato a mia_funzione()
sia “la
variabile pippo
.” Invece, è corretto considerare l’argomento come la
stringa il cui valore è "pluto"
.
Se la funzione mia_funzione()
altera i valori delle sue variabili
locali, ciò non influisce su nessun’altra variabile. Quindi, se
mia_funzione()
fa questo:
function mia_funzione(stringa) { print stringa stringa = "zzz" print stringa }
cambiando così il valore della variabile che è il suo primo argomento, ossia
stringa
, il valore di pippo
per il chiamante non viene
modificato. Il ruolo svolto da pippo
nella chiamata di
mia_funzione()
termina quando il suo valore ("pluto"
) viene
calcolato. Se la variabile stringa
esiste anche al di fuori di
mia_funzione()
, il corpo della funzione non può modificare questo valore
esterno, perché esso rimane oscurato durante l’esecuzione di
mia_funzione()
e non può quindi essere visto o modificato.
Tuttavia, quando sono dei vettori a fungere da parametri alle funzioni, questi non vengono copiati. Invece, il vettore stesso è reso disponibile per essere manipolato direttamente dalla funzione. Questo è quel che si dice solitamente una chiamata per riferimento. Le modifiche effettuate su un vettore passato come parametro all’interno del corpo di una funzione sono visibili all’esterno della funzione.
NOTA: Modificare un vettore passato come parametro all’interno di una funzione può essere molto pericoloso se non si sta attenti a quel che si sta facendo. Per esempio:
function cambialo(vettore, ind, nvalore) { vettore[ind] = nvalore } BEGIN { a[1] = 1; a[2] = 2; a[3] = 3 cambialo(a, 2, "due") printf "a[1] = %s, a[2] = %s, a[3] = %s\n", a[1], a[2], a[3] }stampa ‘a[1] = 1, a[2] = due, a[3] = 3’, perché
cambialo()
memorizza"due"
nel secondo elemento dia
.
Alcune implementazioni di awk
consentono di chiamare una
funzione che non è stata definita.
Viene solo emesso un messaggio che descrive il problema al momento
dell’esecuzione, se il programma tenta di chiamare quella funzione.
Per esempio:
BEGIN { if (0) pippo() else pluto() } function pluto() { … } # si noti che `pippo' non è definito
Poiché la condizione dell’istruzione ‘if’ non risulterà mai verificata
in questo caso,
non è un problema reale il fatto che
che pippo()
non sia stato definito. Solitamente, tuttavia,
è un problema se un programma chiama una funzione indefinita.
Se si specifica l’opzione --lint
(vedi Opzioni),
gawk
elenca le chiamate a funzioni indefinite.
Alcune implementazione di awk
emettono un messaggio di errore
se si usa l’istruzione next
o nextfile
(vedi Istruzione next, e
vedi Istruzione nextfile)
all’interno di una funzione definita dall’utente.
gawk
non ha questa limitazione.
Successivo: Variabili di tipo dinamico, Precedente: Precisazioni sulle funzioni, Su: Funzioni definite dall'utente [Contenuti][Indice]
return
Come visto in parecchi esempi precedenti,
il corpo di una funzione definita dall’utente può contenere un’istruzione
return
.
Quest’istruzione restituisce il controllo a quella parte del
del programma awk
che ha effettuato la chiamata.
Può anche essere usata per restituire un valore da usare nel resto del
programma awk
.
Questo è un esempio:
return
[espressione]
La parte espressione è facoltativa.
Probabilmente per una svista, POSIX non definisce qual è il valore
restituito, se si omette espressione. Tecnicamente parlando, questo
rende il valore restituito indefinito, e quindi, indeterminato.
In pratica, tuttavia, tutte le versioni di awk
restituiscono
semplicemente la stringa nulla, che vale zero se usata
in un contesto che richiede un numero.
Un’istruzione return
senza una espressione è considerata presente
alla fine di ogni definizione di funzione.
Quindi, se il flusso di esecuzione raggiunge la fine del corpo della
funzione, tecnicamente la funzione
restituisce un valore indeterminato.
In pratica, restituisce la stringa nulla. awk
non emette alcun messaggio di avvertimento se si usa
il valore restituito di una tale funzione.
Talvolta può capitare di scrivere una funzione per quello che fa, non per
quello che restituisce. Una tale funzione corrisponde a una funzione
void
in C, C++, o Java, o a una procedure
in Ada.
Quindi, può essere corretto non
restituire alcun valore; basta fare attenzione a non usare poi il
valore restituito da una tale funzione.
Quello che segue è un esempio di una funzione definita dall’utente che restituisce un valore che è il numero più alto presente tra gli elementi di un vettore:
function massimo(vettore, i, max) { for (i in vettore) { if (max == "" || vettore[i] > max) max = vettore[i] } return max }
La chiamata a massimo()
ha un solo argomento, che è il nome di
un vettore. Le variabili locali i
e max
non vanno intese
come argomenti; nulla vieta di passare più di un argomento
a massimo()
ma i risultati sarebbero strani. Gli spazi extra prima
di i
nella lista dei parametri della funzione indicano che i
e
max
sono variabili locali.
È consigliabile seguire questa convenzione quando si definiscono delle funzioni.
Il programma seguente usa la funzione massimo()
. Carica un vettore,
richiama massimo()
, e quindi elenca il numero massimo contenuto in
quel vettore:
function massimo(vettore, i, max) { for (i in vettore) { if (max == "" || vettore[i] > max) max = vettore[i] } return max } # Carica tutti i campi di ogni record in numeri. { for (i = 1; i <= NF; i++) numeri[NR, i] = $i } END { print massimo(numeri) }
Dato il seguente input:
1 5 23 8 16 44 3 5 2 8 26 256 291 1396 2962 100 -6 467 998 1101 99385 11 0 225
il programma trova (come si può immaginare) che 99.385 è il valore più alto contenuto nel vettore.
Precedente: Istruzione return, Su: Funzioni definite dall'utente [Contenuti][Indice]
awk
è un linguaggio molto fluido.
È possible che awk
non sia in grado di stabilire se un
identificativo rappresenta una variabile scalare o un vettore,
prima dell’effettiva esecuzione di un programma.
Ecco un esempio di programma commentato:
function pippo(a) { a[1] = 1 # il parametro è un vettore } BEGIN { b = 1 pippo(b) # non valido: errore fatale, tipi variabile in conflitto pippo(x) # x non inizializzato, diventa un vettore dinamicamente x = 1 # a questo punto, non permesso: errore in esecuzione }
In questo esempio, la prima chiamata a pippo()
genera
un errore fatale, quindi awk
non arriverà a segnalare il secondo
errore. Se si commenta la prima chiamata e si riesegue il
programma, a quel punto awk
terminerà con un messaggio
relativo al secondo errore.
Solitamente queste cose non causano grossi problemi, ma è bene
esserne a conoscenza.
Successivo: Sommario delle funzioni, Precedente: Funzioni definite dall'utente, Su: Funzioni [Contenuti][Indice]
Questa sezione descrive un’estensione avanzata, specifica di gawk
.
Spesso può essere utile ritardare la scelta della funzione da chiamare fino al momento in cui il programma viene eseguito. Per esempio, potrebbero esserci diversi tipi di record in input, ciascuno dei quali dovrebbe essere elaborato in maniera differente.
Solitamente, si userebbe una serie di istruzioni if
-else
per decidere quale funzione chiamare. Usando la chiamata indiretta
a una funzione, si può assegnare il nome della funzione da chiamare a
una variabile di tipo stringa, e usarla per chiamare la funzione.
Vediamo un esempio.
Si supponga di avere un file con i punteggi ottenuti negli esami per i corsi che si stanno seguendo, e che si desideri ottenere la somma e la media dei punteggi ottenuti. Il primo campo è il nome del corso. I campi seguenti sono i nomi delle funzioni da chiamare per elaborare i dati, fino a un campo “separatore” ‘dati:’. Dopo il separatore, fino alla fine del record, ci sono i vari risultati numerici di ogni test.
Ecco il file iniziale:
Biologia_101 somma media dati: 87.0 92.4 78.5 94.9 Chimica_305 somma media dati: 75.2 98.3 94.7 88.2 Inglese_401 somma media dati: 100.0 95.6 87.1 93.4
Per elaborare i dati, si potrebbe iniziare a scrivere:
{ corso = $1 for (i = 2; $i != "dati:"; i++) { if ($i == "somma") somma() # elabora l'intero record else if ($i == "media") media() … # e così via } }
Questo stile di programmazione funziona, ma può essere scomodo.
Con la chiamata indiretta di funzione, si può richiedere a gawk
di usare il valore di una variabile come nome della funzione da
chiamare.
La sintassi è simile a quella di una normale chiamata di funzione: un identificativo, seguito immediatamente da una parentesi aperta, qualche argomento, e una parentesi chiusa, con l’aggiunta di un carattere ‘@’ all’inizio:
quale_funzione = "somma" risultato = @quale_funzione() # chiamata della funzione somma()
Ecco un intero programma che elabora i dati mostrati sopra, usando la chiamata indiretta di funzioni:
# chiamataindiretta.awk --- esempio di chiamata indiretta di funzioni # media --- calcola la media dei valori dei campi $primo - $ultimo function media(primo, ultimo, somma, i) { somma = 0; for (i = primo; i <= ultimo; i++) somma += $i return somma / (ultimo - primo + 1) } # somma --- restituisce la somma dei valori dei campi $primo - $ultimo function somma(primo, ultimo, totale, i) { max = 0; for (i = primo; i <= ultimo; i++) totale += $i return totale }
Queste due funzioni presuppongono che si lavori con dei campi; quindi,
i parametri primo
e ultimo
indicano da quale campo iniziare
e fino a quale arrivare.
Per il resto, eseguono i calcoli richiesti, che sono i soliti:
# Per ogni record, # stampa il nome del corso e le statistiche richieste { nome_corso = $1 gsub(/_/, " ", nome_corso) # Rimpiazza _ con spazi # trova campo da cui iniziare for (i = 1; i <= NF; i++) { if ($i == "dati:") { inizio = i + 1 break } } printf("%s:\n", nome_corso) for (i = 2; $i != "dati:"; i++) { quale_funzione = $i printf("\t%s: <%s>\n", $i, @quale_funzione(inizio, NF) "") } print "" }
Questo è il ciclo principale eseguito per ogni record.
Stampa il nome del corso (con le
lineette basse sostituite da spazi). Trova poi l’inizio dei dati veri
e propri, salvandolo in inizio
.
L’ultima parte del codice esegue un ciclo per ogni nome di funzione
(da $2
fino al separatore, ‘dati:’), chiamando la funzione
il cui nome è specificato nel campo. La chiamata di funzione indiretta
compare come parametro nella chiamata a printf
.
(La stringa di formattazione di printf
usa ‘%s’ come
specificatore di formato, affinché sia possibile usare funzioni
che restituiscano sia stringhe che numeri. Si noti che il risultato
della chiamata indiretta è concatenato con la stringa nulla, in modo da
farlo considerare un valore di tipo stringa).
Ecco il risultato dell’esecuzione del programma:
$ gawk -f chiamataindiretta.awk dati_dei_corsi -| Biologia 101: -| somma: <352.8> -| media: <88.2> -| -| Chimica 305: -| somma: <356.4> -| media: <89.1> -| -| Inglese 401: -| somma: <376.1> -| media: <94.025>
La possibilità di usare la chiamata indiretta di funzioni è più potente
di quel che si possa pensare inizialmente.
I linguaggi C e C++ forniscono “puntatori di funzione” che
sono un metodo per chiamare una funzione scelta al momento dell’esecuzione.
Uno dei più noti usi di questa funzionalità è
la funzione C qsort()
, che ordina un vettore usando il famoso
algoritmo noto come “quicksort”
(si veda l’articolo di Wikipedia
per ulteriori informazioni). Per usare questa funzione, si specifica un
puntatore a una funzione di confronto. Questo meccanismo consente
di ordinare dei dati arbitrari in una maniera arbitraria.
Si può fare qualcosa di simile usando gawk
, così:
# quicksort.awk --- Algoritmo di quicksort, con funzione di confronto # fornita dall'utente # quicksort --- Algoritmo di quicksort di C.A.R. Hoare. # Si veda Wikipedia o quasi ogni libro # che tratta di algoritmi o di informatica. function quicksort(dati, sinistra, destra, minore_di, i, ultimo) { if (sinistra >= destra) # non fa nulla se il vettore contiene return # meno di due elementi quicksort_scambia(dati, sinistra, int((sinistra + destra) / 2)) ultimo = sinistra for (i = sinistra + 1; i <= destra; i++) if (@minore_di(dati[i], dati[sinistra])) quicksort_scambia(dati, ++ultimo, i) quicksort_scambia(dati, sinistra, ultimo) quicksort(dati, sinistra, ultimo - 1, minore_di) quicksort(dati, ultimo + 1, destra, minore_di) } # quicksort_scambia --- funzione ausiliaria per quicksort, # sarebbe meglio fosse nel programma principale function quicksort_scambia(dati, i, j, salva) { salva = dati[i] dati[i] = dati[j] dati[j] = salva }
La funzione quicksort()
riceve il vettore dati
, gli
indici iniziali e finali da ordinare
(sinistra
e destra
), e il nome di una funzione che
esegue un confronto “minore di”. Viene quindi eseguito
l’algoritmo di quicksort.
Per fare uso della funzione di ordinamento, torniamo all’esempio precedente. La prima cosa da fare è di scrivere qualche funzione di confronto:
# num_min --- confronto numerico per minore di function num_min(sinistra, destra) { return ((sinistra + 0) < (destra + 0)) } # num_magg_o_ug --- confronto numerico per maggiore o uguale function num_magg_o_ug(sinistra, destra) { return ((sinistra + 0) >= (destra + 0)) }
La funzione num_magg_o_ug()
serve per ottenere un ordinamento
decrescente (dal numero più alto al più basso); quando è usato
per eseguire un test per “minore di”, in realtà fa l’opposto
(maggiore o uguale a), il che conduce a ottenere dati ordinati
in ordine decrescente.
Poi serve una funzione di ordinamento.
Come parametri ha i numeri del campo iniziale e di quello finale,
e il nome della funzione di confronto.
Costruisce un vettore con
i dati e richiama appropriatamente quicksort()
; quindi formatta i
risultati mettendoli in un’unica stringa:
# ordina --- ordina i dati a seconda di `confronta' # e li restituisce come un'unica stringa function ordina(primo, ultimo, confronta, dati, i, risultato) { delete dati for (i = 1; primo <= ultimo; primo++) { dati[i] = $primo i++ } quicksort(dati, 1, i-1, confronta) risultato = dati[1] for (i = 2; i in dati; i++) risultato = risultato " " dati[i] return risultato }
Per finire, le due funzioni di ordinamento chiamano la funzione
ordina()
, passandole i nomi delle due funzioni di confronto:
# ascendente --- ordina i dati in ordine crescente # e li restituisce sotto forma di stringa function ascendente(primo, ultimo) { return ordina(primo, ultimo, "num_min") } # discendente --- ordina i dati in ordine decrescente # e li restituisce sotto forma di stringa function discendente(primo, ultimo) { return ordina(primo, ultimo, "num_magg_o_ug") }
Ecco una versione estesa del file-dati:
Biologia_101 somma media ordina discendente dati: 87.0 92.4 78.5 94.9 Chimica_305 somma media ordina discendente dati: 75.2 98.3 94.7 88.2 Inglese_401 somma media ordina discendente dati: 100.0 95.6 87.1 93.4
Per finire, questi sono i risultati quando si esegue il programma in questa versione migliorata:
$ gawk -f quicksort.awk -f indirettacall.awk class_data2 -| Biologia 101: -| somma: <352.8> -| media: <88.2> -| ascendente: <78.5 87.0 92.4 94.9> -| discendente: <94.9 92.4 87.0 78.5> -| -| Chimica 305: -| somma: <356.4> -| media: <89.1> -| ascendente: <75.2 88.2 94.7 98.3> -| discendente: <98.3 94.7 88.2 75.2> -| -| Inglese 401: -| somma: <376.1> -| media: <94.025> -| ascendente: <87.1 93.4 95.6 100.0> -| discendente: <100.0 95.6 93.4 87.1>
Un altro esempio in cui le chiamate indirette di funzione sono utili è costituito dall’elaborazione di vettori. La descrizione si può trovare Visitare vettori.
Occorre ricordarsi di anteporre il carattere ‘@’ prima di una chiamata indiretta di funzione.
A partire dalla versione 4.1.2 di gawk
, le chiamate
indirette di funzione
possono anche essere usate per chiamare funzioni predefinite e con
funzioni di estensione
(vedi Estensioni dinamiche). Ci sono alcune limitazioni nel richiamare
in maniera indiretta delle funzioni predefinite, come qui dettagliato:
sub()
, gsub()
, gensub()
, match()
,
split()
e patsplit()
.
sub()
o gsub()
, sono accettati solo due argomenti,
poiché queste funzioni sono atipiche, in quanto aggiornano il loro terzo
argomento. Questo significa che verrà sempre aggiornato l’argomento di
default, $0
.
gawk
fa del suo meglio per rendere efficiente la chiamata indiretta
di funzioni. Per esempio, nel ciclo seguente:
for (i = 1; i <= n; i++) @quale_funzione()
gawk
ricerca solo una volta quale funzione chiamare.
Precedente: Chiamate indirette, Su: Funzioni [Contenuti][Indice]
awk
include delle funzioni predefinite e consente all’utente
di definire le sue proprie funzioni.
awk
include tre tipi di funzioni predefinite: numeriche, di
stringa, e di I/O. gawk
prevede funzioni per ordinare vettori, per
lavorare con valori che rappresentano marcature temporali,
per la manipolazione di bit,
per determinare il tipo di una variabile (vettoriale piuttosto che scalare), e
programmi per l’internazionalizzazione e la localizzazione. gawk
prevede anche parecchie estensioni ad alcune funzioni standard, tipicamente
nella forma di ulteriori argomenti.
sub()
e gsub()
non è
semplice.
È più semplice nella funzione di gawk
gensub()
,
ma anche questa funzione richiede attenzione quando la si usa.
ARGC
)
come nome di un parametro in funzioni definite dall’utente.
return
serve per tornare indietro da una funzione definita
dall’utente. Un’espressione opzionale diviene il valore restituito dalla
funzione. Una funzione può solo restituire valori di tipo scalare.
gawk
consente la chiamata indiretta di funzioni usando una sintassi
speciale. Impostando una variabile al nome di una funzione, si può
determinare al momento dell’esecuzione che funzione sarà chiamata in un certo
punto del programma. Questo equivale a usare un puntatore a una funzione nei
linguaggi C e C++.
Successivo: Programmi di esempio, Precedente: Funzioni, Su: Top [Contenuti][Indice]
awk
awk
Funzioni definite dall'utente descrive come scrivere le proprie
funzioni awk
personali. Scrivere funzioni è importante, perché
consente di incapsulare in un unico contenitore algoritmi e azioni di
programma. Semplifica la programmazione, rendendo lo sviluppo di un programma
più gestibile, e rendendo i programmi più leggibili.
Nel loro autorevole libro del 1976, Software Tools,65 Brian Kernighan e P.J. Plauger hanno scritto:
A programmare bene non s’impara dai concetti generali, ma vedendo come programmi complessi possono essere resi puliti, facili da leggere, facili da manutenere e modificare, strutturati in modo comprensibile, efficienti e affidabili, applicando il buon senso e delle buone pratiche di programmazione. Lo studio attento e l’imitazione di buoni programmi conduce a una migliore scrittura.
In effetti, loro reputavano quest’idea tanto importante da mettere questa frase sulla copertina del libro. Poiché credo fermamente che la loro affermazione sia corretta, questo capitolo e Programmi di esempio forniscono una corposa raccolta di codice da leggere e, si spera, da cui imparare.
Questo capitolo illustra una libreria di utili funzioni awk
.
Molti dei programmi descritti nel seguito di questo Documento
usano queste funzioni.
Le funzioni sono illustrate progressivamente, dalla più semplice alla più
complessa.
Programma extract
illustra un programma che si può usare per estrarre il codice sorgente
degli esempi di funzioni di libreria e di programmi dal sorgente Texinfo
di questo Documento.
(Questo è già stato fatto durante la preparazione della distribuzione
di gawk
.)
Chi avesse scritto una o più funzioni awk
utili e di uso
generale, e volesse metterle a disposizione della comunità degli utenti di
awk
, può leggere le informazioni contenute in
Come contribuire.
I programmi contenuti in questo capitolo e in
Programmi di esempio,
utilizzano anche le funzionalità specifiche di gawk
.
Riscrivere questi programmi per implementazioni di awk
diverse
è piuttosto semplice:
gawk
.
nextfile
(vedi Istruzione nextfile)
per evitare di leggere gli input ancora non letti dal file in input corrente.
IGNORECASE
.
Si può ottenere quasi lo stesso effetto66
aggiungendo la seguente regola
all’inizio del programma:
# ignora maiuscolo/minuscolo { $0 = tolower($0) }
Inoltre, si verifichi che tutte le regexp e le costanti di tipo stringa usate nei confronti utilizzano solo lettere minuscole.
• Nomi di variabili di libreria: | Che nomi è meglio dare alle variabili private globali nelle funzioni di libreria. | |
• Funzioni di tipo generale: | Funzioni di uso generale. | |
• Gestione File Dati: | Funzioni per gestire file-dati specificati sulla riga di comando. | |
• Funzione getopt: | Una funzione per trattare argomenti presenti sulla riga di comando. | |
• Funzioni Passwd: | Funzioni per ottenete informazioni sull’utente [da /etc/passwd]. | |
• Funzioni Group: | Funzioni per ottenete informazioni sul gruppo [da /etc/group]. | |
• Visitare vettori: | Una funzione per visitare vettori di vettori. | |
• Sommario funzioni di libreria: | Sommario funzioni di libreria | |
• Esercizi con le librerie: | Esercizi. |
Successivo: Funzioni di tipo generale, Su: Funzioni di libreria [Contenuti][Indice]
Per come si è sviluppato il linguaggio awk
, le variabili sono
o globali (usabili dall’intero programma) o locali (usabili solo
in una specifica funzione). Non c’è uno stato intermedio analogo alle
variabili statiche
in C.
Le funzioni di libreria hanno spesso necessità di avere variabili globali da
usare per conservare informazioni di stato tra successive chiamate alla
funzione; per esempio, la variabile di getopt()
_opti
(vedi Funzione getopt).
Tali variabili vengono dette private, poiché le sole funzioni che
devono usarle sono quelle della libreria.
Quando si scrive una funzione di libreria, si dovrebbe cercare di scegliere per
le variabili private dei nomi che non entrano in conflitto con nessuna delle
variabili usate da un’altra funzione di libreria o dal programma principale di
un utente. Per esempio, un nome come i
o j
non è una buona
scelta, perché i programmi a livello utente usano spesso nomi di variabile come
questi per le proprie elaborazioni.
I programmi di esempio mostrati in questo capitolo usano per le loro variabili private nomi che iniziano con un trattino basso(‘_’). Generalmente gli utenti non usano trattini bassi iniziali nei nomi di variabile, così questa convenzione riduce le possibilità che il nome di variabile coincida con un nome usato nel programma dell’utente.
Inoltre, parecchie funzioni di libreria usano un prefisso che suggerisce
quale funzione o gruppo di funzioni usa quelle variabili; per esempio,
_pw_byname()
nelle routine che consultano la lista degli utenti
(vedi Funzioni Passwd).
L’uso di questa convenzione viene raccomandata, poiché riduce ulteriormente la
possibilità di conflitti accidentali tra nomi di variabile. Si noti che questa
convenzione può anche essere usata per i nomi di variabile e per i nomi delle
funzioni private.67
Come nota finale sui nomi delle variabili, se una funzione rende
disponibile una variabile globale per essere usata da un programma principale,
è una buona convenzione quella di far iniziare i nomi di queste variabili con
una lettera maiuscola; per esempio, Opterr
e Optind
di
getopt()
(vedi Funzione getopt).
La lettera maiuscola iniziale indica che la variabile è globale,
mentre il fatto che
il nome della variabile non è tutto in lettere maiuscole indica che la variabile
non è una delle variabili predefinite di awk
, come FS
.
È importante anche che tutte le variabili nelle funzioni di libreria che non abbiano la necessità di essere conservate per tutta la durata del programma siano, di fatto, dichiarate come locali.68 Se ciò non viene fatto, la variabile potrebbe essere usata accidentalmente nel programma dell’utente, conducendo a errori che sono molto difficili da scoprire:
function lib_func(x, y, l1, l2) { … # qualche_var dovrebbe essere locale ma per una svista non lo è uso della variabile qualche_var … }
Una differente convenzione, comune nella comunità Tcl, è quella di usare un
solo vettore associativo che contiene i valori necessari alle funzioni di
libreria, o “package.” Questo riduce significativamente il numero degli
effettivi nomi globali in uso. Per esempio, le funzioni descritte in
Funzioni Passwd
potrebbero aver usato gli elementi di vettore
PW_data["inizializzato"]
,
PW_data["totale"]
, PW_data["contatore"]
, e
PW_data["awklib"]
, al posto di _pw_inizializzato
,
_pw_totale
,
_pw_awklib
e
_pw_contatore
.
Le convenzioni illustrate in questa sezione sono esattamente quello che indica il termine: convenzioni. Non si è obbligati a scrivere i propri programmi in questo modo: è solo auspicabile che lo si faccia.
Successivo: Gestione File Dati, Precedente: Nomi di variabili di libreria, Su: Funzioni di libreria [Contenuti][Indice]
Questa sezione illustra diverse funzioni che sono di uso generale nella programmazione.
• Funzione strtonum: | Da usare se non è disponibile la funzione
predefinita strtonum() .
| |
• Funzione assert: | Una funzione per controllare affermazioni
in programmi awk .
| |
• Funzione round: | Una funzione per eseguire arrotondamenti
se sprintf() non lo fa correttamente.
| |
• Funzione random Cliff: | Il generatore Cliff di numeri casuali. | |
• Funzioni ordinali: | Funzioni per usare caratteri come numeri e viceversa. | |
• Funzione join: | Una funzione per fondere un vettore in una stringa. | |
• Funzione getlocaltime: | Una funzione per ottenere data e ora nel formato desiderato. | |
• Funzione readfile: | Una funzione per leggere un file intero in un colpo solo. | |
• Apici alla shell: | Una funzione per passare stringhe con apici alla shell. |
Successivo: Funzione assert, Su: Funzioni di tipo generale [Contenuti][Indice]
La funzione strtonum()
(vedi Funzioni per stringhe)
è un’estensione gawk
. La seguente funzione
fornisce un’implementazione per altre versioni di awk
:
# mystrtonum --- converte stringhe in numeri function mystrtonum(str, ret, n, i, k, c) { if (str ~ /^0[0-7]*$/) { # ottale n = length(str) ret = 0 for (i = 1; i <= n; i++) { c = substr(str, i, 1) # index() restituisce 0 se c non è nella stringa, # e anche se c == "0" k = index("1234567", c) ret = ret * 8 + k } } else if (str ~ /^0[xX][[:xdigit:]]+$/) { # esadecimale str = substr(str, 3) # via 0x iniziale n = length(str) ret = 0 for (i = 1; i <= n; i++) { c = substr(str, i, 1) c = tolower(c) # index() restituisce 0 se c non è nella stringa, # e anche se c == "0" k = index("123456789abcdef", c) ret = ret * 16 + k } } else if (str ~ \ /^[-+]?([0-9]+([.][0-9]*([Ee][0-9]+)?)?|([.][0-9]+([Ee][-+]?[0-9]+)?))$/) { # numero decimale, eventualmente in virgola mobile ret = str + 0 } else ret = "NON-UN-NUMERO" return ret } # BEGIN { # dati per un test # a[1] = "25" # a[2] = ".31" # a[3] = "0123" # a[4] = "0xdeadBEEF" # a[5] = "123.45" # a[6] = "1.e3" # a[7] = "1.32" # a[8] = "1.32E2" # # for (i = 1; i in a; i++) # print a[i], strtonum(a[i]), mystrtonum(a[i]) # }
La funzione cerca dapprima numeri ottali in stile C (base 8).
Se la stringa in input corrisponde all’espressione regolare che descrive i
numeri ottali, mystrtonum()
esegue il ciclo per ogni carattere presente
nella stringa. Imposta k
all’indice in "1234567"
della cifra
ottale corrente.
Il valore di ritorno sarà lo stesso numero della cifra, o zero
se il carattere non c’è, il che succederà per ogni cifra ‘0’.
Questo si può fare, perché il test di regexp nell’istruzione if
assicura che vengano scelti per
essere convertiti solo dei numeri ottali.
Una logica simile si applica al codice che ricerca e converte un
valore esadecimale, che inizia con ‘0x’ o ‘0X’.
L’uso di tolower()
semplifica il calcolo per trovare
il valore numerico corretto per ogni cifra esadecimale.
Infine, se la stringa corrisponde alla (piuttosto complicata) regexp per
un intero decimale regolare o per un numero in virgola mobile, il calcolo
‘ret = str + 0’ fa sì che awk
converta il valore in un
numero.
È incluso un programma di verifica commentato, in modo che la funzione possa
essere verificata con gawk
e il risultato confrontato con la funzione
predefinita strtonum()
.
Successivo: Funzione round, Precedente: Funzione strtonum, Su: Funzioni di tipo generale [Contenuti][Indice]
Quando si scrivono grossi programmi, spesso è utile sapere se
una condizione o una serie di condizioni è verificata oppure no.
Prima di procedere
con un determinato calcolo, si fa un’affermazione su cosa si crede sia
vero. Tale affermazione è nota come
asserzione. Il linguaggio C fornisce un file di intestazione
<assert.h>
e una corrispondente macro assert()
che un
programmatore può utilizzare per fare asserzioni.
Se l’asserzione risulta falsa, la macro assert()
predispone la
stampa di un messaggio diagnostico che descrive la condizione che
sarebbe dovuta essere vera ma che non lo era, e poi fa terminare
il programma.
In C, l’uso di assert()
è simile a questo:
#include <assert.h> int myfunc(int a, double b) { assert(a <= 5 && b >= 17.1); … }
Se l’asserzione è falsa, il programma stampa un messaggio simile a questo:
prog.c:5: asserzione falsa: `a <= 5 && b >= 17.1'
Il linguaggio C rende possibile trasformare questa condizione in una stringa
da
usare per stampare il messaggio di diagnosi. Ciò in awk
non è
possibile, per cui la funzione assert()
scritta in awk
richiede anche una descrizione
della condizione da verificare, in formato stringa.
La funzione è la seguente:
# assert --- Verifica una condizione. Se questa è falsa esce. function assert(condizione, stringa) { if (! condizione) { printf("%s:%d: asserzione falsa: %s\n", FILENAME, FNR, stringa) > "/dev/stderr" _assert_exit = 1 exit 1 } }
END { if (_assert_exit) exit 1 }
La funzione assert()
verifica il parametro condizione
. Se
è falso, stampa un messaggio sullo standard error, usando il parametro
stringa
per descrivere la condizione non verificata. Poi imposta la
variabile _assert_exit
a uno ed esegue l’istruzione exit
.
L’istruzione exit
salta alla regola END
. Se la regola END
trova vera la variabile _assert_exit
, esce immediatamente.
Lo scopo della verifica nella regola END
è quello di evitare che venga
eseguita qualsiasi altra eventuale regola END
.
Quando un’asserzione non è
verificata, il programma dovrebbe uscire immediatamente.
Se nessuna asserzione
fallisce, _assert_exit
è ancora falso quando la regola END
è
eseguita normalmente, e le eventuali altre regole END
del programma
vengono eseguite.
Affinché tutto questo funzioni correttamente, assert.awk dev’essere il
primo file sorgente che viene letto da awk
.
La funzione può essere usata in un programma nel seguente modo:
function miafunz(a, b) { assert(a <= 5 && b >= 17.1, "a <= 5 && b >= 17.1") … }
Se l’asserzione non è verificata, si vedrà un messaggio simile a questo:
mydata:1357: asserzione falsa: a <= 5 && b >= 17.1
C’è un piccolo problema con questa versione di assert()
.
Come visto, una regola END
viene automaticamente aggiunta al programma
che chiama assert()
. Normalmente, se un programma consiste
solo di una regola BEGIN
, i file in input e/o lo standard input non
vengono letti. Tuttavia, ora che il programma ha una regola END
,
awk
tenta di leggere i file-dati in input o lo standard input
(vedi Usare BEGIN/END), provocando molto probabilmente la sospensione del
programma come se rimanesse in attesa di input.
C’è un modo per aggirare questo problema:
assicurarsi che la regola BEGIN
termini sempre
con un’istruzione exit
.
Successivo: Funzione random Cliff, Precedente: Funzione assert, Su: Funzioni di tipo generale [Contenuti][Indice]
Il modo in cui printf
e sprintf()
(vedi Printf)
effettuano l’arrotondamento spesso dipende dalla subroutine C sprintf()
del sistema. Su molte macchine, l’arrotondamento di sprintf()
è
statistico, il che significa che non sempre arrotonda un .5 finale per
eccesso, contrariamente alle normali aspettative. Nell’arrotondamento
statistico, .5 arrotonda alla cifra pari, anziché sempre per eccesso, così
1.5 arrotonda a 2 e 4.5 arrotonda a 4. Ciò significa che se si sta usando un
formato che fa arrotondamenti (p.es. "%.0f"
), si dovrebbe controllare
quello che fa il sistema che si sta usando. La seguente funzione esegue un
arrotondamento tradizionale; potrebbe essere utile nel caso in cui
l’istruzione printf
di awk
che si sta usando faccia degli arrotondamenti statistici:
# round.awk --- effettua arrotondamento tradizionale function round(x, ival, aval, frazione) { ival = int(x) # parte intera, int() fa un troncamento # vedere se c'è la parte frazionale if (ival == x) # nessuna parte frazionale return ival # nessun decimale if (x < 0) { aval = -x # valore assoluto ival = int(aval) frazione = aval - ival if (frazione >= .5) return int(x) - 1 # -2.5 --> -3 else return int(x) # -2.3 --> -2 } else { frazione = x - ival if (frazione >= .5) return ival + 1 else return ival } } # codice per testare, commentato # { print $0, round($0) }
Successivo: Funzioni ordinali, Precedente: Funzione round, Su: Funzioni di tipo generale [Contenuti][Indice]
Il
generatore di numeri casuali Cliff
è un generatore di numeri casuali molto semplice che “passa il test della sfera
del rumore per la casualità non mostrando di avere alcuna struttura.”
È programmato in modo molto semplice, in meno di 10 righe di codice
awk
:
# cliff_rand.awk --- generare numeri casuali con algoritmo di Cliff BEGIN { _cliff_seme = 0.1 } function cliff_rand() { _cliff_seme = (100 * log(_cliff_seme)) % 1 if (_cliff_seme < 0) _cliff_seme = - _cliff_seme return _cliff_seme }
Questo algoritmo richiede un “seme” iniziale di 0,1. Ogni nuovo valore
usa il seme corrente come input per il calcolo.
Se la funzione predefinita rand()
(vedi Funzioni numeriche)
non è abbastanza casuale, si può tentare di usare al suo posto questa funzione.
Successivo: Funzione join, Precedente: Funzione random Cliff, Su: Funzioni di tipo generale [Contenuti][Indice]
Un’implementazione commerciale di awk
fornisce una funzione
predefinita ord()
, che prende un carattere e restituisce il valore
numerico per quel carattere nella rappresentazione dei caratteri
di quella particolare macchina. Se la
stringa passata a ord()
ha più di un carattere, viene usato solo il
primo.
L’inverso di questa funzione è chr()
(dalla funzione con lo stesso nome
in Pascal), che, dato un numero, restituisce il corrispondente carattere.
Entrambe le funzioni si possono scrivere molto bene usando awk
;
non vi è nessun reale motivo per inglobarle come funzioni predefinite
awk
:
# ord.awk --- implementa ord e chr # Identificatori globali: # _ord_: valori numerici indicizzati da caratteri # _ord_init: funzione per inizializzare _ord_ BEGIN { _ord_init() } function _ord_init( basso, alto, i, t) { basso = sprintf("%c", 7) # BEL è ascii 7 if (basso == "\a") { # ascii regolare basso = 0 alto = 127 } else if (sprintf("%c", 128 + 7) == "\a") { # ascii, con il primo bit a 1 (mark) basso = 128 alto = 255 } else { # ebcdic(!) basso = 0 alto = 255 } for (i = basso; i <= alto; i++) { t = sprintf("%c", i) _ord_[t] = i } }
Alcune spiegazioni riguardo ai numeri usati da _ord_init()
non guastano.
La serie di caratteri più importante oggi in uso è nota come
ASCII.69
Sebbene un byte a
8 bit possa contenere 256 valori distinti (da 0 a 255), ASCII definisce solo i
caratteri che usano i valori da 0 a 127.70 Nel lontano passato,
almeno un produttore di microcomputer
ha usato ASCII, ma con una parità di tipo mark, cioè con il bit più a
sinistra sempre a 1. Questo significa che su questi sistemi i caratteri
ASCII hanno valori numerici da 128 a 255.
Infine, i grandi elaboratori centrali usano la serie di caratteri EBCDIC, che
prevede tutti i 256 valori.
Ci sono altre serie di caratteri in uso su alcuni sistemi più vecchi, ma non
vale la pena di considerarli:
function ord(str, c) { # solo il primo carattere è d'interesse c = substr(str, 1, 1) return _ord_[c] } function chr(c) { # trasforma c in un numero aggiungendo uno 0 return sprintf("%c", c + 0) } #### programma di verifica #### # BEGIN { # for (;;) { # printf("immetti un carattere: ") # if (getline var <= 0) # break # printf("ord(%s) = %d\n", var, ord(var)) # } # }
Un ovvio miglioramento a queste funzioni è quello di spostare il codice per la
funzione _ord_init
nel corpo della regola BEGIN
.
Il programma è
stato scritto inizialmente in questo modo per comodità di sviluppo.
C’è un “programma di verifica” in una regola BEGIN
, per verificare
la funzione. È commentato, per poter essere eventualmente usato in produzione.
Successivo: Funzione getlocaltime, Precedente: Funzioni ordinali, Su: Funzioni di tipo generale [Contenuti][Indice]
Quando si fanno elaborazioni su stringhe, spesso è utile poter unire
tutte le stringhe di un vettore in una lunga stringa. La funzione seguente,
join()
, svolge questo compito. Verrà utilizzata nel seguito in diversi
programmi applicativi
(vedi Programmi di esempio).
La buona progettazione di una funzione è importante; la funzione dev’essere
generale, ma potrebbe anche avere un ragionevole comportamento di default.
Viene chiamata con un vettore e anche con gli indici iniziale e finale degli
elementi del vettore da riunire.
Questo presuppone che gli indici del vettore
siano numerici—una supposizione logica, dato che il vettore probabilmente è
stato creato con split()
(vedi Funzioni per stringhe):
# join.awk --- trasforma un vettore in una stringa function join(vettore, iniz, fine, separ, risultato, i) { if (separ == "") separ = " " else if (separ == SUBSEP) # valore magico separ = "" risultato = vettore[iniz] for (i = iniz + 1; i <= fine; i++) risultato = risultato separ vettore[i] return risultato }
Un ulteriore argomento opzionale è il separatore da usare quando si uniscono
nuovamente le stringhe. Se il chiamante fornisce un valore non nullo,
join()
usa quello; se non viene fornito,
per default ha un valore nullo.
In questo caso, join()
usa uno spazio singolo come separatore
di default
per le stringhe. Se il valore è uguale a SUBSEP
,
join()
unisce le stringhe senza un separatore tra di esse.
SUBSEP
serve come valore “magico” per indicare che potrebbe non esserci
un separatore tra le stringhe componenti.71
Successivo: Funzione readfile, Precedente: Funzione join, Su: Funzioni di tipo generale [Contenuti][Indice]
Le funzioni systime()
e strftime()
descritte nella
Funzioni di tempo
forniscono la funzionalità minima necessaria per visualizzare l’ora del giorno
in una forma intelligibile. Sebbene strftime()
offra un’ampia gamma di
formattazioni, i formati di controllo non sono facili da ricordare o
intuitivamente ovvii quando si legge un programma.
La seguente funzione, getlocaltime()
, riempie un vettore fornito
dall’utente con informazioni sul tempo preformattate. Restituisce una stringa
con data e ora corrente formattata come nel programma di utilità date
:
# getlocaltime.awk --- ottiene l'ora del giorno in un formato usabile # Restituisce una stringa nel formato dell'output di date(1) # Riempie l'argomento del vettore time con valori individuali: # time["second"] -- secondi (0 - 59) # time["minute"] -- minuti (0 - 59) # time["hour"] -- ore (0 - 23) # time["althour"] -- ore (0 - 12) # time["monthday"] -- giorno del mese (1 - 31) # time["month"] -- mese dell'anno (1 - 12) # time["monthname"] -- nome del mese # time["shortmonth"] -- nome breve del mese # time["year"] -- anno modulo 100 (0 - 99) # time["fullyear"] -- anno completo # time["weekday"] -- giorno della settimana (domenica = 0) # time["altweekday"] -- giorno della settimana (lunedì = 0) # time["dayname"] -- nome del giorno della settimana # time["shortdayname"] -- nome breve del giorno della settimana # time["yearday"] -- giorno dell'anno (0 - 365) # time["timezone"] -- abbreviazione del nome della zona di fuso orario # time["ampm"] -- designazione di AM o PM # time["weeknum"] -- numero della settimana, domenica primo giorno # time["altweeknum"] -- numero della settimana, lunedì primmo giorno function getlocaltime(ora, ret, adesso, i) { # ottiene data e ora una volta sola, # evitando chiamate di sistema non necessarie adesso = systime() # restituisce l'output in stile date(1) ret = strftime("%a %e %b %Y, %H.%M.%S, %Z", adesso) # clear out target array delete time # immette i valori, forzando i valori numerici # a essere numerici aggiungendo uno 0 time["second"] = strftime("%S", adesso) + 0 time["minute"] = strftime("%M", adesso) + 0 time["hour"] = strftime("%H", adesso) + 0 time["althour"] = strftime("%I", adesso) + 0 time["monthday"] = strftime("%d", adesso) + 0 time["month"] = strftime("%m", adesso) + 0 time["monthname"] = strftime("%B", adesso) time["shortmonth"] = strftime("%b", adesso) time["year"] = strftime("%y", adesso) + 0 time["fullyear"] = strftime("%Y", adesso) + 0 time["weekday"] = strftime("%w", adesso) + 0 time["altweekday"] = strftime("%u", adesso) + 0 time["dayname"] = strftime("%A", adesso) time["shortdayname"] = strftime("%a", adesso) time["yearday"] = strftime("%j", adesso) + 0 time["timezone"] = strftime("%Z", adesso) time["ampm"] = strftime("%p", adesso) time["weeknum"] = strftime("%U", adesso) + 0 time["altweeknum"] = strftime("%W", adesso) + 0 return ret }
Gli indici di stringa sono più facili da usare e leggere rispetto ai
vari formati
richiesti da strftime()
. Il programma alarm
illustrato in
Programma alarm
usa questa funzione.
Una progettazione più generica della funzione getlocaltime()
avrebbe permesso all’utente di fornire un valore di data e ora
opzionale da usare al posto della data/ora corrente.
Successivo: Apici alla shell, Precedente: Funzione getlocaltime, Su: Funzioni di tipo generale [Contenuti][Indice]
Spesso è conveniente avere il contenuto di un intero file disponibile in memoria, visto come un’unica stringa. Un modo chiaro e semplice per far ciò potrebbe essere questo:
function readfile(file, temp, contenuto) { if ((getline temp < file) < 0) return contenuto = temp while (getline temp < file) > 0) contenuto = contenuto RT tmp close(file) return contenuto }
Questa funzione legge da file
un record alla volta, ricostruendo
l’intero contenuto del file nella variabile locale contenuto
.
Funziona, ma non è detto che sia efficiente.
La funzione seguente, basata su un suggerimento di Denis Shirokov, legge l’intero contenuto del file in un colpo solo:
# readfile.awk --- legge un intero file in un colpo solo function readfile(file, temp, salva_rs) { salva_rs = RS RS = "^$" getline temp < file close(file) RS = salva_rs return temp }
Funziona impostando RS
a ‘^$’, un’espressione regolare che
non trova nessuna corrispondenza se il file ha un contenuto.
gawk
legge i dati dal file contenuto in temp
, tentando di trovare una
corrispondenza con RS
.
La ricerca dopo ogni lettura non ha mai successo, ma termina
rapidamente, e quindi gawk
inserisce in
temp
l’intero contenuto del file.
(Vedi Record per informazioni su RT
e RS
.)
Se file
è vuoto, il valore di ritorno è la stringa vuota.
Quindi, il codice chiamante può usare qualcosa simile a questo:
contenuto = readfile("/qualche/percorso") if (length(contenuto) == 0) # file vuoto …
La verifica serve a determinare se il file è vuoto o no. Una verifica equivalente potrebbe essere ‘contenuto == ""’.
Vedi Esempio di estensione Readfile per una funzione di estensione anch’essa finalizzata a leggere un intero file in memoria.
Precedente: Funzione readfile, Su: Funzioni di tipo generale [Contenuti][Indice]
Michael Brennan propone il seguente modello di programma, da lui usato spesso:
#! /bin/sh awkp=' … ' specifica_programma_da_eseguire | awk "$awkp" | /bin/sh
Per esempio, un suo programma chiamato flac-edit
72 ha questa forma:
$ flac-edit -song="Whoope! That's Great" file.flac
flac-edit
genera in output il seguente script, da passare alla
shell (/bin/sh) per essere eseguito:
chmod +w file.flac metaflac --remove-tag=TITLE file.flac LANG=en_US.88591 metaflac --set-tag=TITLE='Whoope! That'"'"'s Great' file.flac chmod -w file.flac
Si noti la necessità di gestire gli apici nello script da passare alla shell.
La funzione
shell_quote()
li prepara nel formato richiesto.
SINGLE
è la stringa di un solo
carattere "'"
e QSINGLE
è la stringa di tre caratteri
"\"'\""
:
# shell_quote --- pone tra apici un argomento da passare alla shell function shell_quote(s, # parametro SINGLE, QSINGLE, i, X, n, ret) # variabili locali { if (s == "") return "\"\"" SINGLE = "\x27" # apice singolo QSINGLE = "\"\x27\"" # apice singolo incapsulato n = split(s, X, SINGLE) ret = SINGLE X[1] SINGLE for (i = 2; i <= n; i++) ret = ret QSINGLE SINGLE X[i] SINGLE return ret }
Successivo: Funzione getopt, Precedente: Funzioni di tipo generale, Su: Funzioni di libreria [Contenuti][Indice]
Questa sezione presenta funzioni utili per gestire file-dati da riga di comando.
• Funzione filetrans: | Una funzione per gestire il passaggio da un file in input al successivo. | |
• Funzione rewind: | Una funzione per rileggere il file in input. | |
• Controllo di file: | Controllare che i file in input siano accessibili. | |
• File vuoti: | Controllare se i file in input sono vuoti. | |
• Ignorare assegnamenti di variabili: | Trattare assegnamenti di variabili. come nomi di file. |
Successivo: Funzione rewind, Su: Gestione File Dati [Contenuti][Indice]
Ognuna delle regole BEGIN
ed END
viene eseguita esattamente
solo una volta, rispettivamente all’inizio e alla fine del programma
awk
(vedi BEGIN/END).
Una volta noi (gli autori di gawk
) siamo venuti in contatto
con un utente che
erroneamemnte pensava che le regole BEGIN
venissero eseguite all’inizio
di ogni file-dati e le regole END
alla fine di ogni file-dati.
Quando lo abbiamo informato che
non era così, ci ha chiesto di aggiungere un nuovo criterio di ricerca speciale
a gawk
, chiamato BEGIN_FILE
e END_FILE
, che avesse il
comportamento desiderato. Ci ha fornito anche il codice per far questo.
Non è stato necessario aggiungere a gawk
questi criteri di ricerca
speciali; il lavoro si può fare tranquillamente usando awk
, come
illustrato nel seguente programma di libreria. È strutturato in modo da
chiamare due funzioni fornite dall’utente, a_inizio_file()
e
a_fine_file()
, all’inizio e alla fine di ogni file-dati. Oltre a risolvere
il problema in sole nove(!) righe di codice,
questa soluzione è portabile; il
programma funziona con qualsiasi implementazione di awk
:
# transfile.awk # # Dare all'utente un aggancio per il passaggio # da un file in input a quello successivo # # L'utente deve fornire le funzioni a_inizio_file() ed a_fine_file() # ciascuna delle quali è invocata # quando il file, rispettivamente, # inizia e finisce. FILENAME != _nome_file_vecchio { if (_nome_file_vecchio != "") a_fine_file(_nome_file_vecchio) _nome_file_vecchio = FILENAME a_inizio_file(FILENAME) } END { a_fine_file(FILENAME) }
Questo file [transfile.awk] dev’essere caricato prima del programma “principale” dell’utente, in modo che la regola ivi contenuta venga eseguita per prima.
Questa regola dipende dalla variabile di awk
FILENAME
, che
cambia automaticamente per ogni nuovo file-dati. Il nome-file corrente viene
salvato in una variabile privata, _nome_file_vecchio
. Se FILENAME
non è
uguale a _nome_file_vecchio
, inizia l’elaborazioone di un nuovo file-dati ed
è necessario chiamare a_fine_file()
per il vecchio file. Poiché
a_fine_file()
dovrebbe essere chiamato solo se un file è stato elaborato, il
programma esegue prima un controllo per assicurarsi che _nome_file_vecchio
non
sia la stringa nulla. Il programma assegna poi il valore corrente di
nome-file a _nome_file_vecchio
e chiama a_inizio_file()
per il file.
Poiché, come tutte le variabili di awk
, _nome_file_vecchio
è
inizializzato alla stringa nulla, questa regola viene eseguita correttamente
anche per il primo file-dati.
Il programma contiene anche una regola END
per completare l’elaborazione
per l’ultimo file. Poiché questa regola END
viene prima di qualsiasi
regola END
contenuta nel programma “principale”,
a_fine_file()
viene
chiamata per prima. Ancora una volta, l’utilità di poter avere più regole
BEGIN
ed END
dovrebbe risultare chiara.
Se lo stesso file-dati compare due volte di fila sulla riga di comando,
a_fine_file()
e a_inizio_file()
non vengono eseguite alla fine del primo
passaggio e all’inizio del secondo passaggio.
La versione seguente risolve il problema:
# ftrans.awk --- gestisce il passaggio da un file dati al successivo # # L'utente deve fornire le funzioni a_inizio_file() ed a_fine_file() FNR == 1 { if (_filename_ != "") a_fine_file(_filename_) _filename_ = FILENAME a_inizio_file(FILENAME) } END { a_fine_file(_filename_) }
Programma wc mostra come utilizzare questa funzione di libreria e come ciò semplifichi la scrittura del programma principale.
Allora perché
gawk ha BEGINFILE e ENDFILE ?
Ci si chiederà, probabilmente: perché, se le funzioni Buona domanda. Normalmente, se |
Successivo: Controllo di file, Precedente: Funzione filetrans, Su: Gestione File Dati [Contenuti][Indice]
Un’altra richiesta per una nuova funzione predefinita è stata per
una funzione per rileggere il file corrente.
L’utente che l’ha richiesta non voleva dover usare getline
(vedi Getline)
all’interno di un ciclo.
Comunque, se non si è nella regola END
, è piuttosto facile
fare in modo di chiudere il corrente file in input immediatamente
e ricominciare a leggerlo dall’inizio.
In mancanza di un nome migliore, chiameremo la funzione rewind()
:
# rewind.awk --- ricarica il file corrente e ricomincia a leggerlo function rewind( i) { # sposta in alto i rimanenti argomenti for (i = ARGC; i > ARGIND; i--) ARGV[i] = ARGV[i-1] # assicurarsi che gawk sappia raggiungerli ARGC++ # fa sì che il file corrente sia il prossimo a essere letto ARGV[ARGIND+1] = FILENAME # do it nextfile }
La funzione rewind()
dipende dalla variabile ARGIND
(vedi Variabili auto-assegnate), che è specifica di gawk
. Dipende anche
dalla parola chiave nextfile
(vedi Istruzione nextfile).
Perciò, non si dovrebbe chiamarla da una regola ENDFILE
.
(Non sarebbe peraltro necessario, perché gawk
legge il file
successivo non appena la regola ENDFILE
finisce!)
Occorre prestare attenzione quando si chiama rewind()
. Si può
provocare una ricorsione infinita se non si sta attenti. Ecco un
esempio di uso:
$ cat dati -| a -| b -| c -| d -| e $ cat test.awk -| FNR == 3 && ! riavvolto { -| riavvolto = 1 -| rewind() -| } -| -| { print FILENAME, FNR, $0 } $ gawk -f rewind.awk -f test.awk dati -| data 1 a -| data 2 b -| data 1 a -| data 2 b -| data 3 c -| data 4 d -| data 5 e
Successivo: File vuoti, Precedente: Funzione rewind, Su: Gestione File Dati [Contenuti][Indice]
Normalmente, se si fornisce ad awk
un file-dati che non è leggibile,
il programma
si arresta con un errore fatale. Ci sono casi in cui sarebbe preferibile
ignorare semplicemente questi file e proseguire.73
Si può far questo facendo precedere il proprio programma awk
dal
seguente programma:
# readable.awk --- file di libreria per saltare file non leggibili BEGIN { for (i = 1; i < ARGC; i++) { if (ARGV[i] ~ /^[a-zA-Z_][a-zA-Z0-9_]*=.*/ \ || ARGV[i] == "-" || ARGV[i] == "/dev/stdin") continue # assegnamento di variabile o standard input else if ((getline aperdere < ARGV[i]) < 0) # file non leggibile delete ARGV[i] else close(ARGV[i]) } }
Questo codice funziona, perché l’errore di getline
non è fatale.
Rimuovendo l’elemento da ARGV
con delete
si tralascia il file (perché non è più nella lista).
Si veda anche ARGC e ARGV.
Poiché per i nomi delle variabili awk
si possono usare solo lettere
dell’alfabeto inglese, di proposito il controllo con espressioni regolari
non usa classi di
carattere come ‘[:alpha:]’ e ‘[:alnum:]’
(vedi Espressioni tra parentesi quadre).
Successivo: Ignorare assegnamenti di variabili, Precedente: Controllo di file, Su: Gestione File Dati [Contenuti][Indice]
Tutte le implementazioni note di awk
ignorano senza
mandare alcun messaggio i file di
lunghezza zero. Questo è un effetto collaterale del ciclo implicito di
awk
"leggi un record e confrontalo con le regole": quando
awk
cerca di leggere un record da un file vuoto, riceve immediatamente
un’indicazione di fine-file [end-of-file], chiude il file,
e prosegue con il
successivo file-dati presente nella riga di comando, senza
eseguire alcun codice
di programma awk
a livello di utente.
Usando la variabile ARGIND
di gawk
(vedi Variabili predefinite), è possibile accorgersi quando un file-dati
è stato saltato. Simile al file di libreria illustrato in
Funzione filetrans, il seguente file di libreria chiama una funzione
di nome zerofile()
che l’utente deve fornire. Gli argomenti passati
sono il nome-file e la posizione del file in ARGV
:
# zerofile.awk --- file di libreria per elaborare file in input vuoti BEGIN { Argind = 0 } ARGIND > Argind + 1 { for (Argind++; Argind < ARGIND; Argind++) zerofile(ARGV[Argind], Argind) } ARGIND != Argind { Argind = ARGIND } END { if (ARGIND > Argind) for (Argind++; Argind <= ARGIND; Argind++) zerofile(ARGV[Argind], Argind) }
La variabile definita dall’utente Argind
permette al programma
awk
di tracciare il suo percorso all’interno di ARGV
. Ogniqualvolta il
programma rileva che ARGIND
è maggiore di ‘Argind + 1’, vuol dire
che uno o più file vuoti sono stati tralasciati. L’azione chiama poi
zerofile()
per ogni file che è stato saltato, incrementando
ogni volta Argind
.
La regola ‘Argind != ARGIND’ tiene semplicemente aggiornato Argind
nel caso che non ci siano file vuoti.
Infine, la regola END
prende in considerazione il caso di un qualsiasi
file vuoto alla fine degli argomenti nella riga di comando. Si noti che nella
condizione del ciclo for
, la verifica usa l’operatore ‘<=’, non
‘<’.
Precedente: File vuoti, Su: Gestione File Dati [Contenuti][Indice]
Occasionalmente, potrebbe essere più opportuno che awk
non elabori gli
assegnamenti di variabile presenti sulla riga di comando
(vedi Opzioni di assegnamento).
In particolare, se si ha un nome-file che contiene un carattere ‘=’,
awk
tratta il nome-file come un assegnamento e non lo elabora.
Alcuni utenti hanno suggerito un’opzione aggiuntiva da riga di comando per
gawk
per disabilitare gli assegnamenti dati sulla riga di comando.
Comunque, poche righe di codice di programmazione in un file di libreria
hanno lo stesso effetto:
# noassign.awk --- file di libreria per evitare la necessità # di una speciale opzione per disabilitare gli assegnamenti da # riga di comando function disable_assigns(argc, argv, i) { for (i = 1; i < argc; i++) if (argv[i] ~ /^[a-zA-Z_][a-zA-Z0-9_]*=.*/) argv[i] = ("./" argv[i]) } BEGIN { if (Disabilita_variabili) disable_assigns(ARGC, ARGV) }
Il programma va poi eseguito in questo modo:
awk -v Disabilita_variabili=1 -f noassign.awk -f vostro_programma.awk *
La funzione esegue un ciclo che esamina ogni argomento. Antepone ‘./’ a qualsiasi argomento che abbia la forma di un assegnamento di variabile, trasformando così quell’argomento in un nome-file.
L’uso di Disabilita_variabili
consente di disabilitare assegnamenti di
variabile dati sulla riga di comando al momento dell’invocazione,
assegnando alla variabile un valore vero.
Se non viene impostata la variabile è inizializzata a zero (cioè
falso), e gli argomenti sulla riga di comando
non vengono modificati.
Successivo: Funzioni Passwd, Precedente: Gestione File Dati, Su: Funzioni di libreria [Contenuti][Indice]
La maggior parte dei programmi di utilità su sistemi compatibili con POSIX
prevedono opzioni presenti sulla riga di comando che possono essere usate per
cambiare il modo in cui un programma si comporta. awk
è un esempio di
tali programmi (vedi Opzioni).
Spesso le opzioni hanno degli argomenti (cioè, dati che servono al
programma per eseguire correttamente le opzioni specificate
sulla riga di comando).
Per esempio, l’opzione -F di awk
richiede di usare la stringa
specificata
come separatore di campo. La prima occorrenza, sulla riga di comando, di
-- o di una stringa che non inizia con ‘-’ segnala la fine
delle opzioni.
I moderni sistemi Unix hanno una funzione C chiamata getopt()
per
elaborare gli argomenti presenti
sulla riga di comando. Il programmatore fornisce una
stringa che descrive le opzioni, ognuna delle quali consiste di
una sola lettera. Se un’opzione richiede un
argomento, nella stringa l’opzione è seguita da due punti.
A getopt()
vengono anche
passati il numero e i valori degli argomenti presenti sulla riga di comando
e viene chiamata in un ciclo.
getopt()
scandisce gli argomenti della riga di comando cercando
le lettere delle opzioni.
A ogni passaggio del ciclo restituisce un carattere
singolo che rappresenta la successiva lettera di opzione trovata, o ‘?’
se viene trovata un’opzione non prevista.
Quando restituisce -1, non ci sono ulteriori
opzioni da trattare sulla riga di comando.
Quando si usa getopt()
, le opzioni che non prevedono argomenti
possono essere raggruppate.
Inoltre, le opzioni che hanno argomenti richiedono obbligatoriamente che
l’argomento sia specificato.
L’argomento può seguire immediatamente la lettera
dell’opzione, o può costituire un argomento separato sulla riga di comando.
Dato un ipotetico programma che ha tre opzioni sulla riga di comando, -a, -b e -c, dove -b richiede un argomento, tutti i seguenti sono modi validi per invocare il programma:
programma -a -b pippo -c dati1 dati2 dati3 programma -ac -bpippo -- dati1 dati2 dati3 programma -acbpippo dati1 dati2 dati3
Si noti che quando l’argomento è raggruppato con la sua opzione, la parte rimanente dell’argomento è considerato come argomento dell’opzione. In quest’esempio, -acbpippo indica che tutte le opzioni -a, -b e -c sono presenti, e che ‘pippo’ è l’argomento dell’opzione -b.
getopt()
fornisce quattro variabili esterne a disposizione del
programmatore:
optind
L’indice nel vettore dei valori degli argomenti (argv
) dove si può
trovare il primo argomento sulla riga di comando che non sia un’opzione.
optarg
Il valore (di tipo stringa) dell’argomento di un’opzione.
opterr
Solitamente getopt()
stampa un messaggio di errore quando trova un’opzione
non valida. Impostando opterr
a zero si disabilita questa funzionalità.
(un’applicazione potrebbe voler stampare un proprio messaggio di errore.)
optopt
La lettera che rappresenta l’opzione sulla riga di comando.
Il seguente frammento di codice C mostra come getopt()
potrebbe
elaborare gli argomenti della riga di comando per awk
:
int main(int argc, char *argv[]) { … /* stampa un appropriato messaggio */ opterr = 0; while ((c = getopt(argc, argv, "v:f:F:W:")) != -1) { switch (c) { case 'f': /* file */ … break; case 'F': /* separatore di campo */ … break; case 'v': /* assegnamento di variabile */ … break; case 'W': /* estensione */ … break; case '?': default: messaggio_di_aiuto(); break; } } … }
Incidentalmente, gawk
al suo interno usa la funzione GNU
getopt_long()
per elaborare sia le normali opzioni che quelle lunghe
in stile GNU
(vedi Opzioni).
L’astrazione fornita da getopt()
è molto utile ed è piuttosto comoda
anche nei programmi awk
. Di seguito si riporta una versione
awk
di getopt()
. Questa funzione mette in evidenza uno dei
maggiori punti deboli di awk
, che è quello di essere molto carente
nella manipolazione di caratteri singoli. Sono necessarie ripetute chiamate a
substr()
per accedere a caratteri singoli.
(vedi Funzioni per stringhe).74
La spiegazione della funzione viene data man mano che si elencano i pezzi di codice che la compongono:
# getopt.awk --- imita in awk la funzione di libreria C getopt(3) # Variabili esterne: # Optind -- indice in ARGV del primo argomento che non è un'opzione # Optarg -- valore di tipo stringa dell'argomento dell'opzione corrente # Opterr -- se diverso da zero, viene stampato un messaggio diagnostico # Optopt -- lettera dell'opzione corrente # Restituisce: # -1 alla fine delle opzioni # "?" per un'opzione non riconosciuta # <c> un carattere che rappresenta l'opzione corrente # Dati privati: # _opti -- indice in un'opzione multipla, p.es., -abc
La funzione inizia con commenti che elencano e descrivono le variabili globali utilizzate, spiegano quali sono i valori di ritorno, il loro significato, e ogni altra variabile che è “esclusiva” a questa funzione di libreria. Tale documentazione è essenziale per qualsiasi programma, e in modo particolare per le funzioni di libreria.
La funzione getopt()
dapprima controlla che sia stata effettivamente
chiamata con una stringa di opzioni (il parametro opzioni
). Se
opzioni
ha lunghezza zero, getopt()
restituisce immediatamente
-1:
function getopt(argc, argv, opzioni, unaopz, i) { if (length(opzioni) == 0) # nessuna opzione specificata return -1
if (argv[Optind] == "--") { # fatto tutto Optind++ _opti = 0 return -1
} else if (argv[Optind] !~ /^-[^:[:space:]]/) { _opti = 0 return -1 }
Il successivo controllo cerca la fine delle opzioni. Due trattini
(--) marcano la fine delle opzioni da riga di comando, e lo stesso
fa qualsiasi
argomento sulla riga di comando che non inizi con ‘-’. Optind
è
usato per scorrere il vettore degli argomenti presenti sulla riga di comando;
mantiene il suo valore attraverso chiamate successive a getopt()
, perché
è una variabile globale.
L’espressione regolare che viene usata, /^-[^:[:space:]/
,
chiede di cercare un
‘-’ seguito da qualsiasi cosa che non sia uno spazio vuoto o un carattere
di due punti. Se l’argomento corrente sulla riga di comando non corrisponde a
quest’espressione regolare, vuol dire che non si tratta di un’opzione, e
quindi viene terminata l’elaborazione delle opzioni. Continuando:
if (_opti == 0) _opti = 2 unaopz = substr(argv[Optind], _opti, 1) Optopt = unaopz i = index(opzioni, unaopz) if (i == 0) { if (Opterr) printf("%c -- opzione non ammessa\n", unaopz) > "/dev/stderr" if (_opti >= length(argv[Optind])) { Optind++ _opti = 0 } else _opti++ return "?" }
La variabile _opti
tiene traccia della posizione nell’argomento
della riga di comando correntemente in esame
(argv[Optind]
). Se opzioni multiple sono
raggruppate con un ‘-’ (p.es., -abx), è necessario
restituirle all’utente una per volta.
Se _opti
è uguale a zero, viene impostato a due, ossia all’indice
nella
stringa del successivo carattere da esaminare (‘-’, che è alla
posizione uno viene ignorato).
La variabile unaopz
contiene il carattere,
ottenuto con substr()
. Questo è salvato in Optopt
per essere
usato dal programma principale.
Se unaopz
non è nella stringa delle opzioni opzioni
,
si tratta di un’opzione
non valida. Se Opterr
è diverso da zero, getopt()
stampa un
messaggio di errore sullo standard error che è simile al messaggio
emesso dalla versione C di getopt()
.
Poiché l’opzione non è valida, è necessario tralasciarla e passare al successivo
carattere di opzione. Se _opti
è maggiore o uguale alla lunghezza
dell’argomento corrente della riga di comando, è necessario passare al
successivo argomento, in modo che Optind
venga incrementato e
_opti
sia reimpostato a zero. In caso contrario, Optind
viene
lasciato com’è e _opti
viene soltanto incrementato.
In ogni caso, poiché l’opzione non è valida, getopt()
restituisce
"?"
. Il programma principale può esaminare Optopt
se serve
conoscere quale lettera di opzione è quella non valida. Proseguendo:
if (substr(opzioni, i + 1, 1) == ":") { # ottiene un argomento di opzione if (length(substr(argv[Optind], _opti + 1)) > 0) Optarg = substr(argv[Optind], _opti + 1) else Optarg = argv[++Optind] _opti = 0 } else Optarg = ""
Se l’opzione richiede un argomento, la lettera di opzione è seguita da due punti
nella stringa opzioni
. Se rimangono altri caratteri nell’argomento
corrente sulla riga di comando (argv[Optind]
), il resto di quella stringa
viene assegnato a Optarg
. Altrimenti, viene usato il successivo
argomento sulla riga di comando (‘-xFOO’ piuttosto che
‘-x FOO’). In
entrambi i casi, _opti
viene reimpostato a zero, perché non ci sono altri
caratteri da esaminare nell’argomento corrente sulla riga di comando.
Continuando:
if (_opti == 0 || _opti >= length(argv[Optind])) { Optind++ _opti = 0 } else _opti++ return unaopz }
Infine, se _opti
è zero o maggiore della lunghezza dell’argomento
corrente sulla riga di comando, significa che l’elaborazione di
quest’elemento in argv
è
terminata, quindi Optind
è incrementato per
puntare al successivo elemento in argv
. Se nessuna delle condizioni è
vera, viene incrementato solo _opti
, cosicché la successiva lettera di
opzione può essere elaborata con la successiva chiamata a getopt()
.
La regola BEGIN
inizializza sia Opterr
che Optind
a uno.
Opterr
viene impostato a uno, perché il comportamento di default per
getopt()
è quello di stampare un messaggio diagnostico dopo aver visto
un’opzione non valida. Optind
è impostato a uno, perché non
c’è alcun motivo
per considerare il nome del programma, che è in ARGV[0]
:
BEGIN { Opterr = 1 # il default è eseguire una diagnosi Optind = 1 # salta ARGV[0] # programma di controllo if (_getopt_test) { while ((_go_c = getopt(ARGC, ARGV, "ab:cd")) != -1) printf("c = <%c>, Optarg = <%s>\n", _go_c, Optarg) printf("argomenti che non sono opzioni:\n") for (; Optind < ARGC; Optind++) printf("\tARGV[%d] = <%s>\n", Optind, ARGV[Optind]) } }
Il resto della regola BEGIN
è un semplice programma di controllo. Qui
sotto si riportano i risultati di
due esecuzioni di prova
del programma di controllo:
$ awk -f getopt.awk -v _getopt_test=1 -- -a -cbARG bax -x -| c = <a>, Optarg = <> -| c = <c>, Optarg = <> -| c = <b>, Optarg = <ARG> -| argomenti che non sono opzioni: -| ARGV[3] = <bax> -| ARGV[4] = <-x> $ awk -f getopt.awk -v _getopt_test=1 -- -a -x -- xyz abc -| c = <a>, Optarg = <> error→ x -- opzione non ammessa -| c = <?>, Optarg = <> -| argomenti che non sono opzioni: -| ARGV[4] = <xyz> -| ARGV[5] = <abc>
In entrambe le esecuzioni, il primo -- fa terminare gli argomenti dati
ad awk
, in modo che awk
non tenti di interpretare le opzioni
-a, etc. come sue opzioni.
NOTA: Dopo che
getopt()
è terminato, il codice a livello utente deve eliminare tutti gli elementi diARGV
da 1 aOptind
, in modo cheawk
non tenti di elaborare le opzioni sulla riga di comando come nomi-file.
Usare ‘#!’ con l’opzione -E può essere d’aiuto per evitare
conflitti tra le opzioni del proprio programma e quelle di gawk
,
poiché l’opzione -E fa sì che gawk
abbandoni
l’elaborazione di ulteriori opzioni.
(vedi Script eseguibili e
vedi Opzioni).
Molti degli esempi presentati in
Programmi di esempio,
usano getopt()
per elaborare i propri argomenti.
Successivo: Funzioni Group, Precedente: Funzione getopt, Su: Funzioni di libreria [Contenuti][Indice]
Il vettore PROCINFO
(vedi Variabili predefinite)
dà accesso ai numeri ID reale ed effettivo dell’utente e del gruppo e, se
disponibili, alla serie di gruppi ulteriori a cui l’utente appartiene.
Comunque, poiché questi sono numeri, non forniscono informazioni molto utili per
l’utente medio. Bisogna trovare un modo per reperire informazioni
sull’utente associate con i numeri ID dell’utente e del gruppo. Questa
sezione illustra una raccolta di funzioni per ottenere le informazioni
dalla lista gli utenti. Vedi Funzioni Group per una raccolta di
funzioni simili per ottenere informazioni dalla lista dei gruppi.
Lo standard POSIX non definisce il file dove sono mantenute le informazioni
degli utenti. Invece, fornisce il file d’intestazione <pwd.h>
e diverse subroutine del linguaggio C per ottenere informazioni sugli
utenti. La funzione primaria è getpwent()
, che sta per “get password
entry”. La “password” proviene dal file originale della lista
degli utenti, /etc/passwd, che contiene le informazioni sugli utenti
assieme alle password criptate (da cui il nome).75
Sebbene un programma awk
possa semplicemente leggere
/etc/passwd direttamente, questo file può non contenere tutte le
informazioni su tutti gli utenti del sistema.76 Per
essere sicuri di poter produrre una versione leggibile e completa della banca
dati degli utenti, è necessario scrivere un piccolo programma in C che chiama
getpwent()
. getpwent()
viene definita in modo da restituire un
puntatore a una struct passwd
. Ogni volta che viene chiamata,
restituisce l’elemento successivo della lista. Quando non ci sono più
elementi, restituisce NULL
, il puntatore nullo. Quando accade ciò, il
programma C dovrebbe chiamare endpwent()
per chiudere la lista..
Quel che segue è pwcat
, un programma in C che “concatena” la
lista delle password:
/* * pwcat.c * * Genera una versione stampabile della lista delle password. */ #include <stdio.h> #include <pwd.h> int main(int argc, char **argv) { struct passwd *p; while ((p = getpwent()) != NULL) printf("%s:%s:%ld:%ld:%s:%s:%s\n", p->pw_name, p->pw_passwd, (long) p->pw_uid, (long) p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell); endpwent(); return 0; }
Se non si conosce il linguaggio C, non è il caso di preoccuparsi.
L’output di pwcat
è la lista degli utenti, nel formato
tradizionale del file /etc/passwd con campi separati da due punti.
I campi sono:
Il nome di login dell’utente.
La password criptata dell’utente. Può non essere disponibile su alcuni sistemi.
L’ID numerico dell’utente.
(Su alcuni sistemi, è un numero di formato long
[32bit]
del linguaggio C, e non nel formato int
[16bit].
Quindi, lo cambieremo in long
per sicurezza.)
L’ID di gruppo numerico dell’utente.
(Valgono le stesse considerazioni su long
al posto di int
.)
Il nome completo dell’utente, e talora altre informazioni associate all’utente.
La directory di login (o “home”) (nota ai programmatori di shell come
$HOME
).
Il programma che viene eseguito quando l’utente effettua l’accesso. Questo è comunemente una shell, come Bash.
Di seguito si riportano alcune righe di un possibile output di pwcat
:
$ pwcat -| root:x:0:1:Operator:/:/bin/sh -| nobody:x:65534:65534::/: -| daemon:x:1:1::/: -| sys:x:2:2::/:/bin/csh -| bin:x:3:3::/bin: -| arnold:x:2076:10:Arnold Robbins:/home/arnold:/bin/sh -| miriam:x:112:10:Miriam Robbins:/home/miriam:/bin/sh -| andy:x:113:10:Andy Jacobs:/home/andy:/bin/sh …
Dopo quest’introduzione, di seguito si riporta un gruppo di funzioni per ottenere informazioni sugli utenti. Ci sono diverse funzioni, che corrispondono alle omonime funzioni C:
# passwd.awk --- accedere alle informazioni del file delle password BEGIN { # modificare per adattarlo al sistema in uso _pw_awklib = "/usr/local/libexec/awk/" } function _pw_init( oldfs, oldrs, olddol0, pwcat, using_fw, using_fpat) { if (_pw_inizializzato) return oldfs = FS oldrs = RS olddol0 = $0 using_fw = (PROCINFO["FS"] == "FIELDWIDTHS") using_fpat = (PROCINFO["FS"] == "FPAT") FS = ":" RS = "\n" pwcat = _pw_awklib "pwcat" while ((pwcat | getline) > 0) { _pw_byname[$1] = $0 _pw_byuid[$3] = $0 _pw_bycount[++_pw_totale] = $0 } close(pwcat) _pw_contatore = 0 _pw_inizializzato = 1 FS = oldfs if (using_fw) FIELDWIDTHS = FIELDWIDTHS else if (using_fpat) FPAT = FPAT RS = oldrs $0 = olddol0 }
La regola BEGIN
imposta una variabile privata col nome
della directory in cui si
trova pwcat
.
Poiché è destinata a essere usata da una routine di
libreria di awk
, si è scelto di metterla in
/usr/local/libexec/awk; comunque, in un altro sistema potrebbe
essere messa in una directory differente.
La funzione _pw_init()
mette tre copie delle informazioni sull’utente in
tre vettori associativi. I vettori sono indicizzati per nome-utente
(_pw_byname
), per numero di ID-utente (_pw_byuid
), e per ordine di
occorrenza (_pw_bycount
).
La variabile _pw_inizializzato
è usata per
efficienza, poiché in questo modo _pw_init()
viene chiamata solo una volta.
Poiché questa funzione usa getline
per leggere informazioni da
pwcat
, dapprima salva i valori di FS
, RS
e $0
.
Annota nella variabile using_fw
se la suddivisione in campi
usando FIELDWIDTHS
è attiva o no.
Far questo è necessario, poiché queste funzioni potrebbero essere chiamate da
qualsiai parte all’interno di un programma dell’utente, e l’utente può
suddividere i record in campi a suo piacimento.
Ciò rende possibile ripristinare il corretto meccanismo di suddivisione dei
campi in un secondo momento. La verifica può restituire solo vero per
gawk
.
Il risultato può essere falso se si usa
FS
o FPAT
,
o in qualche altra implementazione di awk
.
Il codice che controlla se si sta usando FPAT
, utilizzando
using_fpat
e PROCINFO["FS"]
, è simile.
La parte principale della funzione usa un ciclo per leggere le righe della
lista, suddividere le righe in campi, e poi memorizzare la riga
all’interno di ogni vettore a seconda delle necessità. Quando il ciclo è
completato, _pw_init()
fa pulizia chiudendo la pipe,
impostando _pw_inizializzato
a uno, e ripristinando FS
(e FIELDWIDTHS
o FPAT
se necessario), RS
e $0
.
L’uso di _pw_contatore
verrà spiegato a breve.
La funzione getpwnam()
ha un nome utente come argomento di tipo
stringa. Se
quell’utente è presente nella lista, restituisce la riga appropriata.
Altrimenti, il riferimento a un elemento inesistente del vettore
aggiunge al vettore stesso un elemento il cui valore è la stringa nulla:
function getpwnam(nome) { _pw_init() return _pw_byname[nome] }
In modo simile, la funzione getpwuid()
ha per argomento
il numero ID di un utente.
Se un utente con quel numero si trova nella lista, restituisce la riga
appropriata. Altrimenti restituisce la stringa nulla:
function getpwuid(uid) { _pw_init() return _pw_byuid[uid] }
La funzione getpwent()
scorre semplicemnte la lista, un elemento
alla volta. Usa _pw_contatore
per tener traccia della posizione corrente
nel vettore _pw_bycount
:
function getpwent() { _pw_init() if (_pw_contatore < _pw_totale) return _pw_bycount[++_pw_contatore] return "" }
La funzione endpwent()
reimposta _pw_contatore
a zero,
in modo che chiamate successive a getpwent()
ricomincino da capo:
function endpwent() { _pw_contatore = 0 }
In questa serie di funzioni, il fatto che ogni subroutine chiami
_pw_init()
per inizializzare il vettore della lista utenti risponde a una precisa
scelta progettuale.
Il lavoro necessario per eseguire un processo separato che generi la
lista degli utenti, e l’I/O per esaminarla, si ha solo se il programma
principale dell’utente chiama effettivamente una di queste funzioni.
Se questo
file di libreria viene caricato assieme a un programma dell’utente, ma non
viene mai chiamata nessuna delle routine, non c’è nessun lavoro aggiuntivo
richiesto in fase di esecuzione.
(L’alternativa è quella di spostare il corpo di _pw_init()
all’interno di una regola BEGIN
, che esegua sempre pwcat
.
Questo semplifica il codice ma richiede di eseguire un processo extra
il cui risultato potrebbe non essere mai utilizzato dal programma.)
A sua volta, chiamare ripetutamente _pw_init()
non è troppo
dispendioso, perché la
variabile _pw_inizializzato
permette di evitare di leggere
i dati relativi agli utenti più di una
volta. Se la preoccupazione è quella di minimizzare il tempo di
esecuzione del programma awk
,
il controllo di _pw_inizializzato
potrebbe essere spostato
al di fuori di _pw_init()
e duplicato in tutte le altre funzioni.
In pratica, questo non è necessario, poiché la maggior parte dei
programmi di awk
è I/O-bound77,
e una tale modifica complicherebbe inutilmente il codice.
Il programma id
in Programma id
usa queste funzioni.
Successivo: Visitare vettori, Precedente: Funzioni Passwd, Su: Funzioni di libreria [Contenuti][Indice]
Molto di quel che è stato detto
in
Funzioni Passwd
vale anche per la lista dei gruppi. Sebbene questa sia tradizionalmente
contenuta in
un file ben noto (/etc/group) in un altrettanto noto formato,
lo standard
POSIX prevede solo una serie di routine della libreria C
(<grp.h>
e getgrent()
)
per accedere a tali informazioni.
Anche se il file suddetto è disponibile, potrebbe non contenere delle
informazioni
complete. Perciò, come per la lista degli utenti, è necessario avere un
piccolo programma in C che genera la lista dei gruppi come suo output.
grcat
, un programma in C che fornisce la lista dei gruppi,
è il seguente:
/* * grcat.c * * Genera una versione stampabile della lista dei gruppi. */ #include <stdio.h> #include <grp.h> int main(int argc, char **argv) { struct group *g; int i; while ((g = getgrent()) != NULL) { printf("%s:%s:%ld:", g->gr_name, g->gr_passwd, (long) g->gr_gid); for (i = 0; g->gr_mem[i] != NULL; i++) { printf("%s", g->gr_mem[i]);
if (g->gr_mem[i+1] != NULL) putchar(','); }
putchar('\n'); } endgrent(); return 0; }
Ciascuna riga nella lista dei gruppi rappresenta un gruppo. I campi sono separati da due punti e rappresentano le seguenti informazioni:
Il nome del gruppo.
La password del gruppo criptata. In pratica, questo campo non viene mai usato; normalmente è vuoto o impostato a ‘x’.
Il numero ID del gruppo in formato numerico;
l’associazione del nome al numero dev’essere univoca all’interno di questo file.
(Su alcuni sistemi, è un numero nel formato long
[32bit]
del linguaggio C, e non nel formato int
[16bit].
Quindi, lo cambieremo in long
per sicurezza.)
Una lista di nomi utente separati da virgole.
Questi utenti sono i membri del gruppo.
I sistemi Unix moderni consentono agli utenti di appartenere a
diversi gruppi simultaneamente. Se il sistema in uso è uno di questi, ci sono
elementi in PROCINFO
che vanno da "group1"
fino a
"groupN"
per quei numeri di ID di gruppo.
(Si noti che PROCINFO
è un’estensione gawk
;
vedi Variabili predefinite.)
Di seguito si riporta quel che grcat
potrebbe produrre:
$ grcat -| wheel:x:0:arnold -| nogroup:x:65534: -| daemon:x:1: -| kmem:x:2: -| staff:x:10:arnold,miriam,andy -| other:x:20: …
Qui ci sono le funzioni per ottenere informazioni relative alla lista dei gruppi. Ce ne sono diverse, costruite sul modello delle omonime funzioni della libreria C:
# group.awk --- funzioni per il trattamento del file dei gruppi BEGIN { # Modificare in base alla struttura del proprio sistema _gr_awklib = "/usr/local/libexec/awk/" } function _gr_init( oldfs, oldrs, olddol0, grcat, using_fw, using_fpat, n, a, i) { if (_gr_inizializzato) return oldfs = FS oldrs = RS olddol0 = $0 using_fw = (PROCINFO["FS"] == "FIELDWIDTHS") using_fpat = (PROCINFO["FS"] == "FPAT") FS = ":" RS = "\n" grcat = _gr_awklib "grcat" while ((grcat | getline) > 0) { if ($1 in _gr_byname) _gr_byname[$1] = _gr_byname[$1] "," $4 else _gr_byname[$1] = $0 if ($3 in _gr_bygid) _gr_bygid[$3] = _gr_bygid[$3] "," $4 else _gr_bygid[$3] = $0 n = split($4, a, "[ \t]*,[ \t]*") for (i = 1; i <= n; i++) if (a[i] in _gr_groupsbyuser) _gr_groupsbyuser[a[i]] = _gr_groupsbyuser[a[i]] " " $1 else _gr_groupsbyuser[a[i]] = $1 _gr_bycount[++_gr_contatore] = $0 } close(grcat) _gr_contatore = 0 _gr_inizializzato++ FS = oldfs if (using_fw) FIELDWIDTHS = FIELDWIDTHS else if (using_fpat) FPAT = FPAT RS = oldrs $0 = olddol0 }
La regola BEGIN
imposta una variabile privata con il nome della
directory in cui si trova grcat
.
Poiché è destinata a essere usata da una routine di
libreria di awk
, si è scelto di metterla in
/usr/local/libexec/awk; comunque, in un altro sistema potrebbe
essere messa in una directory differente.
Queste routine seguono le stesse linee generali delle routine per formare la
lista degli utenti (vedi Funzioni Passwd).
La variabile _gr_inizializzato
è usata per
essere sicuri che la lista venga letta una volta sola.
La funzione _gr_init()
dapprima salva FS
,
RS
e
$0
, e poi imposta FS
e RS
ai valori da usare nel
passare in rassegna le informazioni di gruppo. Inoltre
viene annotato se si stanno usando FIELDWIDTHS
o FPAT
, per
poter poi
ripristinare il meccanismo di suddivisione in campi appropriato.
Le informazioni sui gruppi sono memorizzate in diversi vettori associativi.
I vettori sono indicizzati per nome di gruppo (_gr_byname
), per
numero ID del gruppo (_gr_bygid
), e per posizione nella lista
(_gr_bycount
). C’è un vettore aggiuntivo indicizzato per nome utente
(_gr_groupsbyuser
), che è una lista, separata da spazi, dei
gruppi ai quali ciascun utente appartiene.
Diversamente dalla lista degli utenti, è possibile avere più record nella lista per lo stesso gruppo. Questo è frequente quando un gruppo ha un gran numero di membri. Un paio di tali voci potrebbero essere come queste:
tvpeople:x:101:johnny,jay,arsenio tvpeople:x:101:david,conan,tom,joan
Per questo motivo, _gr_init()
controlla se un nome di gruppo o un numero
di ID di gruppo è stato già visto. Se così fosse, i nomi utente vanno
semplicemente concatenati con la precedente lista di utenti.78
Infine, _gr_init()
chiude la pipe a grcat
, ripristina
FS
(e FIELDWIDTHS
o FPAT
, se necessario), RS
e
$0
, inizializza _gr_contatore
a zero
(per essere usato più tardi), e rende _gr_inizializzato
diverso da zero.
La funzione getgrnam()
ha come argomento un nome di gruppo, e se quel
gruppo esiste, viene restituito.
Altrimenti, il riferimento a un elemento inesistente del vettore aggiunge al vettore stesso un elemento il cui valore è la stringa nulla:
function getgrnam(group) { _gr_init() return _gr_byname[group] }
La funzione getgrgid()
è simile; ha come argomento un numero ID di
gruppo e controlla le informazioni assiciate con quell’ID di gruppo:
function getgrgid(gid) { _gr_init() return _gr_bygid[gid] }
La funzione getgruser()
non ha un equivalente in C. Ha come argomento un
nome-utente e restituisce l’elenco dei gruppi di cui l’utente è membro:
function getgruser(user) { _gr_init() return _gr_groupsbyuser[user] }
La funzione getgrent()
scorre la lista un elemento alla volta.
Usa _gr_contatore
per ricordare la posizione corrente nella lista:
function getgrent() { _gr_init() if (++_gr_contatore in _gr_bycount) return _gr_bycount[_gr_contatore] return "" }
La funzione endgrent()
reimposta _gr_contatore
a zero in modo che
getgrent()
possa ricominciare da capo:
function endgrent() { _gr_contatore = 0 }
Come con le routine per la lista degli utenti, ogni funzione chiama
_gr_init()
per inizializzare i vettori.
Così facendo si avrà il solo
lavoro aggiuntivo di eseguire grcat
se queste funzioni vengono
usate (rispetto a spostare il corpo di _gr_init()
all’interno della
regola BEGIN
).
La maggior parte del lavoro consiste nell’ispezionare la lista e nel
costruire i vari vettori associativi. Le funzioni che l’utente chiama sono
di per sé molto semplici, poiché si appoggiano sui vettori associativi di
awk
per fare il lavoro.
Il programma id
in Programma id
usa queste funzioni.
Successivo: Sommario funzioni di libreria, Precedente: Funzioni Group, Su: Funzioni di libreria [Contenuti][Indice]
Vettori di vettori trattava come gawk
avere a disposizione vettori di vettori. In particolare, qualsiasi elemento di
un vettore può essere uno scalare o un altro vettore. La funzione
isarray()
(vedi Funzioni per i tipi)
permette di distinguere un vettore
da uno scalare.
La seguente funzione, walk_array()
, attraversa ricorsivamente
un vettore, stampando gli indici e i valori di ogni elemento.
Viene chiamata col vettore e con una stringa che contiene il nome
del vettore:
function walk_array(vett, nome, i) { for (i in vett) { if (isarray(vett[i])) walk_array(vett[i], (nome "[" i "]")) else printf("%s[%s] = %s\n", nome, i, vett[i]) } }
Funziona eseguendo un ciclo su ogni elemento del vettore. Se un dato elemento è esso stesso un vettore, la funzione chiama sé stessa ricorsivamente, passando il sottovettore e una nuova stringa che rappresenta l’indice corrente. In caso contrario, la funzione stampa semplicemente il nome, l’indice e il valore dell’elemento. Qui di seguito si riporta un programma principale che ne mostra l’uso:
BEGIN { a[1] = 1 a[2][1] = 21 a[2][2] = 22 a[3] = 3 a[4][1][1] = 411 a[4][2] = 42 walk_array(a, "a") }
Quando viene eseguito, il programma produce il seguente output:
$ gawk -f walk_array.awk -| a[1] = 1 -| a[2][1] = 21 -| a[2][2] = 22 -| a[3] = 3 -| a[4][1][1] = 411 -| a[4][2] = 42
La funzione appena illustrata stampa semplicemente il nome e il valore di ogni elemento costituito da un vettore scalare. Comunque è facile generalizzarla, passandole il nome di una funzione da chiamare quando si attraversa un vettore. La funzione modificata è simile a questa:
function process_array(vett, nome, elab, do_arrays, i, nuovo_nome) { for (i in vett) { nuovo_nome = (nome "[" i "]") if (isarray(vett[i])) { if (do_arrays) @elab(nuovo_nome, vett[i]) process_array(vett[i], nuovo_nome, elab, do_arrays) } else @elab(nuovo_nome, vett[i]) } }
Gli argomenti sono i seguenti:
vett
Il vettore.
nome
Il nome del vettore (una stringa).
elab
Il nome della funzione da chiamare.
do_arrays
Se vale vero, la funzione può gestire elementi che sono sottovettori.
Se devono essere elaborati sottovettori, questo vien fatto prima di attraversarne altri.
Quando viene eseguita con la seguente struttura, la funzione produce lo stesso
risultato della precedente versione di walk_array()
:
BEGIN { a[1] = 1 a[2][1] = 21 a[2][2] = 22 a[3] = 3 a[4][1][1] = 411 a[4][2] = 42 process_array(a, "a", "do_print", 0) } function do_print(nome, elemento) { printf "%s = %s\n", nome, elemento }
Successivo: Esercizi con le librerie, Precedente: Visitare vettori, Su: Funzioni di libreria [Contenuti][Indice]
Conversione di numeri in stringhe, verifica delle asserzioni, arrotondamenti, generazione di numeri casuali, conversione di caratteri in numeri, unione di stringhe, ottenimento di informazioni su data e ora facilmente usabili, e lettura di un intero file in una volta sola
Annotazione dei limiti di un file-dati, rilettura del file corrente, ricerca di file leggibili, ricerca di file di lunghezza zero, e trattamento degli assegnamenti di variabili fatti sulla riga comando come nomi-file
Una versione awk
della funzione del C standard getopt()
Due serie di routine equivalenti alle versioni disponibili nella libreria del linguaggio C
Due funzioni che attraversano un vettore di vettori fino in fondo
Precedente: Sommario funzioni di libreria, Su: Funzioni di libreria [Contenuti][Indice]
gawk
ARGIND
. Questo problema può
essere risolto senza dipendere da ARGIND
? Se sì, come?
ARGV
sia un assegnamento di variabile.
Successivo: Funzionalità avanzate, Precedente: Funzioni di libreria, Su: Top [Contenuti][Indice]
awk
Funzioni di libreria,
ha prospettato l’idea che la lettura di programmi scritti in un certo
linguaggio possa aiutare a imparare quel linguaggio. Questo
capitolo ripropone lo stesso tema, presentando una miscellanea di
programmi awk
per il piacere di leggerli.
Ci sono tre sezioni.
La prima spiega come eseguire i programmi descritti in questo
capitolo.
La seconda illustra la versione awk
di parecchi comuni programmi di utilità disponibili in POSIX.
Si presuppone che si abbia già una certa familiarità con questi programmi,
e che quindi i problemi a loro legati siano facilmente comprensibili.
Riscrivendo questi programmi in awk
,
ci si può focalizzare sulle particolarità di awk
nella
risoluzione dei problemi di programmazione.
La terza sezione è una collezione di programmi interessanti.
Essi mirano a risolvere un certo numero di differenti problemi di
manipolazione e di gestione dati. Molti dei programmi sono brevi, per
evidenziare la capacità di awk
di fare molte cose usando solo
poche righe di codice.
Molti di questi programmi usano le funzioni di libreria che sono state presentate in Funzioni di libreria.
• Eseguire esempi: | Come eseguire questi esempi. | |
• Cloni: | Cloni di programmi di utilità comuni. | |
• Programmi vari: | Alcuni interessanti programmi in
awk .
| |
• Sommario dei programmi: | Sommario dei programmi. | |
• Esercizi sui programmi: | Esercizi. |
Successivo: Cloni, Su: Programmi di esempio [Contenuti][Indice]
Per eseguire un dato programma, si procederebbe tipicamente così:
awk -f programma -- opzioni file
Qui, programma è il nome del programma awk
(p.es.
cut.awk), opzioni sono le opzioni sulla riga di comando
per il programma che iniziano con un ‘-’, e file sono i
file-dati in input.
Se il sistema prevede il meccanismo ‘#!’ di specifica di un interprete (vedi Script eseguibili), si può invece eseguire direttamente un programma:
cut.awk -c1-8 i_miei_file > risultati
Se awk
non è gawk
, può invece essere necessario usare:
cut.awk -- -c1-8 i_miei_file > risultati
Successivo: Programmi vari, Precedente: Eseguire esempi, Su: Programmi di esempio [Contenuti][Indice]
Questa sezione presenta un certo numero di programmi di utilità
POSIX implementati in awk
. Riscrivere questi programmi in
awk
è spesso divertente,
perché gli algoritmi possono essere espressi molto chiaramente, e il codice
è normalmente molto semplice e conciso. Ciò è possibile perché awk
facilita molto le cose al programmatore.
Va precisato che questi programmi non sono necessariamente scritti per
sostituire le versioni installate sul sistema in uso.
Inoltre, nessuno di questi programmi è del tutto aderente ai più recenti
standard POSIX. Questo non è un problema; il loro scopo
è di illustrare la programmazione in linguaggio awk
che serve nel
“mondo reale”.
I programmi sono presentati in ordine alfabetico.
• Programma cut: | Il programma di utilità cut .
| |
• Programma egrep: | Il programma di utilità egrep .
| |
• Programma id: | Il programma di utilità id .
| |
• Programma split: | Il programma di utilità split .
| |
• Programma tee: | Il programma di utilità tee .
| |
• Programma uniq: | Il programma di utilità uniq .
| |
• Programma wc: | Il programma di utilità wc .
|
Successivo: Programma egrep, Su: Cloni [Contenuti][Indice]
Il programma di utilità cut
seleziona, o “taglia” (cut),
caratteri o campi dal suo standard input e li
spedisce al suo standard output.
I campi sono separati da caratteri TAB per default,
ma è possibile fornire un’opzione dalla riga di comando per cambiare il campo
delimitatore (cioè, il carattere che separa i campi). La definizione di
campo di cut
è meno generale di quella di awk
.
Un uso comune del comando cut
potrebbe essere quello di estrarre
i nomi degli utenti correntemente collegati al sistema, a partire
dall’output del comando who
. Per esempio, la seguente
pipeline genera una lista in ordine alfabetico, senza doppioni, degli utenti
correntemente collegati al sistema:
who | cut -c1-8 | sort | uniq
Le opzioni per cut
sono:
-c lista
Usare lista come lista di caratteri da ritagliare. Elementi all’interno della lista possono essere separati da virgole, e intervalli di caratteri possono essere separated da trattini. La lista ‘1-8,15,22-35’ specifica i caratteri da 1 a 8, 15, e da 22 a 35.
-f lista
Usare lista come lista di campi da ritagliare.
-d delimitatore
Usare delimitatore come carattere che separa i campi invece del carattere TAB.
-s
Evita la stampa di righe che non contengono il delimitatore di campo.
L’implementazione awk
del comando cut
usa la funzione
di libreria getopt()
(vedi Funzione getopt)
e la funzione di libreria join()
(vedi Funzione join).
Il programma inizia con un commento che descrive le opzioni, le funzioni
di libreria necessarie, e una funzione sintassi()
che stampa un
messaggio ed esce. sintassi()
è chiamato se si specificano degli
argomenti non validi:
# cut.awk --- implementa cut in awk # Opzioni: # -f lista Ritagliare campi # -d c Carattere di delimitazione di campo # -c lista Ritagliare caratteri # # -s Sopprimere righe che non contengono il delimitatore # # Richiede le funzioni di libreria getopt() e join()
function sintassi() { print("sintassi: cut [-f lista] [-d c] [-s] [file...]") > "/dev/stderr" print("sintassi: cut [-c lista] [file...]") > "/dev/stderr" exit 1 }
Subito dopo c’è una regola BEGIN
che analizza le opzioni della riga
di comando.
Questa regola imposta FS
a un solo carattere TAB, perché quello è
il separatore di campo di cut
per default.
La regola poi imposta il separatore di campo in output allo stesso valore
del separatore di campo in input. Un ciclo che usa getopt()
esamina
le opzioni della riga di comando. Una e una sola delle variabili
per_campi
o per_caratteri
è impostata a "vero", per indicare
che l’elaborazione sarà fatta per campi o per caratteri, rispettivamente.
Quando si ritaglia per caratteri, il separatore di campo in output è
impostato alla stringa nulla:
BEGIN { FS = "\t" # default OFS = FS while ((c = getopt(ARGC, ARGV, "sf:c:d:")) != -1) { if (c == "f") { per_campi = 1 lista_campi = Optarg } else if (c == "c") { per_caratteri = 1 lista_campi = Optarg OFS = "" } else if (c == "d") { if (length(Optarg) > 1) { printf("cut: usa il primo carattere di %s" \ " come delimitatore\n", Optarg) > "/dev/stderr" Optarg = substr(Optarg, 1, 1) } fs = FS = Optarg OFS = FS if (FS == " ") # mette specifica in formato awk FS = "[ ]" } else if (c == "s") sopprimi = 1 else sintassi() } # Toglie opzioni da riga di comando for (i = 1; i < Optind; i++) ARGV[i] = ""
Nella scrittura del codice si deve porre particolare attenzione quando il
delimitatore di campo è uno spazio. Usare
un semplice spazio (" "
) come valore per FS
è
sbagliato: awk
separerebbe i campi con serie di spazi,
TAB, e/o ritorni a capo, mentre devono essere separati solo da uno spazio.
Per far questo, salviamo il carattere di spazio originale nella variabile
fs
per un uso futuro; dopo aver impostato FS
a "[ ]"
non
è possibile usarlo direttamente per vedere se il carattere delimitatore di
campo è nella stringa.
Si ricordi anche che dopo che si è finito di usare getopt()
(come descritto nella Funzione getopt),
è necessario
eliminare tutti gli elementi del vettore ARGV
da 1 a Optind
,
in modo che awk
non tenti di elaborare le opzioni della riga di comando
come nomi-file.
Dopo aver elaborato le opzioni della riga di comando, il programma verifica
che le opzioni siano coerenti. Solo una tra le opzioni -c
e -f dovrebbe essere presente, ed entrambe richiedono una lista di
campi. Poi il programma chiama
prepara_lista_campi()
oppure prepara_lista_caratteri()
per
preparare la lista dei campi o dei caratteri:
if (per_campi && per_caratteri) sintassi() if (per_campi == 0 && per_caratteri == 0) per_campi = 1 # default if (lista_campi == "") { print "cut: specificare lista per -c o -f" > "/dev/stderr" exit 1 } if (per_campi) prepara_lista_campi() else prepara_lista_caratteri() }
prepara_lista_campi()
pone la lista campi, usando la virgola come
separatore, in un vettore. Poi, per
ogni elemento del vettore, controlla che esso non sia un intervallo. Se è
un intervallo, lo fa diventare un elenco. La funzione controlla l’intervallo
specificato, per assicurarsi che il primo numero sia minore del secondo.
Ogni numero nella lista è aggiunto al vettore lista_c
, che
semplicemente elenca i campi che saranno stampati. Viene usata la normale
separazione in campi di awk
. Il programma lascia ad awk
il compito di separare i campi:
function prepara_lista_campi( n, m, i, j, k, f, g) { n = split(lista_campi, f, ",") j = 1 # indice in lista_c for (i = 1; i <= n; i++) { if (index(f[i], "-") != 0) { # un intervallo m = split(f[i], g, "-")
if (m != 2 || g[1] >= g[2]) { printf("cut: lista campi errata: %s\n", f[i]) > "/dev/stderr" exit 1 }
for (k = g[1]; k <= g[2]; k++) lista_c[j++] = k } else lista_c[j++] = f[i] } ncampi = j - 1 }
La funzione prepara_lista_caratteri()
è più complicata di
prepara_lista_campi()
.
L’idea qui è di usare la variabile di gawk
FIELDWIDTHS
(vedi Dimensione costante),
che descrive input a larghezza costante. Quando si usa una lista di
caratteri questo è proprio il nostro caso.
Impostare FIELDWIDTHS
è più complicato che semplicemente elencare
i campi da stampare. Si deve tener traccia dei campi da
stampare e anche dei caratteri che li separano, che vanno saltati.
Per esempio, supponiamo che si vogliano i caratteri da 1 a 8, 15,
e da 22 a 35. Per questo si specifica ‘-c 1-8,15,22-35’. Il valore che
corrisponde a questo nella variabile FIELDWIDTHS
è
"8 6 1 6 14"
. Questi sono cinque campi, e quelli da stampare
sono $1
, $3
, e $5
.
I campi intermedi sono riempitivo (filler),
ossia è ciò che separa i dati che si desidera estrarre.
lista_c
lista i campi da stampare, e t
traccia l’elenco
completo dei campi, inclusi i riempitivi:
function prepara_lista_caratteri( campo, i, j, f, g, n, m, t, filler, ultimo, lungo) { campo = 1 # contatore totale campi n = split(lista_campi, f, ",") j = 1 # indice in lista_c for (i = 1; i <= n; i++) { if (index(f[i], "-") != 0) { # intervallo m = split(f[i], g, "-") if (m != 2 || g[1] >= g[2]) { printf("cut: lista caratteri errata: %s\n", f[i]) > "/dev/stderr" exit 1 } lungo = g[2] - g[1] + 1 if (g[1] > 1) # calcola lunghezza del riempitivo filler = g[1] - ultimo - 1 else filler = 0
if (filler) t[campo++] = filler
t[campo++] = lungo # lunghezza del campo ultimo = g[2] lista_c[j++] = campo - 1 } else { if (f[i] > 1) filler = f[i] - ultimo - 1 else filler = 0 if (filler) t[campo++] = filler t[campo++] = 1 ultimo = f[i] lista_c[j++] = campo - 1 } } FIELDWIDTHS = join(t, 1, campo - 1) ncampi = j - 1 }
Poi viene la regola che elabora i dati. Se l’opzione -s è stata
specificata, il flag sopprimi
è vero. La prima istruzione
if
accerta che il record in input abbia il separatore di
campo. Se cut
sta elaborando dei campi, e sopprimi
è vero,
e il carattere di separazione dei campi non è presente nel record, il
record è ignorato.
Se il record è valido, gawk
ha già separato i dati in campi,
usando il carattere in FS
o usando campi a lunghezza fissa
e FIELDWIDTHS
. Il ciclo scorre attraverso la lista di campi che
si dovrebbero stampare. Il campo corrispondente è stampato se contiene dati.
Se il campo successivo contiene pure dei dati, il carattere di separazione è
scritto tra i due campi:
{ if (per_campi && sopprimi && index($0, fs) == 0) next for (i = 1; i <= ncampi; i++) { if ($lista_c[i] != "") { printf "%s", $lista_c[i] if (i < ncampi && $lista_c[i+1] != "") printf "%s", OFS } } print "" }
Questa versione di cut
utilizza la variabile FIELDWIDTHS
di
gawk
per ritagliare in base alla posizione dei caratteri. È
possibile, in altre implementazioni di awk
usare substr()
(vedi Funzioni per stringhe), ma
la cosa è molto più complessa.
La variabile FIELDWIDTHS
fornisce una soluzione elegante al problema
di suddividere la riga in input in singoli caratteri.
Successivo: Programma id, Precedente: Programma cut, Su: Cloni [Contenuti][Indice]
Il programma di utilità egrep
ricerca occorrenze di espressioni
regolari all’interno di file. Usa
espressioni regolari che sono quasi identiche a quelle disponibili in
awk
(vedi Espressioni regolari).
Si richiama così:
egrep
[opzioni]'espressione'
file …
espressione è un’espressione regolare. Normalmente, l’espressione
regolare è protetta da apici per impedire alla shell di espandere ogni
carattere speciale come nome-file.
Normalmente, egrep
stampa le righe per cui è stata trovata una
corrispondenza. Se nella riga di comando si richiede di operare su più di un
nome-file, ogni riga in output è preceduta dal nome del file, e dal segno
due punti.
Le opzioni di egrep
sono le seguenti:
-c
Stampa un contatore delle righe che corrispondono al criterio di ricerca, e non le righe stesse.
-s
Funziona in silenzio. Non si produce alcun output ma il codice di ritorno indica se il criterio di ricerca ha trovato almeno una corrispondenza.
-v
Inverte il senso del test. egrep
stampa le righe che
non soddisfano il criterio di ricerca ed esce con successo se il
criterio di ricerca non è soddisfatto.
-i
Ignora maiuscolo/minuscolo sia nel criterio di ricerca che nei dati in input.
-l
Stampa (elenca) solo i nomi dei file che corrispondono, e non le righe trovate.
-e espressione
Usa espressione come regexp da ricercare. Il motivo per cui è prevista l’opzione -e è di permettere dei criteri di ricerca che inizino con un ‘-’.
Questa versione usa la funzione di libreria getopt()
(vedi Funzione getopt)
e il programma di libreria che gestisce il passaggio da un file dati
al successivo
(vedi Funzione filetrans).
Il programma inizia con un commento descrittivo e poi c’è una regola
BEGIN
che elabora gli argomenti della riga di comando usando getopt()
.
L’opzione -i (ignora maiuscolo/minuscolo) è particolarmente facile
da implementare con gawk
; basta usare la variabile predefinita
IGNORECASE
(vedi Variabili predefinite):
# egrep.awk --- simula egrep in awk # # Opzioni: # -c conta le righe trovate # -s sileziosa: genera solo il codice di ritorno # -v inverte test, successo se regexp non presente # -i ignora maiuscolo/minuscolo # -l stampa solo nomi file # -e espressione da ricercare # # Richiede la funzione getopt() e il programma di libreria # che gestisce il passaggio da un file dati al successivo BEGIN { while ((c = getopt(ARGC, ARGV, "ce:svil")) != -1) { if (c == "c") conta_e_basta++ else if (c == "s") non_stampare++ else if (c == "v") inverti_test++ else if (c == "i") IGNORECASE = 1 else if (c == "l") solo_nomi_file++ else if (c == "e") criterio_di_ricerca = Optarg else sintassi() }
Nel seguito c’è il codice che gestisce il comportamento specifico di
egrep
. Se non è fornito esplicitamente alcun criterio di ricerca
tramite l’opzione -e, si usa il primo argomento sulla riga di
comando che non sia un’opzione.
Gli argomenti della riga di comando di awk
fino ad
ARGV[Optind]
vengono cancellati,
in modo che awk
non tenti di elaborarli come file. Se
non è stato specificato alcun nome di file, si usa lo standard input, e se
è presente più di un nome di file, lo si annota, in modo che i nomi-file
vengano scritti prima di ogni riga di output corrispondente:
if (criterio_di_ricerca == "") criterio_di_ricerca = ARGV[Optind++] for (i = 1; i < Optind; i++) ARGV[i] = "" if (Optind >= ARGC) { ARGV[1] = "-" ARGC = 2 } else if (ARGC - Optind > 1) servono_nomi_file++ # if (IGNORECASE) # criterio_di_ricerca = tolower(criterio_di_ricerca) }
Le ultime due righe sono solo dei commenti, in quanto non necessarie in
gawk
. Per altre versioni di
awk
, potrebbe essere necessario utilizzarle come istruzioni
effettive (togliendo il "#").
Il prossimo insieme di righe dovrebbe essere decommentato
se non si sta usando gawk
.
Questa regola converte in minuscolo tutti i caratteri della riga in input,
se è stata specificata l’opzione -i.79
La regola è
commentata perché non è necessaria se si usa gawk
:
#{ # if (IGNORECASE) # $0 = tolower($0) #}
La funzione a_inizio_file()
è chiamata dalla regola in ftrans.awk
quando ogni nuovo file viene elaborato. In questo caso, non c’è molto da fare;
ci si limita a inizializzare una variabile contatore_file
a zero.
contatore_file
serve a ricordare quante righe nel file corrente
corrispondono al criterio di ricerca.
Scegliere come nome di parametro da_buttare
indica che sappiamo che
a_inizio_file()
è chiamata con un parametro, ma che noi non siamo
interessati al suo valore:
function a_inizio_file(da_buttare) { contatore_file = 0 }
La funzione endfile()
viene chiamata dopo l’elaborazione di ogni file.
Ha influenza sull’output solo quando l’utente desidera un contatore del
numero di righe che sono state individuate. non_stampare
è vero nel
caso si desideri solo il codice di
ritorno. conta_e_basta
è vero se si desiderano solo i contatori
delle righe trovate. egrep
quindi stampa i contatori solo se
sia la stampa che il conteggio delle righe sono stati abilitati.
Il formato di output deve tenere conto del numero di file sui quali si
opera. Per finire, contatore_file
è aggiunto a totale
, in
modo da stabilire qual è il numero totale di righe che ha soddisfatto il
criterio di ricerca:
function endfile(file) { if (! non_stampare && conta_e_basta) { if (servono_nomi_file) print file ":" contatore_file else print contatore_file } totale += contatore_file }
Si potrebbero usare i criteri di ricerca speciali BEGINFILE
ed
ENDFILE
(vedi BEGINFILE/ENDFILE),
ma in quel caso il programma funzionerebbe solo usando gawk
.
Inoltre, questo esempio è stato scritto prima che a gawk
venissero
aggiunti i criteri speciali BEGINFILE
ed ENDFILE
.
La regola seguente fa il grosso del lavoro per trovare righe corrispondenti
al criterio di ricerca fornito. La variabile
corrisponde
è vera se la riga è individuata dal criterio di ricerca.
Se l’utente chiede invece le righe che non corrispondono, il senso di
corrisponde
è invertito, usando l’operatore ‘!’.
contatore_file
è incrementato con il valore di
corrisponde
, che vale uno o zero, a seconda che la corrispondenza sia
stata trovata oppure no. Se la riga non corrisponde, l’istruzione
next
passa ad esaminare il record successivo.
Vengono effettuati anche altri controlli, ma soltanto se non
si sceglie di contare le righe. Prima di tutto, se l’utente desidera solo
il codice di ritorno (non_stampare
è vero), è sufficiente sapere
che una riga nel file corrisponde, e si può passare al file successivo
usando nextfile
. Analogamente, se stiamo solo stampando nomi-file,
possiamo stampare il nome-file, e quindi saltare al file successivo con
nextfile
.
Infine, ogni riga viene stampata, preceduta, se necessario, dal nome-file e
dai due punti:
{ corrisponde = ($0 ~ criterio_di_ricerca) if (inverti_test) corrisponde = ! corrisponde contatore_file += corrisponde # 1 o 0 if (! corrisponde) next if (! conta_e_basta) { if (non_stampare) nextfile if (solo_nomi_file) { print nome_file nextfile } if (servono_nomi_file) print nome_file ":" $0 else print } }
La regola END
serve a produrre il codice di ritorno corretto. Se
non ci sono corrispondenze, il codice di ritorno è uno; altrimenti, è zero:
END { exit (totale == 0) }
La funzione sintassi()
stampa un messaggio per l’utente, nel caso
siano state specificate opzioni non valide, e quindi esce:
function sintassi() { print("sintassi: egrep [-csvil] [-e criterio_di_ricerca] [file ...]")\ > "/dev/stderr" print("\n\tegrep [-csvil] criterio_di_ricerca [file ...]") > "/dev/stderr" exit 1 }
Successivo: Programma split, Precedente: Programma egrep, Su: Cloni [Contenuti][Indice]
Il programma di utilità id
elenca i numeri identificativi (ID)
reali ed effettivi di un utente, e l’insieme dei gruppi a cui l’utente
appartiene, se ve ne sono.
id
stampa i numeri identificativi di utente e di gruppo solo se
questi sono differenti da quelli reali. Se possibile, id
elenca
anche i corrispondenti nomi di utente e di gruppo.
L’output potrebbe essere simile a questo:
$ id -| uid=1000(arnold) gid=1000(arnold) groups=1000(arnold),4(adm),7(lp),27(sudo)
Questa informazione è parte di ciò che è reso disponibile dal vettore
PROCINFO
di gawk
(vedi Variabili predefinite).
Comunque, il programma di utilità id
fornisce un output più
comprensibile che non una semplice lista di numeri.
Ecco una versione semplice di id
scritta in awk
.
Usa le funzioni di libreria che riguardano il database degli utenti
(vedi Funzioni Passwd)
e le funzioni di libreria che riguardano il database dei gruppi
(vedi Funzioni Group)
contenute
in
Funzioni di libreria.
Il programma è abbastanza semplice. Tutto il lavoro è svolto nella regola
BEGIN
. I numeri ID di utente e di gruppo sono ottenuti da
PROCINFO
.
Il codice è ripetitivo. La riga nel database degli utenti che descrive
l’ID reale dell’utente è divisa in parti, separate tra loro da ‘:’.
Il nome è il primo campo. Un codice analogo è usato per l’ID effettivo, e
per i numeri che descrivono i gruppi:
# id.awk --- implement id in awk # # Richiede funzioni di libreria per utente e gruppo # l'output è: # uid=12(pippo) euid=34(pluto) gid=3(paperino) \ # egid=5(paperina) groups=9(nove),2(due),1(uno)
BEGIN { uid = PROCINFO["uid"] euid = PROCINFO["euid"] gid = PROCINFO["gid"] egid = PROCINFO["egid"]
printf("uid=%d", uid) pw = getpwuid(uid) stampa_primo_campo(pw) if (euid != uid) { printf(" euid=%d", euid) pw = getpwuid(euid) stampa_primo_campo(pw) } printf(" gid=%d", gid) pw = getgrgid(gid) stampa_primo_campo(pw) if (egid != gid) { printf(" egid=%d", egid) pw = getgrgid(egid) stampa_primo_campo(pw) } for (i = 1; ("group" i) in PROCINFO; i++) { if (i == 1) printf(" gruppi=") group = PROCINFO["group" i] printf("%d", group) pw = getgrgid(group) stampa_primo_campo(pw) if (("group" (i+1)) in PROCINFO) printf(",") } print "" } function stampa_primo_campo(str, a) { if (str != "") { split(str, a, ":") printf("(%s)", a[1]) } }
Il test incluso nel ciclo for
è degno di nota.
Ogni ulteriore gruppo nel vettore PROCINFO
ha come indice da
"group1"
a "groupN"
dove il numero
N è il numero totale di gruppi ulteriori).
Tuttavia, non si sa quanti di questi gruppi ci siano per un dato utente.
Questo ciclo inizia da uno, concatena il valore di ogni iterazione con
"group"
, e poi usando l’istruzione in
verifica se quella
chiave è nel vettore (vedi Visitare elementi). Quando i
è
incrementato oltre l’ultimo gruppo presente nel vettore, il ciclo termina.
Il ciclo funziona correttamente anche se non ci sono ulteriori gruppi; in quel caso la condizione risulta falsa fin dal primo controllo, e il corpo del ciclo non viene mai eseguito.
La funzione stampa_primo_campo()
semplicemente incapsula quelle parti di
codice che vengono usate ripetutamente, rendendo il programma più conciso e
ordinato.
In particolare, inserendo in questa funzione il test per la stringa nulla
consente di risparmiare parecchie righe di programma.
Successivo: Programma tee, Precedente: Programma id, Su: Cloni [Contenuti][Indice]
Il programma split
divide grossi file di testo in pezzi più piccoli.
La sua sintassi è la seguente:80
split
[-contatore
] [file] [prefisso]
Per default, i file di output avranno nome xaa, xab, e così via. Ogni file contiene 1.000 righe, con la probabile eccezione dell’ultimo file. Per cambiare il numero di righe in ogni file, va indicato un numero sulla riga di comando, preceduto da un segno meno (p.es., ‘-500’ per file con 500 righe ognuno invece che 1.000). Per modificare i nomi dei file di output in qualcosa del tipo miofileaa, miofileab, e così via, va indicato un argomento ulteriore che specifica il prefisso del nome-file.
Ecco una versione di split
in awk
. Usa le funzioni
ord()
e chr()
descritte nella
Funzioni ordinali.
Il programma dapprima imposta i suoi valori di default, e poi controlla che non siano stati specificati troppi argomenti. Quindi esamina gli argomenti uno alla volta. Il primo argomento potrebbe essere un segno meno seguito da un numero. Poiché il numero in questione può apparire negativo, lo si fa diventare positivo, e viene usato per contare le righe. Il nome del file-dati è per ora ignorato e l’ultimo argomento è usato come prefisso per i nomi-file in output:
# split.awk --- comando split scritto in awk # # Richiede le funzioni di libreria ord() e chr() # sintassi: split [-contatore] [file] [nome_in_output] BEGIN { outfile = "x" # default contatore = 1000 if (ARGC > 4) sintassi() i = 1 if (i in ARGV && ARGV[i] ~ /^-[[:digit:]]+$/) { contatore = -ARGV[i] ARGV[i] = "" i++ } # testa argv nel caso che si legga da stdin invece che da file if (i in ARGV) i++ # salta nome file-dati if (i in ARGV) { outfile = ARGV[i] ARGV[i] = "" } s1 = s2 = "a" out = (outfile s1 s2) }
La regola seguente fa il grosso del lavoro. contatore_t
(contatore temporaneo) tiene conto di
quante righe sono state stampate sul file di output finora. Se questo
numero supera il valore di contatore
, è ora di chiudere il file
corrente e di iniziare a scriverne uno nuovo.
Le variabili s1
e s2
sono usate per creare i suffissi
da apporre a nome-file. Se entrambi arrivano al valore ‘z’, il file
è troppo grosso. Altrimenti, s1
passa alla successiva lettera
dell’alfabeto e s2
ricomincia da ‘a’:
{ if (++contatore_t > contatore) { close(out) if (s2 == "z") { if (s1 == "z") { printf("split: %s è troppo grosso da suddividere\n", nome_file) > "/dev/stderr" exit 1 } s1 = chr(ord(s1) + 1) s2 = "a" }
else s2 = chr(ord(s2) + 1)
out = (outfile s1 s2) contatore_t = 1 } print > out }
La funzione sintassi()
stampa solo un messaggio di errore ed esce:
function sintassi() { print("sintassi: split [-num] [file] [nome_in_output]") > "/dev/stderr" exit 1 }
Questo programma è un po’ approssimativo; conta sul fatto che awk
chiuda
automaticamente l’ultimo file invece di farlo in una regola END
.
Un altro presupposto del programma è che le lettere dell’alfabeto siano
in posizioni consecutive nella codifica in uso, il che non è vero per i
sistemi che usano la codifica EBCDIC.
Successivo: Programma uniq, Precedente: Programma split, Su: Cloni [Contenuti][Indice]
Il programma tee
è noto come pipe fitting (tubo secondario).
tee
copia il suo standard input al suo standard output e inoltre lo
duplica scrivendo sui file indicati nella riga di comando. La sua sintassi
è la seguente:
tee
[-a] file …
L’opzione -a chiede a tee
di aggiungere in fondo al file
indicato, invece che riscriverlo dall’inizio.
La regola BEGIN
dapprima fa una copia di tutti gli argomenti presenti
sulla riga di comando, in un vettore di nome copia
.
ARGV[0]
non serve, e quindi non viene copiato.
tee
non può usare ARGV
direttamente, perché awk
tenta
di elaborare ogni nome-file in ARGV
come dati in input.
Se il primo argomento è -a, la variabile flag
append
viene impostata a vero, e sia ARGV[1]
che
copia[1]
vengono cancellati. Se ARGC
è minore di due, nessun
nome-file è stato fornito, e tee
stampa un messaggio di sintassi ed
esce.
Infine, awk
viene obbligato a leggere lo standard input
impostando ARGV[1]
al valore "-"
e ARGC
a due:
# tee.awk --- tee in awk # # Copia lo standard input a tutti i file di output indicati. # Aggiunge in fondo se viene data l'opzione -a. # BEGIN { for (i = 1; i < ARGC; i++) copia[i] = ARGV[i] if (ARGV[1] == "-a") { append = 1 delete ARGV[1] delete copia[1] ARGC-- } if (ARGC < 2) { print "sintassi: tee [-a] file ..." > "/dev/stderr" exit 1 } ARGV[1] = "-" ARGC = 2 }
La seguente regola è sufficiente da sola a eseguire il lavoro. Poiché non è presente alcun criterio di ricerca, la regola è eseguita per ogni riga di input. Il corpo della regola si limita a stampare la riga su ogni file indicato nella riga di comando, e poi sullo standard output:
{ # spostare l'if fuori dal ciclo ne velocizza l'esecuzione if (append) for (i in copia) print >> copia[i] else for (i in copia) print > copia[i] print }
È anche possibile scrivere il ciclo così:
for (i in copia) if (append) print >> copia[i] else print > copia[i]
Questa forma è più concisa, ma anche meno efficiente. L’‘if’ è
eseguito per ogni record e per ogni file di output. Duplicando il corpo
del ciclo, l’‘if’ è eseguito solo una volta per ogni record in input.
Se ci sono
N record in input e M file di output, il primo metodo esegue solo
N istruzioni ‘if’, mentre il secondo esegue
N*
M istruzioni ‘if’.
Infine, la regola END
fa pulizia, chiudendo tutti i file di output:
END { for (i in copia) close(copia[i]) }
Successivo: Programma wc, Precedente: Programma tee, Su: Cloni [Contenuti][Indice]
Il programma di utilità uniq
legge righe di dati ordinati sul suo
standard input, e per default rimuove righe duplicate. In altre parole,
stampa solo righe uniche; da cui il
nome. uniq
ha diverse opzioni. La sintassi è la seguente:
uniq
[-udc [-n
]] [+n
] [file_input [file_output]]
Le opzioni per uniq
sono:
-d
Stampa solo righe ripetute (duplicate).
-u
Stampa solo righe non ripetute (uniche).
-c
Contatore righe. Quest’opzione annulla le opzioni -d e -u. Sia le righe ripetute che quelle non ripetute vengono contate.
-n
Salta n campi prima di confrontare le righe. La definizione di campo
è simile al default di awk
: caratteri non bianchi, separati da
sequenze di spazi e/o TAB.
+n
Salta n caratteri prima di confrontare le righe. Eventuali campi specificati con ‘-n’ sono saltati prima.
file_input
I dati sono letti dal file in input specificato sulla riga di comando, invece che dallo standard input.
file_output
L’output generato è scritto sul file di output specificato, invece che sullo standard output.
Normalmente uniq
si comporta come se siano state specificate entrambe
le opzioni -d e -u.
uniq
usa la
funzione di libreria getopt()
(vedi Funzione getopt)
e la funzione di libreria join()
(vedi Funzione join).
Il programma inizia con una funzione sintassi()
e poi con una breve
spiegazione delle opzioni e del loro significato, sotto forma di commenti.
La regola BEGIN
elabora gli argomenti della riga di comando e le
opzioni. Viene usato un artificio per poter impiegare getopt()
con
opzioni della forma ‘-25’,
trattando quest’opzione come la lettera di opzione ‘2’ con
l’argomento ‘5’. Se si specificano due o più cifre (Optarg
sembra essere numerico), Optarg
è concatenato con la cifra che
costituisce l’opzione e poi al risultato è addizionato zero, per trasformarlo
in un numero. Se c’è solo una cifra nell’opzione, Optarg
non è
necessario. In tal caso, Optind
dev’essere decrementata, in modo che
getopt()
la elabori quando viene nuovamente richiamato. Questo codice
è sicuramente un po’ intricato.
Se non sono specificate opzioni, per default si stampano sia le righe
ripetute che quelle non ripetute. Il file di output, se specificato, è
assegnato a file_output
. In precedenza, file_output
è
inizializzato allo standard output, /dev/stdout:
# uniq.awk --- implementa uniq in awk # # Richiede le funzioni di libreria getopt() e join()
function sintassi() { print("sintassi: uniq [-udc [-n]] [+n] [ in [ out ]]") > "/dev/stderr" exit 1 } # -c contatore di righe. prevale su -d e -u # -d solo righe ripetute # -u solo righe non ripetute # -n salta n campi # +n salta n caratteri, salta prima eventuali campi BEGIN { contatore = 1 file_output = "/dev/stdout" opts = "udc0:1:2:3:4:5:6:7:8:9:" while ((c = getopt(ARGC, ARGV, opts)) != -1) { if (c == "u") solo_non_ripetute++ else if (c == "d") solo_ripetute++ else if (c == "c") conta_record++ else if (index("0123456789", c) != 0) { # getopt() richiede argomenti per le opzioni # questo consente di gestire cose come -5 if (Optarg ~ /^[[:digit:]]+$/) contatore_file = (c Optarg) + 0 else { contatore_file = c + 0 Optind-- } } else sintassi() } if (ARGV[Optind] ~ /^\+[[:digit:]]+$/) { conta_caratteri = substr(ARGV[Optind], 2) + 0 Optind++ } for (i = 1; i < Optind; i++) ARGV[i] = "" if (solo_ripetute == 0 && solo_non_ripetute == 0) solo_ripetute = solo_non_ripetute = 1 if (ARGC - Optind == 2) { file_output = ARGV[ARGC - 1] ARGV[ARGC - 1] = "" } }
La funzione seguente, se_sono_uguali()
, confronta la riga corrente,
$0
, con la riga precedente, ultima
. Gestisce il salto di
campi e caratteri. Se non sono stati richiesti né contatori di campo né
contatori di carattere, se_sono_uguali()
restituisce uno o zero a
seconda del risultato di un semplice confronto tra le stringhe ultima
e $0
.
In caso contrario, le cose si complicano. Se devono essere saltati dei campi,
ogni riga viene suddivisa in un vettore, usando split()
(vedi Funzioni per stringhe); i campi desiderati sono poi nuovamente uniti in
un’unica riga usando join()
. Le righe ricongiunte vengono
immagazzinate in campi_ultima
e campi_corrente
. Se non ci
sono campi da saltare, campi_ultima
e campi_corrente
sono
impostati a ultima
e $0
, rispettivamente. Infine, se
occorre saltare dei caratteri, si usa substr()
per eliminare i primi
conta_caratteri
caratteri in campi_ultima
e
campi_corrente
. Le due stringhe sono poi confrontare e
se_sono_uguali()
restituisce il risultato del confronto:
function se_sono_uguali( n, m, campi_ultima, campi_corrente,\ vettore_ultima, vettore_corrente) { if (contatore_file == 0 && conta_caratteri == 0) return (ultima == $0) if (contatore_file > 0) { n = split(ultima, vettore_ultima) m = split($0, vettore_corrente) campi_ultima = join(vettore_ultima, contatore_file+1, n) campi_corrente = join(vettore_corrente, contatore_file+1, m) } else { campi_ultima = ultima campi_corrente = $0 } if (conta_caratteri) { campi_ultima = substr(campi_ultima, conta_caratteri + 1) campi_corrente = substr(campi_corrente, conta_caratteri + 1) } return (campi_ultima == campi_corrente) }
Le due regole seguenti sono il corpo del programma. La prima è eseguita solo
per la prima riga dei dati. Imposta ultima
al record corrente
$0
, in modo che le righe di testo successive abbiano qualcosa con cui
essere confrontate.
La seconda regola fa il lavoro. La variabile uguale
vale uno o zero,
a seconda del risultato del confronto effettuato in se_sono_uguali()
.
Se uniq
sta contando le righe ripetute, e le righe sono uguali,
viene incrementata la variabile contatore
.
Altrimenti, viene stampata la riga e azzerato contatore
,
perché le due righe non sono uguali.
Se uniq
non sta contando, e se le righe sono uguali,
contatore
è incrementato.
Non viene stampato niente, perché l’obiettivo è quello di rimuovere i duplicati.
Altrimenti, se uniq
sta contando le righe ripetute e viene trovata più
di una riga, o se uniq
sta contando le righe non ripetute
e viene trovata solo una riga, questa riga viene stampata, e contatore
è
azzerato.
Infine, una logica simile è usata nella regola END
per stampare
l’ultima riga di dati in input:
NR == 1 { ultima = $0 next } { uguale = se_sono_uguali() if (conta_record) { # prevale su -d e -u if (uguale) contatore++ else { printf("%4d %s\n", contatore, ultima) > file_output ultima = $0 contatore = 1 # reset } next } if (uguale) contatore++ else { if ((solo_ripetute && contatore > 1) || (solo_non_ripetute && contatore == 1)) print ultima > file_output ultima = $0 contatore = 1 } } END { if (conta_record) printf("%4d %s\n", contatore, ultima) > file_output else if ((solo_ripetute && contatore > 1) || (solo_non_ripetute && contatore == 1)) print ultima > file_output close(file_output) }
Precedente: Programma uniq, Su: Cloni [Contenuti][Indice]
Il programma di utilità wc
(word count, contatore di parole)
conta righe, parole, e caratteri in uno o più file in input. La sua sintassi
è la seguente:
wc
[-lwc] [file …]
Se nessun file è specificato sulla riga di comando, wc
legge il suo
standard input. Se ci sono più file, stampa anche il contatore totale di
tutti i file. Le opzioni e il loro significato sono i seguenti:
-l
Conta solo le righe.
-w
Conta solo le parole.
Una “parola” è una sequenza contigua di caratteri non bianchi, separata da
spazi e/o TAB. Fortunatamente, questo è il modo normale in cui awk
separa i campi nei suoi record in input.
-c
Conta solo i caratteri.
Implementare wc
in awk
è particolarmente elegante,
perché awk
fa molto lavoro al posto nostro; divide le righe in
parole (cioè, campi) e le conta, conta le righe (cioè, i record),
e può facilmente dire quanto è lunga una riga.
Questo programma usa la funzione di libreria getopt()
(vedi Funzione getopt)
e le funzioni di passaggio da un file all’altro
(vedi Funzione filetrans).
Questa versione ha una differenza significativa rispetto alle versioni
tradizionali di wc
: stampa sempre i contatori rispettando l’ordine
righe, parole e caratteri. Le versioni tradizionali rilevano l’ordine in cui
sono specificate le opzioni -l, -w e -c sulla riga
di comando, e stampano i contatori in quell’ordine.
La regola BEGIN
si occupa degli argomenti. La variabile
stampa_totale
è vera se più di un file è presente sulla
riga di comando:
# wc.awk --- conta righe, parole, caratteri # Opzioni: # -l conta solo righe # -w conta solo parole # -c conta solo caratteri # # Il default è di contare righe, parole, caratteri # # Richiede le funzioni di libreria getopt() # e il programma di libreria che gestisce # il passaggio da un file dati al successivo BEGIN { # consente a getopt() di stampare un messaggio se si specificano # opzioni non valide. Noi le ignoriamo while ((c = getopt(ARGC, ARGV, "lwc")) != -1) { if (c == "l") conta_righe = 1 else if (c == "w") conta_parole = 1 else if (c == "c") conta_caratteri = 1 } for (i = 1; i < Optind; i++) ARGV[i] = "" # se nessuna opzione è specificata, conta tutto if (! conta_righe && ! conta_parole && ! conta_caratteri) conta_righe = conta_parole = conta_caratteri = 1 stampa_totale = (ARGC - i > 1) }
La funzione a_inizio_file()
è semplice; si limita ad azzerare i contatori
di righe, parole e caratteri, e salva il valore corrente di nome-file in
nome_file
:
function a_inizio_file(file) { righe = parole = caratteri = 0 nome_file = FILENAME }
La funzione a_fine_file()
aggiunge i numeri del file corrente al totale
di righe, parole, e caratteri. Poi stampa i numeri relativi al file appena
letto. La funzione
a_inizio_file()
azzera i numeri relativi al file-dati seguente:
function a_fine_file(file) { totale_righe += righe totale_parole += parole totale_caratteri += caratteri if (conta_righe) printf "\t%d", righe
if (conta_parole) printf "\t%d", parole
if (conta_caratteri) printf "\t%d", caratteri printf "\t%s\n", nome_file }
C’è una regola che viene eseguita per ogni riga. Aggiunge la lunghezza del record
più uno, a caratteri
.81
Aggiungere uno alla lunghezza del record
è necessario, perché il carattere di ritorno a capo, che separa i record
(il valore di RS
) non è parte del record stesso, e quindi non è
incluso nella sua lunghezza. Poi, righe
è incrementata per ogni riga
letta, e parole
è incrementato con il valore NF
, che è il
numero di “parole” su questa riga:
# per ogni riga... { caratteri += length($0) + 1 # aggiunge un ritorno a capo righe++ parole += NF }
Infine, la regola END
si limita a stampare i totali per tutti i file:
END { if (stampa_totale) { if (conta_righe) printf "\t%d", totale_righe if (conta_parole) printf "\t%d", totale_parole if (conta_caratteri) printf "\t%d", totale_caratteri print "\ttotale" } }
Successivo: Sommario dei programmi, Precedente: Cloni, Su: Programmi di esempio [Contenuti][Indice]
awk
Questa sezione è un “paniere” che contiene vari programmi. Si spera che siano interessanti e divertenti.
• Programma dupword: | Trovare parole duplicate in un documento. | |
• Programma alarm: | Un programma di sveglia. | |
• Programma translate: | Un programma simile al programma di utilità
tr .
| |
• Programma labels: | Stampare etichette per lettere. | |
• Programma utilizzo parole: | Un programma per produrre un contatore dell’uso di parole in un testo. | |
• Programma riordino diario: | Eliminare righe doppie da un file di cronologia. | |
• Programma extract : | Estrarre programmi da file sorgenti Texinfo. | |
• Programma sed semplice: | Un semplice editor di flusso. | |
• Programma igawk: | Un programma per fornire ad
awk la possibilità di includere
file.
| |
• Programma anagram: | Trovare anagrammi da una lista di parole. | |
• Programma signature: | La gente fa cose stupefacenti se ha troppo tempo libero. |
Successivo: Programma alarm, Su: Programmi vari [Contenuti][Indice]
Un errore comune quando si scrive un testo lungo è quello di ripetere accidentalmente delle parole. Tipicamente lo si può vedere in testi del tipo “questo questo programma fa quanto segue…” Quando il testo è pubblicato in rete, spesso le parole duplicate sono poste tra il termine di una riga e l’inizio di un’altra, il che rende difficile scoprirle.
Questo programma, dupword.awk, legge un file una riga alla volta
e cerca le occorrenze adiacenti della stessa parola. Conserva anche
l’ultima parola di ogni riga (nella variabile precedente
) per
confrontarla con la prima parola sulla riga successiva.
Le prime due istruzioni fanno sì che la riga sia tutta in minuscolo, in modo che, per esempio, “Il” e “il” risultino essere la stessa parola. L’istruzione successiva sostituisce i caratteri che sono non alfanumerici e diversi dagli spazi bianchi con degli spazi, in modo che neppure la punteggiatura influenzi i confronti. I caratteri sono rimpiazzati da spazi in modo che i controlli di formattazione non creino parole prive di senso (p.es., l’espressione Texinfo ‘@code{NF}’ diventa ‘codeNF’, se ci si limita a eliminare la punteggiatura). Il record è poi suddiviso di nuovo in campi, producendo così solo la lista delle parole presenti sulla riga, esclusi eventuali campi nulli.
Se, dopo aver rimosso tutta la punteggiatura, non rimane alcun campo, il record corrente è saltato. In caso contrario, il programma esegue il ciclo per ogni parola, confrontandola con quella che la precede:
# dupword.awk --- trova parole duplicate in un testo { $0 = tolower($0) gsub(/[^[:alnum:][:blank:]]/, " "); $0 = $0 # divide di nuovo in campi if (NF == 0) next if ($1 == prec) printf("%s:%d: duplicato %s\n", nome_file, FNR, $1) for (i = 2; i <= NF; i++) if ($i == $(i-1)) printf("%s:%d: duplicato %s\n", nome_file, FNR, $i) prec = $NF }
Successivo: Programma translate, Precedente: Programma dupword, Su: Programmi vari [Contenuti][Indice]
Nessuna cura contro l’insonnia è efficace quanto una sveglia che suona.
Il sonno è per sviluppatori web.
Il seguente programma è un semplice programma di “sveglia”. Si può specificare un’ora del giorno e un messaggio opzionale. All’ora specificata, il programma stampa il messaggio sullo standard output. Inoltre, si può specificare il numero di volte in cui il messaggio va ripetuto, e anche un intervallo di tempo (ritardo) tra ogni ripetizione.
Questo programma usa la funzione getlocaltime()
da
Funzione getlocaltime.
Tutto il lavoro è svolto nella regola BEGIN
. La prima parte è
il controllo degli argomenti e l’impostazione dei valori di default:
l’intervallo prima di ripetere, il contatore, e il messaggio da stampare.
Se l’utente ha fornito un messaggio che non contiene il carattere ASCII BEL
(noto come carattere “campanello”, "\a"
), questo viene aggiunto al
messaggio. (Su molti sistemi, stampare il carattere ASCII BEL genera un suono
udibile. Quindi, quando la sveglia suona, il sistema richiama l’attenzione
su di sé nel caso che l’utente non stia guardando il computer.)
Per amor di varietà, questo programma usa un’istruzione switch
(vedi Istruzione switch), ma l’elaborazione potrebbe anche essere fatta
con una serie di istruzioni if
-else
.
Ecco il programma:
# alarm.awk --- impostare una sveglia # # Richiede la funzione di libreria getlocaltime() # sintassi: alarm a_che_ora [ "messaggio" [ contatore [ ritardo ] ] ] BEGIN { # Controllo iniziale congruità argomenti sintassi1 = "sintassi: alarm a_che_ora ['messaggio' [contatore [ritardo]]]" sintassi2 = sprintf("\t(%s) formato ora: ::= hh:mm", ARGV[1]) if (ARGC < 2) { print sintassi1 > "/dev/stderr" print sintassi2 > "/dev/stderr" exit 1 } switch (ARGC) { case 5: ritardo = ARGV[4] + 0 # vai al caso seguente case 4: contatore = ARGV[3] + 0 # vai al caso seguente case 3: messaggio = ARGV[2] break default: if (ARGV[1] !~ /[[:digit:]]?[[:digit:]]:[[:digit:]]{2}/) { print sintassi1 > "/dev/stderr" print sintassi2 > "/dev/stderr" exit 1 } break } # imposta i valori di default per quando arriva l'ora desiderata if (ritardo == 0) ritardo = 180 # 3 minuti
if (contatore == 0) contatore = 5
if (messaggio == "") messaggio = sprintf("\aAdesso sono le %s!\a", ARGV[1]) else if (index(message, "\a") == 0) messaggio = "\a" messaggio "\a"
La successiva sezione di codice scompone l’ora specificata in ore e minuti, la converte (se è il caso) al formato 24-ore, e poi calcola il relativo numero di secondi dalla mezzanotte. Poi trasforma l’ora corrente in un contatore dei secondi dalla mezzanotte. La differenza tra i due è il tempo di attesa che deve passare prima di far scattare la sveglia:
# scomponi ora della sveglia split(ARGV[1], ore_minuti, ":") ora = ore_minuti[1] + 0 # trasforma in numero minuto = ore_minuti[2] + 0 # trasforma in numero # ottiene ora corrente divisa in campi getlocaltime(adesso) # se l'ora desiderata è in formato 12-ore ed è nel pomeriggio # (p.es., impostare `alarm 5:30' alle 9 del mattino # vuol dire far suonare la sveglia alle 5:30 pomeridiane) # aggiungere 12 all'ora richiesta if (hour < 12 && adesso["hour"] > ora) ora += 12 # imposta l'ora in secondi dalla mezzanotte sveglia = (ora * 60 * 60) + (minuto * 60) # ottieni l'ora corrente in secondi dalla mezzanotte corrente = (now["hour"] * 60 * 60) + \ (now["minute"] * 60) + now["second"] # quanto restare appisolati sonno = sveglia - corrente if (sonno <= 0) { print "alarm: l'ora è nel passato!" > "/dev/stderr" exit 1 }
Infine, il programma usa la funzione system()
(vedi Funzioni di I/O)
per chiamare il programma di utilità sleep
. Il programma di utilità
sleep
non fa altro che aspettare per il numero di secondi
specificato. Se il codice di ritorno restituito è diverso da zero, il
programma suppone che sleep
sia stato interrotto ed esce. Se
sleep
è terminato con un codice di ritorno corretto, (zero), il
programma stampa il messaggio in un ciclo, utilizzando ancora sleep
per ritardare per il numero di secondi necessario:
# zzzzzz..... esci se sleep è interrotto if (system(sprintf("sleep %d", sonno)) != 0) exit 1 # è ora di avvisare! command = sprintf("sleep %d", ritardo) for (i = 1; i <= contatore; i++) { print messaggio # se il comando sleep è interrotto, esci if (system(command) != 0) break } exit 0 }
Successivo: Programma labels, Precedente: Programma alarm, Su: Programmi vari [Contenuti][Indice]
Il programma di utilità di sistema tr
rimpiazza caratteri. Per
esempio, è spesso usato per trasformare lettere maiuscole in lettere minuscole
in vista di ulteriori elaborazioni:
generare dei dati | tr 'A-Z' 'a-z' | elaborare dei dati …
tr
richiede due liste di caratteri.82 Quando
si elabora l’input, il primo carattere della prima lista è rimpiazzato con il
primo carattere della seconda lista, il secondo carattere della prima lista è
rimpiazzato con il secondo carattere della seconda lista, e così via. Se ci
sono più caratteri nella lista “da” che in quella “a”, l’ultimo carattere
della lista “a” è usato per i restanti caratteri della lista “da”.
In un lontano passato,
un utente propose di aggiungere una funzione di traslitterazione a
gawk
.
Il programma seguente è stato scritto per dimostrare che la traslitterazione
di caratteri poteva essere fatta con una funzione definita dall’utente.
Questo programma non è così completo come il programma di utilità di sistema
tr
, ma svolge buona parte dello stesso lavoro.
Il programma translate
è stato scritto molto prima che gawk
fosse in grado di separare ciascun carattere di una stringa in elementi
distinti di un vettore. Questo è il motivo per cui usa ripetutamente le
funzioni predefinite substr()
, index()
, e gsub()
(vedi Funzioni per stringhe).
Ci sono due funzioni. La prima, traduci_stringa()
,
richiede tre argomenti:
da
Una lista di caratteri da cui traslitterare
a
Una lista di caratteri a cui traslitterare
stringa
La stringa su cui effettuare la traslitterazione
I vettori associativi facilitano molto la parte di traslitterazione.
vettore_trad
contiene i caratteri “a”, indicizzato dai
caratteri “da”. Poi un semplice
ciclo scandisce da
, un carattere alla volta. Per ogni carattere
in da
, se il carattere compare in stringa
è rimpiazzato con il corrispondente carattere a
.
La funzione traducilo()
chiama traduci_stringa()
, usando $0
come stringa. Il programma principale imposta due variabili globali, DA
e
A
, dalla riga di comando, e poi modifica ARGV
in modo che
awk
legga dallo standard input.
Infine, la regola di elaborazione si limita a chiamare traducilo()
per ogni record:
# translate.awk --- fa cose simili al comando tr # Bug: non gestisce cose del tipo tr A-Z a-z; deve essere # descritto carattere per carattere. # Tuttavia, se `a' è più corto di `da', # l'ultimo carattere in `a' è usato per il resto di `da'. function traduci_stringa(da, a, stringa, lf, lt, lstringa, vettore_trad, i, c, risultato) { lf = length(da) lt = length(a) lstringa = length(stringa) for (i = 1; i <= lt; i++) vettore_trad[substr(da, i, 1)] = substr(a, i, 1) if (lt < lf) for (; i <= lf; i++) vettore_trad[substr(da, i, 1)] = substr(a, lt, 1) for (i = 1; i <= lstringa; i++) { c = substr(stringa, i, 1) if (c in vettore_trad) c = vettore_trad[c] risultato = risultato c } return risultato } function traducilo(da, a) { return $0 = traduci_stringa(da, a, $0) } # programma principale BEGIN {
if (ARGC < 3) { print "sintassi: translate da a" > "/dev/stderr" exit }
DA = ARGV[1] A = ARGV[2] ARGC = 2 ARGV[1] = "-" } { traducilo(DA, A) print }
È possibile effettuare la traslitterazione di caratteri in una funzione a
livello utente, ma non è detto che sia efficiente, e noi (sviluppatori
di gawk
) abbiamo iniziato a prendere in considerazione l’aggiunta di una funzione.
Tuttavia, poco dopo aver scritto questo programma, abbiamo saputo che Brian
Kernighan aveva aggiunto le funzioni toupper()
e tolower()
alla
sua versione di awk
(vedi Funzioni per stringhe). Queste
funzioni gestiscono la maggior parte dei casi in cui serva la traslitterazione
di caratteri, e quindi abbiamo deciso di limitarci ad aggiungere le stesse
funzioni a gawk
, e di disinteressarci del resto.
Un miglioramento ovvio a questo programma sarebbe di impostare il vettore
vettore_trad
solo una volta, in una regola BEGIN
. Tuttavia, ciò
presuppone che le liste “da” e “a” non cambino mai durante tutta
l’esecuzione del programma.
Un altro miglioramento ovvio è di consentire l’uso di intervalli, come
‘a-z’, come consentito dal programma di utilità tr
. Si può
trarre ispirazione dal codice di cut.awk (vedi Programma cut).
Successivo: Programma utilizzo parole, Precedente: Programma translate, Su: Programmi vari [Contenuti][Indice]
Ecco un programma “del mondo-reale”83. Questo script legge elenchi di nomi e indirizzi, e genera etichette per lettera. Ogni pagina di etichette contiene 20 etichette, su due file da 10 etichette l’una. Gli indirizzi non possono contenere più di cinque righe di dati. Ogni indirizzo è separato dal successivo da una riga bianca.
L’idea di base è di leggere dati per 20 etichette. Ogni riga di ogni etichetta
è immagazzinata nel vettore riga
. L’unica regola si occupa di riempire
il vettore riga
e di stampare la pagina dopo che sono state lette 20
etichette.
La regola BEGIN
si limita a impostare RS
alla stringa vuota, in
modo che awk
divida un record dal successivo quando incontra una riga
bianca.
(vedi Record).
Inoltre imposta LIMITE_LINEE
a 100,
perché 100 è il massimo numero di righe sulla pagina
(20 * 5 = 100).
Il grosso del lavoro è svolto nella funzione stampa_pagina()
.
Le righe che compongono le etichette sono immagazzinate sequenzialmente nel vettore
riga
. Ma occorre stamparle in
orizzontale: riga[1]
a fianco di riga[6]
, riga[2]
a
fianco di riga[7]
, e così via. Questo si può fare utilizzando due
cicli. Quello più esterno, controllato dalla variabile i
, gestisce 10
righe di dati, ovvero la stampa di due etichette una a fianco dell’altra.
Il ciclo più interno
controllato dalla variabile j
, gestisce le singole righe che compongono
ognuno degli indirizzi.
Poiché j
varia da 0 a 4, ‘i+j’ è la riga j
-esima
dell’indirizzo di sinistra, e ‘i+j+5’ è quella stampata alla sua destra.
L’output è simile a quello mostrato qui sotto:
riga 1 riga 6 riga 2 riga 7 riga 3 riga 8 riga 4 riga 9 riga 5 riga 10 …
La stringa di formato per printf
‘%-41s’ allinea a
sinistra i dati, e li stampa in un campo di lunghezza fissa.
Come nota finale, un’ulteriore riga bianca extra viene stampata alle righe 21 e 61, per mantenere entro i bordi l’output sulle etichette. Ciò dipende dalla particolare marca di etichette in uso quando il programma è stato scritto. Si noti anche che ci sono due righe bianche a inizio pagina e due righe bianche a fine pagina.
La regola END
si occupa di stampare l’ultima pagina di
etichette; è improbabile che il numero di indirizzi da stampare sia un
multiplo esatto di 20:
# labels.awk --- stampare etichette per lettera # Ogni etichetta è 5 righe di dati, qualcuna delle quali può essere bianca. # I fogli con le etichetta hanno 2 righe bianche in cima alla pagina e altre 2 # a fine pagina. BEGIN { RS = "" ; LIMITE_LINEE = 100 } function stampa_pagina( i, j) { if (NUMEROrighe <= 0) return printf "\n\n" # in cima for (i = 1; i <= NUMEROrighe; i += 10) { if (i == 21 || i == 61) print "" for (j = 0; j < 5; j++) { if (i + j > LIMITE_LINEE) break printf " %-41s %s\n", riga[i+j], riga[i+j+5] } print "" } printf "\n\n" # in fondo delete riga } # regola principale { if (contatore >= 20) { stampa_pagina() contatore = 0 NUMEROrighe = 0 } n = split($0, a, "\n") for (i = 1; i <= n; i++) riga[++NUMEROrighe] = a[i] for (; i <= 5; i++) riga[++NUMEROrighe] = "" contatore++ } END { stampa_pagina() }
Successivo: Programma riordino diario, Precedente: Programma labels, Su: Programmi vari [Contenuti][Indice]
Quando si lavora con una grande quantità di testo, può essere interessante sapere quanto spesso ricorrono le diverse parole. Per esempio, un autore può fare un uso eccessivo di certe parole, e in questo caso si potrebbero trovare sinonimi da sostituire a parole che appaiono troppo spesso. Questa sottosezione spiega come scrivere un programma per contare le parole e presentare in un formato utile le informazioni relative alla loro frequenza.
A prima vista, un programma come questo sembrerebbe essere sufficiente:
# wordfreq-first-try.awk --- stampa lista frequenze utilizzo parole { for (i = 1; i <= NF; i++) freq[$i]++ } END { for (word in freq) printf "%s\t%d\n", word, freq[word] }
Il programma si affida al meccanismo con cui awk
divide i campi per
default, per suddividere ogni riga in “parole” e usa un vettore associativo
di nome freq
, che ha per indici le singole parole, per contare il
numero di volte che ogni parola viene usata. Nella regola END
, stampa i
contatori.
Questo programma ha parecchi problemi che lo rendono praticamente inutile su file di testo reali:
awk
considera i caratteri maiuscoli e minuscoli come
distinti (non equivalenti). Quindi, “barista” e “Barista” sono
considerate parole differenti. Questo non è un comportamento auspicabile,
perché le parole iniziano con la lettera maiuscola se sono a inizio frase in
un testo normale, e un analizzatore di frequenze dovrebbe ignorare la
distinzione maiuscolo/minuscolo.
awk
secondo cui i
campi sono separati solo da spazi bianchi. Altri caratteri nell’input
(tranne il ritorno a capo) non hanno alcun particolare significato per
awk
. Questo significa che i segni di interpunzione sono visti come
parte di una parola.
Il primo problema si può risolvere usando tolower()
per rimuovere la
distinzione maiuscolo/minuscolo. Il secondo problema si può risolvere usando
gsub()
per rimuovere i caratteri di interpunzione. Infine, per
risolvere il terzo problema si può usare il programma di utilità
sort
per elaborare l’output dello script awk
. Ecco la
nuova versione del programma:
# wordfreq.awk --- stampa la lista con la frequenza delle parole { $0 = tolower($0) # togli maiuscolo/minuscolo # togli interpunzione gsub(/[^[:alnum:]_[:blank:]]/, "", $0) for (i = 1; i <= NF; i++) freq[$i]++ } END { for (word in freq) printf "%s\t%d\n", word, freq[word] }
La regexp /[^[:alnum:]_[:blank:]]/
si poteva scrivere come
/[[:punct:]]/
, ma in questo modo il caratteri trattino basso sarebbe
stato rimosso, mentre si desidera conservarlo.
Supponendo di aver salvato questo programma in un file di nome wordfreq.awk, e che i dati siano in file1, il seguente comando con pipeline:
awk -f wordfreq.awk file1 | sort -k 2nr
produce una tabella delle parole che appaiono in file1 in ordine descrescente di frequenza.
Il programma awk
da solo gestisce adeguatamente i dati e produce
una tabella delle frequenza che non è ordinata.
L’output di awk
è poi messo in ordine dal programma di utilità
sort
e stampato sullo schermo.
Le opzioni passate a sort
richiedono un ordinamento che usi come chiave il secondo campo di ogni riga
in input (saltando il primo campo), che le chiavi di ordinamento siano
trattate come quantità numeriche
(altrimenti ‘15’ sarebbe stampato prima di ‘5’), e che l’ordinamento
sia fatto in ordine decrescente (inverso).
Il comando sort
potrebbe anche essere richiamato dall’interno del
programma, cambiando l’azione da fare nella regola END
a:
END { sort = "sort -k 2nr" for (word in freq) printf "%s\t%d\n", word, freq[word] | sort close(sort) }
Questa maniera di ordinare dev’essere usata su sistemi che non hanno delle
vere e proprie pipe a livello di riga di comando (o di procedura di
comandi).
Si veda la documentazione generale riguardo al sistema operativo per maggiori
informazioni su come usare il programma sort
.
Successivo: Programma extract, Precedente: Programma utilizzo parole, Su: Programmi vari [Contenuti][Indice]
Il programma uniq
(vedi Programma uniq)
rimuove righe duplicate da dati ordinati.
Si supponga, tuttavia, di dover rimuovere righe duplicate da un file-dati, ma di voler conservare l’ordine in cui le righe sono state scritte. Un buon esempio di questo tipo potrebbe essere un file della cronologia dei comandi della shell. Il file della cronologia dei comandi mantiene copia di tutti i comandi che sono stati dati, e non è insolito ripetere un comando molte volte di fila. Occasionalmente si potrebbe voler compattare la cronologia togliendo le righe duplicate. Tuttavia sarebbe opportuno mantenere l’ordine originale dei comandi.
Questo semplice programma fa questo. Usa due vettori. Il vettore dati
ha come indice il testo di ogni riga.
Per ogni riga, dati[$0]
è incrementato di uno.
Se una particolare riga non è stata ancora vista, dati[$0]
è zero.
In tal caso, il testo della riga è immagazzinato in righe[contatore]
.
Ogni elemento del vettore righe
è un comando unico, e gli
indici di righe
indicano l’ordine in cui quelle righe sono state
incontrate.
La regola END
stampa semplicemente le righe, in ordine:
# histsort.awk --- compatta un file della cronologia dei comandi della shell # Grazie a Byron Rakitzis per l'idea generale
{ if (dati[$0]++ == 0) righe[++contatore] = $0 }
END { for (i = 1; i <= contatore; i++) print righe[i] }
Questo programma può essere un punto di partenza per generare altre
informazioni utili.
Per esempio, usando la seguente istruzione print
nella regola
END
permette di sapere quante volte viene usato un certo comando:
print dati[righe[i]], righe[i]
Questo si può fare perché dati[$0]
è incrementato ogni volta che una
riga è stata trovata.
Successivo: Programma sed semplice, Precedente: Programma riordino diario, Su: Programmi vari [Contenuti][Indice]
Sia questo capitolo che il precedente
(Funzioni di libreria)
presentano un numero elevato di programmi awk
.
Se si vuole fare pratica con questi programmi, è fastidioso doverli
digitare di nuovo manualmente. È per questo che abbiamo pensato a un programma
in grado di estrarre parti di un file in input Texinfo e metterli in file
separati.
Questo Documento è scritto in Texinfo, il programma di formattazione di documenti del progetto GNU. Un solo file sorgente Texinfo può essere usato per produrre sia la documentazione stampata, usando TeX, sia quella online. (Texinfo è esaurientemente documentato nel libro Texinfo—The GNU Documentation Format, disponibile alla Free Software Foundation, e anche online.)
Per quel che ci riguarda, è sufficiente sapere tre cose riguardo ai file di input Texinfo:
awk
. I simboli ‘@’ sono rappresentati nel sorgente
Texinfo come ‘@@’.
Il programma seguente, extract.awk, legge un file sorgente Texinfo
e fa due cose, basandosi sui commenti speciali.
Dopo aver visto il commento ‘@c system …’,
esegue un comando, usando il testo del comando contenuto nella
riga di controllo e passandolo alla funzione system()
(vedi Funzioni di I/O).
Dopo aver trovato il commento ‘@c file nome_file’, ogni riga
successiva è spedita al file nome_file, fino a che si trova un
commento ‘@c endfile’.
Le regole in extract.awk sono soddisfatte sia quando incontrano
‘@c’ che quando incontrano ‘@comment’ e quindi la parte
‘omment’ è opzionale.
Le righe che contengono ‘@group’ e ‘@end group’ sono semplicemente
ignorate.
extract.awk usa la funzione di libreria join()
(vedi Funzione join).
I programmi di esempio nel sorgente Texinfo online di GAWK: Programmare efficacemente in AWK
(gawktexi.in) sono stati tutti inseriti tra righe ‘file’ e righe
‘endfile’. La distribuzione di gawk
usa una copia di
extract.awk per estrarre i programmi di esempio e per installarne
molti in una particolare directory dove gawk
li può trovare.
Il file Texinfo ha un aspetto simile a questo:
… Questo programma ha una regola @code{BEGIN} che stampa un messaggio scherzoso: @example @c file esempi/messages.awk BEGIN @{ print "Non v'allarmate!" @} @c endfile @end example Stampa anche qualche avviso conclusivo: @example @c file esempi/messages.awk END @{ print "Evitate sempre gli archeologi annoiati!" @} @c endfile @end example …
Il programma extract.awk inizia con l’impostare IGNORECASE
a
uno, in modo che un miscuglio di lettere maiuscole e minuscole nelle direttive
non faccia differenza.
La prima regola gestisce le chiamate a system()
, controllando che sia
stato fornito un comando (NF
dev’essere almeno tre) e controllando
anche che il comando termini con un codice di ritorno uguale a zero, che sta
a significare che tutto è andato bene:
# extract.awk --- estrae file ed esegue programmi dal file Texinfo BEGIN { IGNORECASE = 1 } /^@c(omment)?[ \t]+system/ { if (NF < 3) { e = ("extract: " FILENAME ":" FNR) e = (e ": riga `system' con formato errato") print e > "/dev/stderr" next } $1 = "" $2 = "" stat = system($0) if (stat != 0) { e = ("extract: " FILENAME ":" FNR) e = (e ": attenzione: system ha restituito " stat) print e > "/dev/stderr" } }
La variabile e
è stata usata per far sì che la regola
sia agevolemente contenuta nella videata.
La seconda regola gestisce il trasferimento di dati in un file. Verifica che nella direttiva sia stato fornito un nome-file. Se il nome del file non è quello del file corrente, il file corrente viene chiuso. Mantenere aperto il file corrente finché non si trova un nuovo nome file permette di usare la ridirezione ‘>’ per stampare i contenuti nel file, semplificando la gestione dei file aperti.
Il ciclo for
esegue il lavoro. Legge le righe usando getline
(vedi Getline).
Se si raggiunge una fine-file inattesa, viene chiamata la funzione
fine_file_inattesa()
. Se la riga è una riga “endfile”,
il ciclo viene abbandonato.
Se la riga inizia con ‘@group’ o ‘@end group’, la riga viene
ignorata, e si passa a quella seguente. Allo stesso modo, eventuali commenti
all’interno degli esempi vengono ignorati.
Il grosso del lavoro è nelle poche righe che seguono. Se la riga non ha
simboli ‘@’, il programma la può
stampare così com’è. Altrimenti, ogni ‘@’ a inizio parola dev’essere
eliminato.
Per rimuovere i simboli ‘@’, la riga viene divisa nei singoli elementi
del vettore a
, usando la funzione split()
(vedi Funzioni per stringhe).
Il simbolo ‘@’ è usato come carattere di separazione.
Ogni elemento del vettore a
che risulti vuoto indica due caratteri
‘@’ contigui nella riga originale. Per ogni due elementi vuoti
(‘@@’ nel file originale), va inserito un solo simbolo ‘@’ nel
file in output.
Una volta terminato di esaminare il vettore, viene chiamata la funzione join()
specificando nella chiamata il valore di SUBSEP
(vedi Vettori multidimensionali),
per riunire nuovamente i pezzi in una riga sola.
La riga è poi stampata nel file di output:
/^@c(omment)?[ \t]+file/ { if (NF != 3) { e = ("extract: " FILENAME ":" FNR ": riga `file' con formato errato") print e > "/dev/stderr" next } if ($3 != file_corrente) { if (file_corrente != "") close(file_corrente) file_corrente = $3 } for (;;) { if ((getline riga) <= 0) fine_file_inattesa() if (riga ~ /^@c(omment)?[ \t]+endfile/) break else if (riga ~ /^@(end[ \t]+)?group/) continue else if (riga ~ /^@c(omment+)?[ \t]+/) continue if (index(riga, "@") == 0) { print riga > file_corrente continue } n = split(riga, a, "@") # if a[1] == "", vuol dire riga che inizia per @, # non salvare un @ for (i = 2; i <= n; i++) { if (a[i] == "") { # era un @@ a[i] = "@" if (a[i+1] == "") i++ } } print join(a, 1, n, SUBSEP) > file_corrente } }
È importante notare l’uso della ridirezione ‘>’ . L’output fatto usando ‘>’ apre il file solo la prima volta; il file resta poi aperto, e ogni scrittura successiva è aggiunta in fondo al file. (vedi Ridirezione). Ciò rende possibile mischiare testo del programm e commenti esplicativi (come è stato fatto qui) nello stesso file sorgente, senza nessun problema. Il file viene chiuso solo quando viene trovato un nuovo nome di file-dati oppure alla fine del file in input.
Per finire, la funzione fine_file_inattesa()
stampa un
appropriato messaggio di errore ed esce.
La regola END
gestisce la pulizia finale, chiudendo il file aperto:
function fine_file_inattesa() { printf("extract: %s:%d: fine-file inattesa, o errore\n", FILENAME, FNR) > "/dev/stderr" exit 1 }
END { if (file_corrente) close(file_corrente) }
Successivo: Programma igawk, Precedente: Programma extract, Su: Programmi vari [Contenuti][Indice]
Il programma di utilità sed
è un editore di flusso,
ovvero un programma che legge un flusso di dati, lo modifica, e scrive il file
così modificato.
È spesso usato per fare modifiche generalizzate a un grosso file, o a un
flusso di dati generato da una pipeline di comandi.
Sebbene sed
sia un programma piuttosto complesso di suo, l’uso che
se ne fa solitamente è di effettuare delle sostituzioni globali attraverso
una pipeline:
comando1 < dati.originali | sed 's/vecchio/nuovo/g' | comando2 > risultato
Qui, ‘s/vecchio/nuovo/g’ chiede a sed
di ricercare la
regexp ‘vecchio’ in ogni riga di input e di sostituirla
dappertutto con il testo ‘nuovo’ (cioè, in tutte le occorrenze di
ciascuna riga). Questo è simile a quello che fa la funzione di awk
gsub()
(vedi Funzioni per stringhe).
Il programma seguente, awksed.awk, accetta almeno due argomenti dalla riga di comando: l’espressione da ricercare e il testo con cui rimpiazzarla. Ogni ulteriore argomento è considerato come un nome di file-dati da elaborare. Se non ne viene fornito alcuno, si usa lo standard input:
# awksed.awk --- fa s/pippo/pluto/g usando solo print # Ringraziamenti a Michael Brennan per l'idea function sintassi() { print "sintassi: awksed espressione rimpiazzo [file...]" > "/dev/stderr" exit 1 } BEGIN { # valida argomenti if (ARGC < 3) sintassi() RS = ARGV[1] ORS = ARGV[2] # non usare argomenti come nomi di file ARGV[1] = ARGV[2] = "" }
# guarda, mamma, senza mani! { if (RT == "") printf "%s", $0 else print }
Il programma fa assegnamento sulla capacità di gawk
di avere come
RS
una regexp,
e anche sul fatto che RT
viene impostato al testo che effettivamente
delimita il record (vedi Record).
L’idea è di usare RS
come espressione da ricercare. gawk
automaticamente imposta $0
al testo che compare tra due corrispondenze
all’espressione di ricerca.
Questo è appunto il testo che vogliamo conservare inalterato. Quindi,
impostando ORS
al testo che si vuole sostituire, una semplice
istruzione print
scrive il testo che si vuole mantenere, seguito dal
testo che si vuole invece sostituire.
C’è un problema in questo schema, ossia cosa fare se l’ultimo record
non termina con un testo che corrisponde a RS
. Usando un’istruzione
print
incondizionatamente stampa il testo da sostituire, il che non
è corretto.
Tuttavia, se il file non termina con del testo che corrisponde a RS
,
RT
è impostata alla stringa nulla. In tal caso, si può stampare
$0
usando printf
(vedi Printf).
La regola BEGIN
gestisce la preparazione, controllando che ci sia
il numero giusto di argomenti e chiamando sintassi()
se c’è un problema.
Poi imposta RS
e ORS
dagli argomenti della riga di comando e
imposta ARGV[1]
e ARGV[2]
alla stringa nulla, per impedire che
vengano considerati dei nomi-file
(vedi ARGC e ARGV).
La funzione sintassi()
stampa un messaggio di errore ed esce.
Per finire, l’unica regola gestisce lo schema di stampa delineato più sopra,
usando print
o printf
come richiesto, a seconda del valore di
RT
.
Successivo: Programma anagram, Precedente: Programma sed semplice, Su: Programmi vari [Contenuti][Indice]
In
Includere file, abbiamo visto come gawk
preveda la
possibilità di includere file. Tuttavia, questa è un’estensione gawk
.
Questa sezione evidenzia l’utilità di rendere l’inclusione di
file disponibile per awk
standard, e mostra come farlo utilizzando
una combinazione di programmazione di shell e di awk
.
Usare funzioni di libreria in awk
può presentare molti vantaggi.
Incoraggia il riutilizzo di codice e la scrittura di funzioni di tipo
generale. I programmi sono più snelli e quindi più comprensibili.
Tuttavia, usare funzioni di libreria è facile solo in fase di scrittura di
programmi awk
; è invece complicato al momento di eseguirli,
rendendo necessario specificare molte opzioni -f. Se gawk
non è disponibile, non lo sono neppure la variabile d’ambiente AWKPATH
e
la possibilità di conservare funzioni awk
in una directory di
libreria (vedi Opzioni).
Sarebbe bello poter scrivere programmi nel modo seguente:
# funzioni di libreria @include getopt.awk @include join.awk … # programma principale BEGIN { while ((c = getopt(ARGC, ARGV, "a:b:cde")) != -1) … … }
Il programma seguente, igawk.sh, fornisce questo servizio.
Simula la ricerca da parte di gawk
della variabile d’ambiente
AWKPATH
e permette anche delle inclusioni nidificate (cioè,
un file che è stato incluso tramite
@include
può contenere ulteriori istruzioni @include
).
igawk
tenta di includere ogni file una volta sola, in modo che delle
inclusioni nidificate non contengano accidentalmente una funzione di libreria
più di una volta.
igawk
dovrebbe comportarsi esternamente proprio come gawk
.
Questo vuol dire che dovrebbe accettare sulla riga di comando tutti gli
argomenti di gawk
, compresa
la capacità di specificare più file
sorgenti tramite l’opzione -f
e la capacità di mescolare istruzioni da riga di comando e file di sorgenti di
libreria.
Il programma è scritto usando il linguaggio della Shell POSIX
(sh
).84
Il funzionamento è il seguente:
awk
, per quando il programma espanso sarà eseguito.
awk
, mette l’argomento
in una variabile di shell che verrà espansa. Ci sono due casi:
gawk
, ne risulta che il file viene
incluso nel programma al punto giusto.
awk
) sui contenuti della variabile
di shell per espandere le istruzioni
@include
. Il programma espanso è messo in una seconda variabile di
shell.
gawk
e tutti gli altri
argomenti originalmente forniti dall’utente sulla riga di comando (come p.es.
dei nomi di file-dati).
Questo programma usa variabili di shell in quantità: per immagazzinare
argomenti della riga di comando e
il testo del programma awk
che espanderà il programma dell’utente,
per il programma originale dell’utente e per il programma espanso. Questo
modo di procedere risolve potenziali
problemi che potrebbero presentarsi se si usassero invece dei file temporanei,
ma rende lo script un po’ più complicato.
La parte iniziale del programma attiva il tracciamento della shell se il primo argomento è ‘debug’.
La parte successiva esegue un ciclo che esamina ogni argomento della riga di comando. Ci sono parecchi casi da esaminare:
Quest’opzione termina gli argomenti per igawk
. Tutto quel che segue
dovrebbe essere passato al programma awk
dell’utente senza essere
preso in considerazione.
Questo indica che l’opzione successiva è propria di gawk
. Per
facilitare l’elaborazione degli argomenti, l’opzione -W è aggiunta
davanti agli argomenti rimanenti, e il
ciclo continua. (Questo è un trucco di programmazione della sh
.
Non è il caso di preoccuparsene se non si ha familiarità con il comando
sh
.)
Queste opzioni sono conservate e lasciate da gestire a gawk
.
Il nome-file è aggiunto alla variabile di shell programma
, insieme
a un’istruzione @include
.
Il programma di utilità expr
è usato per eliminare la parte
iniziale dell’argomento (p.es., ‘--file=’).
(La sintassi tipica di sh
richiederebbe di usare il comando
echo
e il programma di utilità sed
per far questo.
Sfortunatamente, alcune versioni di echo
valutano le sequenze
di protezione contenute nei loro argomenti, e questo potrebbe finire per
alterare il testo del programma.
L’uso di expr
evita questo problema.)
Il testo sorgente è aggiunto in fondo a programma
.
igawk
stampa il proprio numero di versione, esegue
‘gawk --version’ per ottenere l’informazione relativa alla versione di
gawk
, ed esce.
Se nessuno degli argomenti
-f, --file, -Wfile, --source,
o -Wsource è stato fornito, il primo argomento che non è un’opzione
dovrebbe essere il programma awk
. Se non ci sono argomenti rimasti
sulla riga di comando, igawk
stampa un messaggio di errore ed esce.
Altrimenti, il primo argomento è aggiunto in fondo a programma
.
In qualsiasi caso, dopo che gli argomenti sono stati elaborati,
la variabile di shell
programma
contiene il testo completo del programma originale
awk
.
Il programma è il seguente:
#! /bin/sh # igawk --- come gawk ma abilita l'uso di @include if [ "$1" = debug ] then set -x shift fi # Un ritorno a capo letterale, # per formattare correttamente il testo del programma n=' ' # Inizializza delle variabili alla stringa nulla programma= opts= while [ $# -ne 0 ] # ciclo sugli argomenti do case $1 in --) shift break ;; -W) shift # Il costrutto ${x?'messaggio qui'} stampa un # messaggio diagnostico se $x è la stringa nulla set -- -W"${@?'manca operando'}" continue ;; -[vF]) opts="$opts $1 '${2?'manca operando'}'" shift ;; -[vF]*) opts="$opts '$1'" ;; -f) programma="$programma$n@include ${2?'manca operando'}" shift ;; -f*) f=$(expr "$1" : '-f\(.*\)') programma="$programma$n@include $f" ;; -[W-]file=*) f=$(expr "$1" : '-.file=\(.*\)') programma="$programma$n@include $f" ;; -[W-]file) programma="$programma$n@include ${2?'manca operando'}" shift ;; -[W-]source=*) t=$(expr "$1" : '-.source=\(.*\)') programma="$programma$n$t" ;; -[W-]source) programma="$programma$n${2?'manca operando'}" shift ;; -[W-]version) echo igawk: version 3.0 1>&2 gawk --version exit 0 ;; -[W-]*) opts="$opts '$1'" ;; *) break ;; esac shift done if [ -z "$programma" ] then programma=${1?'manca programma'} shift fi # A questo punto, `programma' contiene il programma.
Il programma awk
che elabora le direttive @include
è immagazzinato nella variabile di shell progr_che_espande
. Ciò serve
a mantenere leggibile lo script. Questo programma awk
legge
tutto il programma dell’utente, una riga per volta, usando getline
(vedi Getline). I nomi-file in input e le istruzioni @include
sono gestiti usando una pila. Man mano che viene trovata una @include
,
il valore corrente di nome-file è
“spinto” sulla pila e il file menzionato nella direttiva @include
diventa il nome-file corrente. Man mano che un file è finito,
la pila viene “disfatta”, e il precedente file in input diventa nuovamente il
file in input corrente. Il processo viene iniziato ponendo il file originale
come primo file sulla pila.
La funzione percorso()
trova qual è il percorso completo di un file.
Simula il comportamento di gawk
quando utilizza la variabile
d’ambiente AWKPATH
(vedi AWKPATH (Variabile)).
Se un nome-file contiene una ‘/’, non viene effettuata la ricerca del
percorso. Analogamente, se il
nome-file è "-"
, viene usato senza alcuna modifica. Altrimenti,
il nome-file è concatenato col nome di ogni directory nella lista dei
percorsi, e vien fatto un tentativo per aprire il nome-file così generato.
Il solo modo di controllare se un file è leggibile da awk
è di
andare avanti e tentare di leggerlo con
getline
; questo è quel che
percorso()
fa.85
Se il file può essere letto, viene chiuso e viene restituito il valore di
nome-file:
progr_che_espande=' function percorso(file, i, t, da_buttare) { if (index(file, "/") != 0) return file if (file == "-") return file for (i = 1; i <= n_dir; i++) { t = (lista_percorsi[i] "/" file)
if ((getline da_buttare < t) > 0) { # found it close(t) return t }
} return "" }
Il programma principale è contenuto all’interno di una regola BEGIN
.
La prima cosa che fa è di impostare il vettore lista_percorsi
usato
dalla funzione percorso()
. Dopo aver diviso la lista usando come
delimitatore ‘:’, gli elementi nulli sono sostituiti da "."
,
che rappresenta la directory corrente:
BEGIN { percorsi = ENVIRON["AWKPATH"] n_dir = split(percorsi, lista_percorsi, ":") for (i = 1; i <= n_dir; i++) { if (lista_percorsi[i] == "") lista_percorsi[i] = "." }
La pila è inizializzata con ARGV[1]
, che sarà "/dev/stdin"
.
Il ciclo principale viene subito dopo. Le righe in input sono lette una dopo
l’altra. Righe che non iniziano con @include
sono stampate così come
sono.
Se la riga inizia con @include
, il nome-file è in $2
.
La funzione percorso()
è chiamata per generare il percorso completo.
Se questo non riesce, il programma stampa un messaggio di errore e continua.
Subito dopo occorre controllare se il file sia già stato incluso. Il vettore
gia_fatto
è indicizzato dal nome completo di ogni nome-file incluso
e tiene traccia per noi di questa informazione. Se un file viene visto più
volte, viene stampato un messaggio di avvertimento. Altrimenti il nuovo
nome-file è aggiunto alla pila e l’elaborazione continua.
Infine, quando getline
giunge alla fine del file in input, il file
viene chiuso, e la pila viene elaborata. Quando indice_pila
è minore
di zero, il programma è terminato:
indice_pila = 0 input[indice_pila] = ARGV[1] # ARGV[1] è il primo file for (; indice_pila >= 0; indice_pila--) { while ((getline < input[indice_pila]) > 0) { if (tolower($1) != "@include") { print continue } cammino = percorso($2)
if (cammino == "") { printf("igawk: %s:%d: non riesco a trovare %s\n", input[indice_pila], FNR, $2) > "/dev/stderr" continue }
if (! (cammino in gia_fatto)) { gia_fatto[cammino] = input[indice_pila] input[++indice_pila] = cammino # aggiungilo alla pila } else print $2, "incluso in", input[indice_pila], "era già incluso in", gia_fatto[cammino] > "/dev/stderr" } close(input[indice_pila]) } }' # l'apice chiude la variabile `progr_che_espande' programma_elaborato=$(gawk -- "$progr_che_espande" /dev/stdin << EOF $programma EOF )
Il costrutto di shell ‘comando << marcatore’ è chiamato here document (documento sul posto). Ogni riga presente nello script di shell fino al marcatore è passato in input a comando. La shell elabora i contenuti dell’here document sostituendo, dove serve, variabili e comandi (ed eventualmente altre cose, a seconda della shell in uso).
Il costrutto di shell ‘$(…)’ è chiamato sostituzione di comando. L’output del comando posto all’interno delle parentesi è sostituito nella riga di comando. Poiché il risultato è usato in un assegnamento di variabile, viene salvato come un’unica stringa di caratteri, anche se il risultato contiene degli spazi bianchi.
Il programma espanso è salvato nella variabile programma_elaborato
.
Il tutto avviene secondo le fasi seguenti:
gawk
con il programma che gestisce le @include
(il valore della variabile di shell
progr_che_espande
) leggendo lo standard input.
programma
.
L’input è passato a gawk
tramite un here document.
programma_elaborato
usando la sostituzione di comando.
L’ultima fase è la chiamata a gawk
con il programma espanso,
insieme alle opzioni originali e agli argomenti della riga di comando che
l’utente aveva fornito:
eval gawk $opts -- '"$programma_elaborato"' '"$@"'
Il comando eval
è una struttura della shell che riesegue
l’elaborazione dei parametri della riga di comando. Gli apici proteggono le
parti restanti.
Questa versione di igawk
è la quinta versione di questo programma.
Ci sono quattro semplificazioni migliorative:
@include
anche per i file specificati tramite l’opzione
-f consente di semplificare di molto la preparazione del programma
iniziale awk
; tutta l’elaborazione delle istruzioni @include
può essere svolta in una sola volta.
getline
all’interno della
funzione percorso()
quando si controlla se il file è accessibile
per il successivo uso nel programma principale semplifica notevolmente
le cose.
getline
nella regola BEGIN
rende possibile
fare tutto in un solo posto. Non è necessario programmare un ulteriore ciclo
per elaborare le istruzioni @include
nidificate.
sh
, il che rende più difficile la comprensione a chi non
abbia familiarità con il comando
sh
.
Inoltre, questo programma dimostra come spesso valga la pena di utilizzare
insieme la programmazione della sh
e quella di awk
.
Solitamente, si può fare parecchio senza dover ricorrere alla programmazione
di basso livello in C o C++, ed è spesso più facile fare certi tipi di
manipolazioni di stringhe e argomenti usando la shell, piuttosto che
awk
.
Infine, igawk
dimostra che non è sempre necessario aggiungere nuove
funzionalità a un programma; queste possono spesso essere aggiunte in
cima.86
Successivo: Programma signature, Precedente: Programma igawk, Su: Programmi vari [Contenuti][Indice]
Un’interessante sfida per il programmatore è quella di cercare anagrammi in una lista di parole (come /usr/share/dict/italian presente in molti sistemi GNU/Linux). Una parola è un anagramma di un’altra se entrambe le parole contengono le stesse lettere (p.es., “branzino” e “bronzina”).
La Colonna 2, Problema C, della seconda edizione del libro di Jon Bentley Programming Pearls, presenta un algoritmo elegante. L’idea è di assegnare a parole che sono anagrammi l’una dell’altra una firma comune, e poi di ordinare tutte le parole in base alla loro firma e di stamparle. Il Dr. Bentley fa notare che prendere tutte le lettere di ogni parola ed elencarle in ordine alfabetico produce queste firme comuni.
Il programma seguente usa vettori di vettori per riunire parole con la stessa firma, e l’ordinamento di vettori per stampare le parole trovate in ordine alfabetico:
# anagram.awk --- Un'implementazione dell'algoritmo per trovare anagrammi # dalla seconda edizione # del libro di Jon Bentley "Programming Pearls". # Addison Wesley, 2000, ISBN 0-201-65788-0. # Colonna 2, Problema C, sezione 2.8, pp 18-20. /'s$/ { next } # Salta i genitivi sassoni
Il programma inizia con un’intestazione, e poi una regola per saltare i genitivi sassoni eventualmente contenuti nel file che contiene la lista di parole. La regola successiva costruisce la struttura dei dati. Il primo indice del vettore è rappresentato dalla firma; il secondo è la parola stessa:
{ chiave = da_parola_a_chiave($1) # costruisce la firma data[chiave][$1] = $1 # Immagazzina parola con questa firma }
La funzione da_parola_a_chiave()
crea la firma.
Divide la parola in lettere singole, mette in ordine alfabetico le lettere,
e poi le rimette ancora insieme:
# da_parola_a_chiave --- divide parole in lettere, ordina e riunisce function da_parola_a_chiave(parola, a, i, n, risultato) { n = split(parola, a, "") asort(a) for (i = 1; i <= n; i++) risultato = risultato a[i] return risultato }
Infine, la regola END
percorre tutto il vettore e stampa
le liste degli anagrammi. L’output è poi passato al
comando di sistema sort
perché altrimenti gli
anagrammi sarebbero elencati in ordine arbitrario:
END { sort = "sort" for (chiave in data) { # ordina parole con la stessa chiave n_parole = asorti(data[chiave], parole) if (n_parole == 1) continue # e stampa. Problema minore: uno spazio extra a fine di ogni riga for (j = 1; j <= n_parole; j++) printf("%s ", parole[j]) | sort print "" | sort } close(sort) }
Ecco una piccola parte dell’output quando il programma è eseguito:
$ gawk -f anagram.awk /usr/share/dict/italian | grep '^b' … baraste bastare serbata barasti basarti baratro tabarro barattoli ribaltato tribolata barbieri birberia barche brache barcollerei corbelleria bare erba bareremmo brameremo barili librai …
Precedente: Programma anagram, Su: Programmi vari [Contenuti][Indice]
Il programma seguente è stato scritto da Davide Brini
ed è pubblicato sul suo sito web.
Serve come sua firma nel gruppo Usenet comp.lang.awk
.
Questi sono i termini da lui stabiliti per il copyright:
Copyright © 2008 Davide Brini
Copying and distribution of the code published in this page, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved.
Ecco il programma:
awk 'BEGIN{O="~"~"~";o="=="=="==";o+=+o;x=O""O;while(X++<=x+o+o)c=c"%c"; printf c,(x-O)*(x-O),x*(x-o)-o,x*(x-O)+x-O-o,+x*(x-O)-x+o,X*(o*o+O)+x-O, X*(X-x)-o*o,(x+X)*o*o+o,x*(X-x)-O-O,x-O+(O+o+X+x)*(o+O),X*X-X*(x-O)-x+O, O+X*(o*(o+O)+O),+x+O+X*o,x*(x-o),(o+X+x)*o*o-(x-O-O),O+(X-x)*(X+O),x-O}'
Viene lasciato al lettore il piacere di stabilire cosa fa il programma. (Se si è sull’orlo della disperazione nel tentativo di comprensione, si veda la spiegazione di Chris Johansen, che è contenuta nel file sorgente Texinfo di questo Documento.)
Successivo: Esercizi sui programmi, Precedente: Programmi vari, Su: Programmi di esempio [Contenuti][Indice]
awk
direttamente eseguibili
ne rende più semplice l’uso. In alternativa, si può invocare un
programma usando ‘awk -f …’.
awk
è un esercizio
piacevole; il potere espressivo di awk
consente di scrivere tali
programmi usando relativamente poche righe di codice, nonostante i programmi
risultanti siano funzionalmente completi e utilizzabili.
awk
riguarda il
lavorare con singoli caratteri. La possibilità di usare split()
con
la stringa nulla come separatore può semplificare considerevolmente tale
compito.
Precedente: Sommario dei programmi, Su: Programmi di esempio [Contenuti][Indice]
split()
con ""
come separatore.
awk
che non prevedono IGNORECASE
usando tolower()
sulla riga e nei criteri di ricerca. In una nota a
piè di pagina è anche detto che questa soluzione ha un problema: in output
viene scritta la riga tradotta (a lettere minuscole), e non quella originale.
Risolvere questo problema.
id
accetta opzioni che controllano quali
informazioni stampare. Modificare la versione awk
(vedi Programma id) per accettare gli stessi argomenti e funzionare allo
stesso modo.
split.awk
(vedi Programma split) presuppone che le
lettere siano contigue nella codifica dei caratteri,
il che non è vero per sistemi che usano la codifica EBCDIC.
Risolvere questo problema.
(Suggerimento: Considerare un modo diverso di analizzare l’alfabeto,
senza appoggiarsi sulle funzioni ord()
e chr()
.)
FNR
nella funziona a_fine_file()
?
Suggerimento: Esaminare il codice
in
Funzione filetrans.
translate
(vedi Programma translate) è farraginosa usando le funzione standard
awk
. Poiché gawk
può dividere stringhe in caratteri
singoli usando come separatore ""
, come si potrebbe usare questa
funzionalità per semplificare il programma?
gawk
avesse a disposizione la funzione
gensub()
. Usarla per semplificare il codice.
BEGIN { stringa = ARGV[1] rimpiazzo = ARGV[2] ARGV[1] = ARGV[2] = "" } { gsub(stringa, rimpiazzo); print }
sed
?
getline
nella funzione percorso()
, mentre si
controlla l’accessibilità del file da usare nel programma principale,
semplifica notevolmente le cose. Quale problema è peraltro generato così
facendo?
Questo file contiene un insieme di funzioni di libreria di default, come
getopt()
e assert()
.
Questo file contiene funzioni di libreria che sono specifiche di
un sito o di un’installazione; cioè, funzioni sviluppate localmente.
Mantenere due file separati consente a default.awk di essere
modificato in seguito a nuove versioni di gawk
, senza che
l’amministratore di sistema debba ogni volta aggiornarlo aggiungendo le
funzioni locali.
Un utente
ha suggerito che gawk
venga modificato per leggere automaticamente
questi file alla partenza. Piuttosto, sarebbe molto semplice
modificare igawk
per farlo. Poiché igawk
è capace di
elaborare direttive @include
nidificate, default.awk potrebbe contenere semplicemente la lista di
direttive @include
con le funzioni di libreria desiderate.
Fare questa modifica.
sort
.
Successivo: Internazionalizzazione, Precedente: Programmi di esempio, Su: Top [Contenuti][Indice]
awk
con gawk
gawk
Scrivete la documentazione supponendo che chiunque la leggerà sia uno psicopatico violento, che conosce il vostro indirizzo di casa.
Questo capitolo tratta delle funzionalità avanzate in gawk
.
È un po’ come un “pacco sorpresa” di argomenti che non sono collegati tra di
loro in altro modo.
Per prima cosa, vediamo un’opzione da riga di comando che consente a
gawk
di riconoscere i numeri non-decimali nei dati in input, e non
soltanto nei programmi awk
.
Poi vengono illustrate delle funzionalità speciali di gawk
per
l’ordinamento di vettori. Quindi viene trattato dettagliatamente l’I/O
bidirezionale, di cui si è fatto cenno in precedenti parti di questo
Documento, assieme ai fondamenti sulle reti TCP/IP.
Infine, vediamo come gawk
può tracciare il profilo di un programma awk
, così che si
possa ritoccarlo per migliorarne le prestazioni.
Altre funzionalità avanzate vengono trattate separatamente dedicando un capitolo per ciascuna di esse:
awk
, in modo che parlino più lingue
nazionali.
gawk
per individuare errori nei programmi awk
.
gawk
per eseguire calcoli con precisione arbitraria.
gawk
.
• Dati non decimali: | Consentire dati di input non decimali. | |
• Ordinamento di vettori: | Modi per controllare la visita di un vettore e il suo ordinamento. | |
• I/O bidirezionale: | Comunicazione bidirezionale con un altro processo. | |
• Reti TCP/IP: | Usare gawk per programmazione di rete.
| |
• Profilare: | Profilare i propri programmi awk .
| |
• Sommario funzionalità avanzate: | Sommario delle funzionalità avanzate. |
Successivo: Ordinamento di vettori, Su: Funzionalità avanzate [Contenuti][Indice]
Se si esegue gawk
con l’opzione --non-decimal-data,
si possono avere valori in base diversa da dieci nei dati di input:
$ echo 0123 123 0x123 | > gawk --non-decimal-data '{ printf "%d, %d, %d\n", $1, $2, $3 }' -| 83, 123, 291
Affinché questa funzionalità sia disponibile, i programmi devono essere
scritti in modo che gawk
tratti i dati come valori numerici:
$ echo 0123 123 0x123 | gawk '{ print $1, $2, $3 }' -| 0123 123 0x123
L’istruzione print
tratta le sue espressioni come se fossero stringhe.
Sebbene i campi possano comportarsi come numeri, quando necessario,
essi rimangono sempre stringhe, per cui print
non cerca di elaborarli
come se fossero numeri. Si deve aggiungere zero a un campo affiché venga
considerato come un numero. Per esempio:
$ echo 0123 123 0x123 | gawk --non-decimal-data ' > { print $1, $2, $3 > print $1 + 0, $2 + 0, $3 + 0 }' -| 0123 123 0x123 -| 83 123 291
Poiché capita comunemente di avere dati di tipo decimale con degli zeri iniziali, e poiché l’uso di questa funzionalità può portare a risultati inattesi, il comportamento di default è quello lasciarla disabilitata. Se si vuole, la si deve richiedere esplicitamente.
ATTENZIONE: L’uso di questa opzione non è consigliata. Può provocare errori molto seri eseguendo vecchi programmi. Al suo posto è meglio usare la funzione
strtonum()
per convertire i dati (vedi Funzioni per stringhe). Questo rende i programmi più facili da scrivere e più facili da leggere, e porta a risultati meno inattesi.Quest’opzione potrebbe sparire dalle versioni future di
gawk
.
Successivo: I/O bidirezionale, Precedente: Dati non decimali, Su: Funzionalità avanzate [Contenuti][Indice]
gawk
permette di controllare l’ordine con cui un ciclo
‘for (indice in vettore)’
attraversa un vettore.
Inoltre, due funzioni predefinite, asort()
e asorti()
,
permettono di mettere in ordine i vettori sulla base, rispettivamente, dei
valori e degli indici del vettore. Queste due funzioni danno anche il
controllo sui criteri in base ai quali riordinare gli elementi del vettore.
• Controllare visita vettori: | Come usare PROCINFO["sorted_in"]. | |
• Funzioni di ordinamento di vettori: | Come usare asort() e asorti() .
|
Successivo: Funzioni di ordinamento di vettori, Su: Ordinamento di vettori [Contenuti][Indice]
Per default, l’ordine secondo il quale un ciclo ‘for (indice in
vettore)’ scorre un vettore non è definito; in genere si basa
sull’implementazione interna dei vettori all’interno di awk
.
Spesso, tuttavia, si vorrebbe poter eseguire un ciclo sugli elementi in un
determinato ordine scelto dall’utente programmatore. Con gawk
si può fare.
Controllare visita parla di come si possono assegnare valori speciali
predefiniti a PROCINFO["sorted_in"]
per controllare l’ordine secondo il
quale gawk
attraversa un vettore
durante un ciclo for
.
Inoltre, il valore di PROCINFO["sorted_in"]
può essere un nome di
funzione.88
Questo consente di scorrere un vettore sulla base di un qualsiasi criterio
personalizzato. Gli elementi del vettore vengono ordinati in accordo col valore
ritornato da questa funzione. La funzione che fa il confronto dovrebbe essere
definita con almeno quattro argomenti:
function confronta(i1, v1, i2, v2) { confronta gli elementi 1 e 2 in qualche modo return < 0; 0; o > 0 }
Qui, i1
e i2
sono gli indici, e v1
e v2
sono i corrispondenti valori dei due elementi che si stanno confrontando.
v1
oppure v2
, o entrambi, possono essere vettori se il vettore
che si sta visitando contiene sottovettori come valori.
(Vedi Vettori di vettori per maggiori informazioni sui sottovettori.)
I tre possibili valori di ritorno sono interpretati nel seguente modo:
confronta(i1, v1, i2, v2) < 0
L’indice i1
viene prima dell’indice i2
durante l’avanzamento
del ciclo.
confronta(i1, v1, i2, v2) == 0
Gli indici i1
e i2
sono equivalenti, ma l’ordine tra loro non è definito.
confronta(i1, v1, i2, v2) > 0
L’indice i1
viene dopo l’indice i2
durante l’avanzamento del
ciclo.
La prima funzione di confronto può essere usata per scorrere un vettore secondo l’ordine numerico degli indici:
function cfr_ind_num(i1, v1, i2, v2) { # confronto di indici numerici, ordine crescente return (i1 - i2) }
La seconda funzione scorre un vettore secondo l’ordine delle stringhe dei valori degli elementi piuttosto che secondo gli indici:
function cfr_val_str(i1, v1, i2, v2) { # confronto di valori di stringa, ordine crescente v1 = v1 "" v2 = v2 "" if (v1 < v2) return -1 return (v1 != v2) }
La terza funzione di confronto restituisce dapprima tutti i numeri, e dopo questi le stringhe numeriche senza spazi iniziali o finali, durante l’avanzamento del ciclo:
function cfr_val_num_str(i1, v1, i2, v2, n1, n2) { # confronto mettendo i numeri prima dei valori di stringa, # ordine crescente n1 = v1 + 0 n2 = v2 + 0 if (n1 == v1) return (n2 == v2) ? (n1 - n2) : -1 else if (n2 == v2) return 1 return (v1 < v2) ? -1 : (v1 != v2) }
Qui vediamo un programma principale che mostra come gawk
si comporta usando ciascuna delle funzioni precedenti:
BEGIN { data["uno"] = 10 data["due"] = 20 data[10] = "uno" data[100] = 100 data[20] = "due" f[1] = "cfr_ind_num" f[2] = "cfr_val_str" f[3] = "cfr_val_num_str" for (i = 1; i <= 3; i++) { printf("Funzione di ordinamento: %s\n", f[i]) PROCINFO["sorted_in"] = f[i] for (j in data) printf("\tdata[%s] = %s\n", j, data[j]) print "" } }
I risultati dell’esecuzione del programma sono questi:
$ gawk -f compdemo.awk -| Funzione di ordinamento: cfr_ind_num Ordinamento per indice numerico -| data[uno] = 10 -| data[due] = 20 Entrambe le stringhe sono numericamente zero -| data[10] = uno -| data[20] = due -| data[100] = 100 -| -| Funzione di ordinamento: cfr_val_str Ordinamento per valore degli -| elementi come stringhe -| data[uno] = 10 -| data[100] = 100 La stringa 100 è minore della stringa 20 -| data[due] = 20 -| data[20] = due -| data[10] = uno -| -| Funzione di ordinamento: cfr_val_num_str Ordinamento con tutti i -| valori numerici prima di tutte le stringhe -| data[uno] = 10 -| data[due] = 20 -| data[100] = 100 -| data[20] = due -| data[10] = uno
Si provi a ordinare gli elementi di un file delle password del sistema GNU/Linux in base al nome d’accesso dell’utente. Il seguente programma ordina i record secondo una specifica posizione del campo e può essere usato per questo scopo:
# passwd-sort.awk --- semplice programma per ordinare in base alla # posizione del campo # la posizione del campo è specificata dalla variabile globale POS function per_campo(i1, v1, i2, v2) { # confronto per valore, come stringa, e in ordine crescente return v1[POS] < v2[POS] ? -1 : (v1[POS] != v2[POS]) } { for (i = 1; i <= NF; i++) a[NR][i] = $i } END { PROCINFO["sorted_in"] = "per_campo" if (POS < 1 || POS > NF) POS = 1 for (i in a) { for (j = 1; j <= NF; j++) printf("%s%c", a[i][j], j < NF ? ":" : "") print "" } }
Il primo campo di ogni elemento del file delle password è il nome d’accesso dell’utente, e i campi sono separati tra loro da due punti. Ogni record definisce un sottovettore, con ogni campo come elemento nel sottovettore. L’esecuzione del programma produce il seguente output:
$ gawk -v POS=1 -F: -f sort.awk /etc/passwd -| adm:x:3:4:adm:/var/adm:/sbin/nologin -| apache:x:48:48:Apache:/var/www:/sbin/nologin -| avahi:x:70:70:Avahi daemon:/:/sbin/nologin …
Il confronto normalmente dovrebbe restituire sempre lo stesso valore quando vien dato come argomento un preciso paio di elementi del vettore. Se viene restituito un risultato non coerente, l’ordine è indefinito. Questo comportamento può essere sfruttato per introdurre un ordinamento casuale in dati apparentemente ordinati:
function ordina_a_caso(i1, v1, i2, v2) { # ordine casuale (attenzione: potrebbe non finire mai!) return (2 - 4 * rand()) }
Come già accennato, l’ordine degli indici è arbitrario se due elementi risultano uguali. Normalmente questo non è un problema, ma lasciare che elementi di uguale valore compaiano in ordine arbitrario può essere un problema, specialmente quando si confrontano valori di elementi di un elenco. L’ordine parziale di elementi uguali può cambiare quando il vettore viene visitato di nuovo, se nel vettore vengono aggiunti o rimossi elementi. Un modo per superare l’ostacolo quando si confrontano elementi con valori uguali è quello di includere gli indici nelle regole di confronto. Si noti che questo potrebbe rendere meno efficiente l’attraversamento del ciclo, per cui si consiglia di farlo solo se necessario. Le seguenti funzioni di confronto impongono un ordine deterministico, e si basano sul fatto che gli indici (di stringa) di due elementi non sono mai uguali:
function per_numero(i1, v1, i2, v2) { # confronto di valori numerici (e indici), ordine decrescente return (v1 != v2) ? (v2 - v1) : (i2 - i1) } function per_stringa(i1, v1, i2, v2) { # confronto di valori di stringa (e indici), ordine decrescente v1 = v1 i1 v2 = v2 i2 return (v1 > v2) ? -1 : (v1 != v2) }
Una funzione di confronto personalizzata spesso può semplificare l’attraversamento del ciclo ordinato, e il solo limite è il cielo, quando si va a progettare una funzione di questo tipo.
Quando i confronti tra stringhe son fatti durante un’operazione di ordinamento,
per valori di elementi che, uno o entrambi, non sono numeri, o per
indici di elementi gestiti come stringhe, il valore di IGNORECASE
(vedi Variabili predefinite) controlla se
i confronti trattano corrispondenti lettere maiuscole e minuscole
come equivalenti o come distinte.
Un’altra cosa da tenere a mente è che nel caso di sottovettori, i valori degli
elementi possono essere a loro volta dei vettori; una funzione di confronto in
produzione dovrebbe usare la funzione isarray()
(vedi Funzioni per i tipi)
per controllare ciò, e scegliere un ordinamento preciso per i sottovettori.
Tutti gli ordinamenti basati su PROCINFO["sorted_in"]
sono disabilitati in modalità POSIX,
perché il vettore PROCINFO
in questo caso non è speciale.
Come nota a margine, si è visto che ordinare gli indici del vettore prima di
scorrere il vettore porta a un incremento variabile dal 15% al 20% del tempo di
esecuzione dei programmi awk
. Per questo motivo l’attraversamento
ordinato di vettori non è il default.
Precedente: Controllare visita vettori, Su: Ordinamento di vettori [Contenuti][Indice]
gawk
Nella maggior parte delle implementazioni di awk
, ordinare un vettore
richiede una funzione sort()
. Questo può essere istruttivo per provare
diversi algoritmi di ordinamento, ma normalmente non è questo lo scopo del
programma. In gawk
ci sono le funzioni predefinite asort()
e
asorti()
(vedi Funzioni per stringhe) per i vettori ordinati.
Per esempio:
riempire il vettore dati n = asort(dati) for (i = 1; i <= n; i++) fare qualcosa con dati[i]
Dopo la chiamata ad asort()
, il vettore dati
è indicizzato da 1
a n, il numero totale di elementi in dati
.
(Questo conteggio è il valore di ritorno di asort()
).
dati[1]
<= dati[2]
<= dati[3]
, e così
via. Il confronto di default è basato sul tipo di elementi
(vedi Tipi di variabile e confronti).
Tutti i valori numerici vengono prima dei valori di stringa,
che a loro volta vengono prima di tutti i sottovettori.
Un effetto collaterale rilevante nel chiamare asort()
è che
gli indici originali del vettore vengono persi irreparabilmente.
Poiché questo non sempre è opportuno, asort()
accetta un
secondo argomento:
populate the array orig n = asort(orig, dest) for (i = 1; i <= n; i++) fai qualcosaa con dest[i]
In questo caso, gawk
copia il vettore orig
nel vettore
dest
e ordina dest
, distruggendo i suoi indici.
Tuttavia il vettore orig
non viene modificato.
Spesso, ciò di cui si ha bisogno è di ordinare per i valori degli
indici invece che per i valori degli elementi. Per far questo si usa la
funzione asorti()
. L’interfaccia e il comportamento sono identici a
quelli di asort()
, solo che per l’ordinamento vengono usati i valori
degli indici, che diventano i valori del vettore risultato:
{ orig[$0] = una_funz($0) } END { n = asorti(orig, dest) for (i = 1; i <= n; i++) { Lavora direttamente con gli indici ordinati: fa qualcosa con dest[i] … Accede al vettore originale attraverso gli indici ordinati: fa qualcosa con orig[dest[i]] } }
Fin qui, tutto bene. Ora inizia la parte interessante. Sia asort()
che asorti()
accettano un terzo argomento di stringa per controllare il
confronto di elementi del vettore. Quando abbiamo introdotto asort()
e
asorti()
nella Funzioni per stringhe, abbiamo ignorato questo terzo
argomento; comunque, è giunto il momento di descrivere come questo argomento
influenza queste due funzioni.
Fondamentalmente, il terzo argomento specifica come dev’essere ordinato il
vettore. Ci sono due possibilità. Come per PROCINFO["sorted_in"]
,
quest’argomento può essere uno degli argomenti predefiniti che gawk
fornisce (vedi Controllare visita), o può essere il nome di una funzione
definita dall’utente (vedi Controllare visita vettori).
Nell’ultimo caso, la funzione può confrontare gli elementi in qualunque modo si voglia, prendendo in considerazione solo gli indici, solo i valori, o entrambi. Questo è estremamente potente.
Una volta che il vettore è ordinato, asort()
prende i valori nel
loro ordine finale e li usa per riempire il vettore risultato, mentre
asorti()
prende gli indici nel loro ordine finale e li usa per
riempire il vettore risultato.
NOTA: Copiare indici ed elementi non è dispendioso in termini di memoria. Internamente,
gawk
mantiene un conteggio dei riferimenti ai dati. Per esempio, dopo cheasort()
copia il primo vettore nel secondo, in memoria c’è ancora una sola copia dei dati degli elementi del vettore originale, ed entrambi i vettori accedono all’unica copia di valori che esiste in memoria.
Poiché IGNORECASE
influenza i confronti tra stringhe, il valore di
IGNORECASE
influisce anche sull’ordinamento sia con asort()
che
con asorti()
.
Si noti inoltre che l’ordinamento della localizzazione non entra in
gioco; i confronti sono basati solamente sul valore dei
caratteri.89
L’esempio seguente mostra l’uso di una funzione di confronto usata con
asort()
. La funzione di confronto, confronta_in_minuscolo()
,
trasforma gli elementi da confrontare in lettere minuscole, in modo da avere
confronti che non dipendono da maiuscolo/minuscolo.
# confronta_in_minuscolo --- confronta stringhe ignorando maiuscolo/minuscolo function confronta_in_minuscolo(i1, v1, i2, v2, l, r) { l = tolower(v1) r = tolower(v2) if (l < r) return -1 else if (l == r) return 0 else return 1 }
E questo programma può essere usato per provarla:
# programma di test BEGIN { Letters = "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" split(Letters, data, "") asort(data, risultato, "confronta_in_minuscolo") j = length(risultato) # numero elementi del vettore "risultato" for (i = 1; i <= j; i++) { printf("%s", risultato[i]) if (i % (j/2) == 0) # a metà, la stampa del vettore va a capo printf("\n") else printf(" ") } }
Se si esegue il programma, si ottiene:
$ gawk -f confronta_in_minuscolo.awk -| A a B b c C D d e E F f g G H h i I J j k K l L M m -| n N O o p P Q q r R S s t T u U V v w W X x y Y z Z
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.90
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
):91
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 Chiusura file e pipe).
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 Continuazione dopo errori 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 Variabili auto-assegnate),
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.
Successivo: Profilare, Precedente: I/O bidirezionale, Su: Funzionalità avanzate [Contenuti][Indice]
gawk
per la programmazione di rete
EMRED
:
A host is a host from coast to coast,
and nobody talks to a host that’s close,
unless the host that isn’t close
is busy, hung, or dead.
EMRED
:
Un computer è un computer lontano o vicino,
e nessuno parla con un computer vicino,
a meno che il computer lontano
sia occupato, fuori linea, o spento.
Oltre a poter aprire una pipeline bidirezionale verso un coprocesso sullo stesso sistema (vedi I/O bidirezionale), è possibile attivare una connessione bidirezionale verso un altro processo o verso un altro sistema attraverso una connessione di rete IP.
Si può pensare a questo semplicemente come a una pipeline bidirezionale
molto lunga verso un coprocesso.
Il modo in cui gawk
stabilisce quando usare una rete TCP/IP è
mediante il riconoscimento di speciali nomi-file che iniziano con
‘/inet/’, con ‘/inet4/’ o con ‘/inet6/’.
La sintassi completa del nome-file speciale è /tipo-rete/protocollo/porta-locale/host-remoto/porta-remota. I componenti sono:
Specifica il tipo di connessione a internet da stabilire. Si usa ‘/inet4/’ per usare solo IPv4, e ‘/inet6/’ per usare solo IPv6. Il semplice ‘/inet/’ (che usualmente è l’unica opzione) usa il tipo di default del sistema, quasi certamente IPv4.
Il protocollo da usare sull’IP. Questo dev’essere o ‘tcp’ o ‘udp’, per una connessione IP rispettivamente TCP o UDP. TCP dovrebbe venir usato per la maggior parte delle applicazioni.
Il numero di porta TCP o UDP da usare. Si usa un numero di porta di valore
‘0’ quando si vuole che sia il sistema a scegliere una porta.
Questo è quel che si dovrebbe fare quando si scrive un’applicazione
cliente TCP o UDP.
Si può usare anche un nome di un servizio noto, come ‘smtp’
o ‘http’, nel qual caso gawk
tenta di determinare
il numero di porta predefinito usando la funzione C getaddrinfo()
.
L’indirizzo IP o il nome di dominio completamente qualificato dell’host internet al quale ci si vuol connettere.
Il numero di porta TCP o UDP da usare sul particolare host remoto. Anche in questo caso, si usi ‘0’ se non ci sono preferenze, o alternativamente, un nome di servizio comunemente noto.
NOTA: Un insuccesso nell’apertura di un socket bidirezionale darà luogo alla segnalazione di un errore non fatale al codice chiamante. Il valore di
ERRNO
indica l’errore (vedi Variabili auto-assegnate).
Si consideri il seguente esempio molto semplice:
BEGIN { Servizio = "/inet/tcp/0/localhost/daytime" Servizio |& getline print $0 close(Servizio) }
Questo programma legge la data e l’ora corrente dal server daytime
TCP del sistema locale.
Stampa poi il risultato e chiude la connessione.
Poiché questo tema è molto ampio, l’uso di gawk
per
la programmazione TCP/IP viene documentato separatamente.
Si veda
TCP/IP Internetworking with gawk
,
che fa parte della distribuzione gawk
,
per una introduzione e trattazione molto più completa e con molti
esempi.
NOTA:
gawk
può aprire solo socket diretti. Al momento non c’è alcun modo per accedere ai servizi disponibili su Secure Socket Layer (SSL); questo comprende qualsiasi servizio web il cui URL inizia con ‘https://’.
Successivo: Sommario funzionalità avanzate, Precedente: Reti TCP/IP, Su: Funzionalità avanzate [Contenuti][Indice]
awk
È possibile tener traccia dell’esecuzione dei propri programmi awk
.
Ciò si può fare passando l’opzione --profile a gawk
.
Al termine dell’esecuzione, gawk
crea un profilo del programma in un
file chiamato awkprof.out. A causa dell’attività di profilazione
l’esecuzione del programma è più lenta fino al 45% rispetto al normale.
Come mostrato nel seguente esempio,
l’opzione --profile può essere usata per cambiare il nome del file
su cui gawk
scriverà il profilo:
gawk --profile=mioprog.prof -f mioprog.awk dati1 dati2
Nell’esempio precedente, gawk
mette il profilo in
mioprog.prof anziché in awkprof.out.
Vediamo ora una sessione d’esempio che mostra un semplice programma
awk
, i suoi dati in input, e il risultato dell’esecuzione di
gawk
con l’opzione --profile. Innanzitutto, il
programma awk
:
BEGIN { print "Prima regola BEGIN" } END { print "Prima regola END" } /pippo/ { print "trovato /pippo/, perbacco" for (i = 1; i <= 3; i++) sing() } { if (/pippo/) print "l'if è vero" else print "l'else è vero" } BEGIN { print "Seconda regola BEGIN" } END { print "Seconda regola END" } function sing( ignora) { print "Devo essere io!" }
Questi sono i dati in input:
pippo pluto paperino pippo cianfrusaglie
E questo è il file awkprof.out che è il risultato dell’esecuzione
del profilatore di gawk
su questo programma e sui dati (quest’esempio
dimostra anche che i programmatori di awk
a volte si alzano molto
presto al mattino per lavorare):
# profilo gawk, creato Mon Sep 29 05:16:21 2014 # BEGIN regola(e) BEGIN { 1 print "Prima regola BEGIN" } BEGIN { 1 print "Seconda regola BEGIN" } # Regola(e) 5 /pippo/ { # 2 2 print "trovato /pippo/, perbacco" 6 for (i = 1; i <= 3; i++) { 6 sing() } } 5 { 5 if (/pippo/) { # 2 2 print "l'if è vero" 3 } else { 3 print "l'else è vero" } } # END regola(e) END { 1 print "Prima regola END" } END { 1 print "Seconda regola END" } # Funzioni, in ordine alfabetico 6 function sing(ignora) { 6 print "Devo essere io!" }
Quest’esempio illustra molte caratteristiche fondamentali dell’output della profilazione. Queste sono:
BEGIN
,
regole BEGINFILE
,
regole criterio di ricerca–azione,
regole ENDFILE
, regole END
, e funzioni, elencate
in ordine alfabetico.
Le regole BEGIN
ed END
multiple conservano le loro
distinte identità, così come le regole BEGINFILE
ed ENDFILE
multiple.
if
-else
mostra quante volte
la condizione è stata testata.
Alla destra della parentesi graffa sinistra aperta per il corpo di if
c’è un conteggio che mostra quante volte la condizione è stata trovata vera.
Il conteggio per else
indica
quante volte la verifica non ha avuto successo.
for
o while
) mostra quante volte il test del ciclo è stato eseguito.
(Per questo motivo, non si può solamente guardare il conteggio sulla prima
istruzione in una regola per determinare quante volte la regola è stata
eseguita. Se la prima istruzione è un ciclo, il conteggio è ingannevole.)
function
indica quante volte la funzione è stata chiamata.
I conteggi vicino alle istruzioni nel corpo mostrano quante volte
quelle istruzioni sono state eseguite.
if
, di un else
o di un ciclo è formato da un’unica istruzione.
print
e printf
solo quando l’istruzione
print
o printf
è seguita da una ridirezione.
Similarmente, se
l’oggetto di una ridirezione non è uno scalare, viene messo tra parentesi.
gawk
mette dei commenti iniziali
davanti alle regole BEGIN
ed END
,
alle regole BEGINFILE
ed ENDFILE
,
alle regole criterio_di_ricerca–azione e alle funzioni.
La versione profilata del proprio programma potrebbe non apparire esattamente
come quella scritta durante la stesura del programma. Questo perché
gawk
crea la versione profilata facendo una “stampa elegante” della
sua rappresentazione interna del programma. Un vantaggio di ciò è che
gawk
può produrre una rappresentazione standard.
Inoltre, cose come:
/pippo/
appaiono come:
/pippo/ { print $0 }
che è corretto, ma probabilmente inatteso.
Oltre a creare profili una volta completato il programma,
gawk
può generare un profilo mentre è in esecuzione.
Questo è utile se il proprio programma awk
entra in un ciclo
infinito e si vuol vedere cosa è stato eseguito.
Per usare questa funzionalità, bisogna eseguire gawk
con l’opzione
--profile in background:
$ gawk --profile -f mioprog & [1] 13992
La shell stampa un numero di job e il numero di ID del relativo processo;
in questo caso, 13992. Si usi il comando kill
per inviare il
segnale USR1
a gawk
:
$ kill -USR1 13992
Come al solito, la versione profilata del programma è scritta nel file awkprof.out, o in un file differente se ne viene specificato uno con l’opzione --profile.
Assieme al profilo regolare, come mostrato in precedenza, il file del profilo include una traccia di ogni funzione attiva:
# `Stack' (Pila) Chiamate Funzione: # 3. paperino # 2. pluto # 1. pippo # -- main --
Si può inviare a gawk
il segnale USR1
quante volte si vuole.
Ogni volta, il profilo e la traccia della chiamata alla funzione vengono
aggiunte in fondo al file di profilo creato.
Se si usa il segnale HUP
invece del segnale USR1
, gawk
genera il profilo e la traccia della chiamata alla funzione ed esce.
Quando gawk
viene eseguito sui sistemi MS-Windows, usa i segnali
INT
e QUIT
per generare il profilo, e nel
caso del segnale INT
, gawk
esce. Questo perché
questi sistemi non prevedono il comando kill
, per cui gli unici
segnali che si possono trasmettere a un programma sono quelli generati dalla
tastiera. Il segnale INT
è generato dalle combinazioni di tasti
Ctrl-c o Ctrl-BREAK, mentre il segnale
QUIT
è generato dalla combinazione di tasti Ctrl-\.
Infine, gawk
accetta anche un’altra opzione, --pretty-print.
Quando viene chiamato in questo modo, gawk
fa una “stampa elegante”
del programma nel file awkprof.out, senza conteggi sull’esecuzione.
NOTA: Una volta, l’opzione --pretty-print eseguiva anche il programma. Ora non più.
C’è una differenza significativa tra l’output creato durante la profilazione, e quello creato durante la stampa elegante. L’output della stampa elegante preserva i commenti originali che erano nel programma, anche se la loro posizione può non corrispondere esattamente alle posizioni originali che avevano nel codice sorgente.92
Comunque, per una precisa scelta progettuale, l’output della profilazione omette i commenti del programma originale. Questo permette di concentrarsi sui dati del conteggio di esecuzione ed evita la tentazione di usare il profilatore per creare una stampa elegante.
Oltre a ciò, l’output stampato in modo elegante non ha l’indentazione iniziale che ha l’output della profilazione. Questo rende agevole la stampa elegante del proprio codice una volta completato lo sviluppo, usando poi il risultato come versione finale del programma.
Poiché la rappresentazione interna del programma è formattata per
essere aderente al programma awk
in questione, la profilatura
e la formattazione graziosa (opzione --pretty-print) disabilitano
automaticamente le optimizzazioni di default di gawk
.
La formattazione elegante mantiene anche il formato originale delle costanti numeriche; se sono stati usati dei valori ottali o esadecimali nel codice sorgente, questi compariranno nell’output nello stesso formato con cui sono stati inseriti.
Precedente: Profilare, Su: Funzionalità avanzate [Contenuti][Indice]
gawk
tratti
i dati di input che hanno l’aspetto ottale ed esadecimale come valori ottali ed
esadecimali. L’opzione dovrebbe essere usata con prudenza o non usata affatto;
è preferibile l’uso di strtonum()
.
Si noti che quest’opzione potrebbe sparire nelle prossime versioni di
gawk
.
PROCINFO["sorted_in"]
al nome di una funzione definita dall’utente che
fa il confronto tra elementi del vettore basandosi su indice e valore.
asort()
o di asorti()
per
controllare come queste funzioni ordinano i vettori. O si può fornire una delle
stringhe di controllo predefinite che funzionano per
PROCINFO["sorted_in"]
.
getline
, ci si
scrive sopra con print
o con printf
. Usare close()
per bloccare il coprocesso completamente o, se necessario, chiudere le
comunicazioni bidirezionali in una direzione.
gawk
supporta sia IPv4 che IPv6.
USR1
durante la
profilazione gawk
scrive il profilo, includendo la
stack della chiamata alla funzione e prosegue nell’elaborazione.
Successivo: Debugger, Precedente: Funzionalità avanzate, Su: Top [Contenuti][Indice]
gawk
Tanto tempo fa i produttori di computer scrivevano software che comunicava solo in inglese. Col passare del tempo, i venditori di hardware e di software si sono resi conto che se i loro sistemi avessero comunicato anche nelle lingue materne di paesi dove non si parlava inglese, ciò avrebbe avuto come risultato un incremento delle vendite. Per questo motivo, l’internazionalizzazione e la localizzazione di programmi e sistemi software è divenuta una pratica comune.
Per molti anni la possibilità di fornire l’internazionalizzazione
era sostanzialmente limitata ai programmi scritti in C e C++.
Questo capitolo descrive la libreria ad hoc utilizzata da
gawk
per l’internazionalizzazione e anche il modo in cui le funzionalità che
consentono l’internazionalizzazione sono rese disponibili da gawk
a ogni programma scritto in awk
.
La disponibilità dell’internazionalizzazione a livello di programma
awk
offre ulteriore flessibilità agli sviluppatori di software:
non sono più obbligati a scrivere in C o C++ quando l’internazionalizzazione
è necessaria in un programma.
• I18N e L10N: | Internazionalizzazione e localizzazione. | |
• Utilizzare gettext : | Come funziona il comando GNU gettext .
| |
• I18N per programmatore: | Funzionalità per il programmatore. | |
• I18N per traduttore: | Funzionalità per il traduttore. | |
• Esempio I18N: | Un semplice esempio di internazionalizzazione. | |
• Gawk internazionalizzato: | Anche gawk è internazionalizzato.
| |
• Sommario I18N: | Sommario dell’internazionalizzazione. |
Successivo: Utilizzare gettext
, Su: Internazionalizzazione [Contenuti][Indice]
Internazionalizzazione significa scrivere (o modificare) un programma una volta sola, in maniera tale che possa usare più di una lingua senza bisogno di ulteriori modifiche al file sorgente. Localizzazione significa fornire i dati necessari perché un programma internazionalizzato sia in grado di funzionare con una data lingua. Questi termini si riferiscono comunemente a funzionalità quali la lingua usata per stampare messaggi di errore, quella usata per leggere risposte, e alle informazioni relative al modo di leggere e di stampare dati di tipo numerico o valutario.
Successivo: I18N per programmatore, Precedente: I18N e L10N, Su: Internazionalizzazione [Contenuti][Indice]
gettext
gawk
usa il comando GNU gettext
per rendere disponibili
le proprie funzionalità di internazionalizzazione.
L’attenzione del comando GNU gettext
è rivolta principalmente
ai messaggi: stringhe di caratteri stampate da un programma, sia
direttamente sia usando la formattazione prevista dalle istruzioni
printf
o sprintf()
.93
Quando si usa il comando GNU gettext
, ogni applicazione ha il
proprio dominio di testo. Questo è un nome unico come,
p.es., ‘kpilot’ o ‘gawk’,
che identifica l’applicazione.
Un’applicazione completa può avere più componenti: programmi scritti
in C o C++, come pure script di sh
o di awk
.
Tutti i componenti usano lo stesso dominio di testo.
Per andare sul concreto, si supponga di scrivere un’applicazione
chiamata guide
. L’internazionalizzazione per quest’applicazione
può essere implementata seguendo nell’ordine i passi qui delineati:
guide
e prende nota di ogni stringa che potrebbe aver bisogno
di traduzione.
Per esempio, "`-F': option required"
è una stringa che sicuramente
necessita di una traduzione.
Una tabella che contenga stringhe che sono nomi di opzioni non
necessita di traduzione.
(P.es., l’opzione di gawk
--profile
dovrebbe restare immutata, a prescindere dalla lingua locale).
"guide"
) alla libreria gettext
,
chiamando la funzione textdomain()
.
.po
)
e le traduzioni sono effettuate su quel file,
che viene distribuito con l’applicazione.
Per esempio, potrebbe esserci un file it.po per la traduzione italiana.
guide
è compilato e installato, i file binari contenenti le
traduzioni sono installati in una directory standard.
gettext
di usare un file .gmo in una directory diversa da quella standard,
usando la funzione bindtextdomain()
.
awk
guide
cerca ogni
stringa da tradurre facendo una chiamata a gettext()
. La stringa
ricevuta in ritorno è la stringa tradotta, se è stata trovata, o la stringa
originale, se una traduzione non è disponibile.
In C (o C++), la marcatura della stringa la ricerca dinamica
della traduzione si fanno inserendo ogni stringa da tradurre in una chiamata
a gettext()
:
printf("%s", gettext("Don't Panic!\n"));
Gli strumenti software che estraggono messaggi dal codice sorgente
individuano tutte le stringhe racchiuse nelle chiamate a gettext()
.
Gli sviluppatori del comando GNU gettext
, riconoscendo che
continuare a immettere ‘gettext(…)’ è sia faticoso che poco
elegante da vedere, usano la macro ‘_’ (un trattino basso) per
facilitare la cosa:
/* Nel file di intestazione standard: */ #define _(str) gettext(str) /* Nel testo del programma: */ printf("%s", _("Don't Panic!\n"));
Questo permette di ridurre la digitazione extra a solo tre caratteri per ogni stringa da tradurre e inoltre migliora di molto la leggibità.
Ci sono categorie di localizzazione per tipi diversi di informazioni
legate a una particolare localizzazione.
Le categorie di localizzazione note a gettext
sono:
LC_MESSAGES
Testo dei messaggi. Questa è la categoria di default usata all’interno di
gettext
, ma è possibile specificarne esplicitamente una differente,
se necessario. (Questo non è quasi mai necessario.)
LC_COLLATE
Informazioni sull’ordinamento alfabetico (cioè, come caratteri diversi e/o gruppi di carattere sono ordinati in un dato linguaggio).
LC_CTYPE
Informazioni sui singoli caratteri (alfabetico, numerico, maiuscolo
o minuscolo, etc.), come pure sulla codifica dei caratteri.
Quest’informazione è utilizzata per stabilire le classi di caratteri come
definite nello standard POSIX, nelle espressioni regolari,
come p. es. /[[:alnum:]]/
(vedi Espressioni tra parentesi quadre).
LC_MONETARY
Le informazioni di tipo monetario, quali il simbolo della moneta, e se il simbolo va prima o dopo il valore numerico.
LC_NUMERIC
Informazioni di tipo numerico, quali il carattere da usare per separare le cifre decimali e quello per separare le migliaia.94
LC_TIME
Informazioni relative alle date e alle ore, come l’uso di ore nel formato a 12 ore oppure a 24 ore, il mese stampato prima o dopo il giorno in una data, le abbreviazioni dei mesi nella lingua locale, e così via.
LC_ALL
Tutte le categorie viste sopra. (Non molto utile nel contesto del comando
gettext
.)
NOTA: Come descritto in Localizzazioni, le variabili d’ambiente che hanno lo stesso nome delle categorie di localizzazione (
LC_CTYPE
,LC_ALL
, etc.) influenzano il comportamento digawk
(e quello di altri programmi di utilità).Solitamente, queste variabili influenzano anche il modo con cui la libreria
gettext
trova le traduzioni. Tuttavia, la variabile d’ambienteLANGUAGE
prevale sulle variabili della famigliaLC_xxx
. Molti sistemi GNU/Linux possono aver definito questa variabile senza esplicitamente notificarlo all’utente, e questo potrebbe far sì chegawk
non riesca a trovare le traduzioni corrette. Se si incontra questa situazione, occorre controllare se la variabile d’ambienteLANGUAGE
è definita, e, in questo caso, va usato il comandounset
per rimuoverla.
Per il test di traduzioni dei messaggi inviati da gawk
stesso, si può
impostare la variabile d’ambiente GAWK_LOCALE_DIR
. Si veda la
documentazione per la funzione C bindtextdomain()
, e si veda anche
Altre variabili d'ambiente.
Successivo: I18N per traduttore, Precedente: Utilizzare gettext
, Su: Internazionalizzazione [Contenuti][Indice]
awk
gawk
prevede le seguenti variabili per l’internazionalizzazione:
TEXTDOMAIN
Questa variabile indica il dominio di testo dell’applicazione.
Per compatibilità con il comando GNU gettext
, il valore di default
è "messages"
.
_"questo è un messaggio da tradurre"
Costanti di tipo stringa marcate con un trattino basso iniziale
sono candidate per essere tradotte al momento dell’esecuzione del
programma gawk
.
Costanti di tipo stringa non precedute da un trattino basso non
verranno tradotte.
gawk
fornisce le seguenti funzioni al servizio
dell’internazionalizzazione:
dcgettext(string
[,
dominio [,
categoria]])
Restituisce la traduzione di stringa nel
dominio di testo dominio per la categoria di localizzazione categoria.
Il valore di default per dominio è il valore corrente di TEXTDOMAIN
.
Il valore di default per categoria è "LC_MESSAGES"
.
Se si assegna un valore a categoria, dev’essere una stringa uguale a
una delle categorie di localizzazione note, descritte
nella precedente sezione.
Si deve anche specificare un dominio di testo. Si usi TEXTDOMAIN
se
si desidera usare il dominio corrente.
ATTENZIONE: L’ordine degli argomenti per la versione
awk
della funzionedcgettext()
è differente, per una scelta di progetto, dall’ordine degli argomenti passati alla funzione C che ha lo stesso nome. L’ordine della versioneawk
è stato scelto per amore di semplicità e per consentire di avere dei valori di default per gli argomenti che fossero il più possibile simili, come stile, a quello diawk
.
dcngettext(stringa1, stringa2, numero
[,
dominio [,
categoria]])
Restituisce la forma, singolare o plurale, da usare a seconda del valore
di numero per la
traduzione di stringa1 e stringa2 nel dominio di testo
dominio per la categoria di localizzazione categoria.
stringa1 è la variante al singolare in inglese di un messaggio,
e stringa2 è la variante al plurale in inglese dello stesso messaggio.
Il valore di default per dominio è il valore corrente di TEXTDOMAIN
.
Il valore di default per categoria è "LC_MESSAGES"
.
Valgono le stesse osservazioni riguardo all’ordine degli argomenti
fatte a proposito della funzione dcgettext()
.
bindtextdomain(directory
[,
dominio ])
Cambia la directory nella quale
gettext
va a cercare i file .gmo, per il caso in cui questi
non possano risiedere nelle posizioni standard
(p.es., in fase di test).
Restituisce la directory alla quale dominio è “collegato”.
Il dominio di default è il valore di TEXTDOMAIN
.
Se l’argomento directory è impostato alla stringa nulla (""
),
bindtextdomain()
restituisce il collegamento corrente applicabile
al dominio specificato.
Per usare queste funzionalità in un programma awk
,
va seguita la procedura qui indicata:
TEXTDOMAIN
al dominio di testo del
programma. È meglio fare ciò all’interno di una regola BEGIN
(vedi BEGIN/END),
ma si può anche fare dalla riga di comando, usando l’opzione -v
(vedi Opzioni):
BEGIN { TEXTDOMAIN = "guide" … }
print _"hello, world" x = _"you goofed" printf(_"Number of users is %d\n", nusers)
dcgettext()
:95
if (assonnato) messaggio = dcgettext("%d clienti mi scocciano\n", "adminprog") else messaggio = dcgettext("mi diverto con %d clienti\n", "adminprog") printf(messaggio, numero_clienti)
In questo esempio, la chiamata a dcgettext()
specifica un diverso
dominio di testo ("adminprog"
) in cui trovare il
messaggio, ma usa la categoria di default "LC_MESSAGES"
.
Il precedente esempio funziona solo se numero_clienti
è un numero maggiore
di uno.
Per questo esempio sarebbe più appropriato usare la funzione dcngettext()
:
if (assonnato) messaggio = dcngettext("%d cliente mi scoccia\n", "%d clienti mi scocciano\n", numero_clienti, "adminprog") else messaggio = dcngettext("mi diverto con %d cliente\n", "mi diverto con %d clienti\n", numero_clienti, "adminprog") printf(messaggio, numero_clienti)
bindtextdomain()
:
BEGIN { TEXTDOMAIN = "guide" # dominio di testo regolare if (Testing) { # dove trovare il file in prova bindtextdomain("testdir") # joe si occupa del programma adminprog bindtextdomain("../joe/testdir", "adminprog") } … }
Vedi Esempio I18N
per un programma di esempio che illustra i passi da seguire per creare
e usare traduzioni nei programmi awk
.
Successivo: Esempio I18N, Precedente: I18N per programmatore, Su: Internazionalizzazione [Contenuti][Indice]
awk
Dopo aver marcato le stringhe che si desidera tradurre in un programma,
queste vanno estratte per creare il file iniziale .pot.
Durante la traduzione, è spesso utile modificare l’ordine nel quale
gli argomenti passati a printf
vengono stampati.
L’opzione da riga di comando --gen-pot di gawk
serve a
estrarre i messaggi, ed è esposta qui di seguito.
Dopo di che, verrà illustrata la possibilità di modificare l’ordine
in cui l’istruzione printf
stampa gli argomenti che le sono passati
in fase di esecuzione.
• Estrazione di stringhe: | Estrarre stringhe marcate. | |
• Ordinamento di printf: | Riordinare argomenti printf
| |
• Portabilità nell'I18N: | Problemi di portabilità a livello di awk .
|
Successivo: Ordinamento di printf, Su: I18N per traduttore [Contenuti][Indice]
Una volta che il programma awk
funziona, e tutte le stringhe
sono state marcate ed è stato impostato (e forse fissato) il dominio di
testo, è ora di preparare le traduzioni.
Per prima cosa, usando l’opzione di riga di comando --gen-pot,
si crea il file iniziale .pot:
gawk --gen-pot -f guide.awk > guide.pot
Quando viene chiamato specificando --gen-pot, gawk
non
esegue il programma. Il programma viene esaminato come al solito, e tutte
le stringhe che sono state marcate per essere tradotte vengono scritte nello
standard output, nel formato di un file Portable Object di GNU
gettext
.
L’output comprende anche quelle stringhe costanti che appaiono come primo
argomento della funzione dcgettext()
o come primo e secondo
argomento della funzione dcngettext()
.96
Il file .pot così generato
andrebbe distribuito insieme al programma awk
; i traduttori
potranno eventualmente utilizzarlo per preparare delle traduzioni, le quali
potranno a loro volta essere distribuite.
Vedi Esempio I18N
per una lista esauriente dei passi necessari per creare e testare
traduzioni per il programma guide
.
Successivo: Portabilità nell'I18N, Precedente: Estrazione di stringhe, Su: I18N per traduttore [Contenuti][Indice]
printf
Le stringhe di formattazione per printf
e sprintf()
(vedi Printf)
hanno un problema speciale con le traduzioni.
Si consideri il seguente esempio:97
printf(_"String `%s' has %d characters\n", string, length(string)))
Una possibile traduzione in italiano di questo messaggio potrebbe essere:
"%d è la lunghezza della stringa `%s'\n"
Il problema dovrebbe essere ovvio: l’ordine delle specifiche di
formattazione è differente da quello originale!
gettext()
, che pure può restituire la stringa tradotta
in fase di esecuzione, non è in grado di cambiare l’ordine degli argomenti
nella chiamata a printf
.
Per risolvere questo problema, gli specificatori di formato di printf
possono avere un elemento in più, facoltativo, detto specificatore
posizionale. Per esempio:
"%2$d è la lunghezza della stringa `%1$s'\n"
Qui, lo specificatore posizionale consiste in un numero intero, che indica quale argomento utilizzare, seguito da un carattere ‘$’. I numeri partono da uno, e la stringa di formattazione vera e propria non è inclusa. Quindi, nell’esempio seguente, ‘stringa’ è il primo argomento e ‘length(stringa)’ è il secondo:
$ gawk 'BEGIN { > stringa = "Non v\47allarmate!" > printf "%2$d caratteri compongono \"%1$s\"\n", > stringa, length(stringa) > }' -| 16 caratteri compongono "Non v\47allarmate!"
Se presenti, gli specificatori posizionali precedono, nella specifica di formato, i flag, la larghezza del campo e/o la precisione.
Gli specificatori posizionali possono essere usati anche se si specifica una larghezza dinamica dei campi, e della capacità di precisione:
$ gawk 'BEGIN { > printf("%*.*s\n", 10, 20, "hello") > printf("%3$*2$.*1$s\n", 20, 10, "hello") > }' -| hello -| hello
NOTA: Se si usa ‘*’ con uno specificatore posizionale, il carattere ‘*’ viene per primo, seguito dal numero che indica la posizione, a sua volta seguito dal ‘$’. Ciò può parere poco intuitivo.
gawk
non consente di mischiare specificatori di formato standard
con altri contenenti degli specificatori posizionali in una stessa stringa di
formato:
$ gawk 'BEGIN { printf "%d %3$s\n", 1, 2, "ciao" }' error→ gawk: riga com.:1: fatale: `count$' va usato per tutti error→ i formati o per nessuno
NOTA: Ci sono alcuni casi patologici in cui
gawk
potrebbe non inviare alcun messaggio diagnostico, anche quando sarebbe necessario. In tali casi, l’output può non essere quello atteso. Rimane sempre una pessima idea quella di tentare di mischiare i formati, anche segawk
non riesce ad accorgersene.
Sebbene gli specificatori posizionali possano essere usati direttamente nei
programmi awk
, il motivo per cui sono stati introdotti è perché
siano d’aiuto nel produrre traduzioni corrette della stringa di
formattazione in lingue differenti da quella nella quale il programma è stato
originariamente scritto.
Precedente: Ordinamento di printf, Su: I18N per traduttore [Contenuti][Indice]
awk
Le funzionalità di internazionalizzazione di gawk
sono state
appositamente implementate per avere il minimo impatto possibile sulla
portabilità, verso altre versioni di awk
, dei programmi
awk
che ne fanno uso.
Si consideri questo programma:
BEGIN { TEXTDOMAIN = "guide" if (Test_Guide) # da impostare tramite -v bindtextdomain("/test/guide/messages") print _"don't panic!" }
Per il modo in cui è scritto, non funzionerà con altre versioni di
awk
.
Tuttavia, è in realtà quasi portabile, e richiede modifiche minime:
TEXTDOMAIN
non avranno effetto alcuno,
perché TEXTDOMAIN
non è una variabile speciale in altre
implementazioni di awk
.
awk
considerano le stringhe marcate
come la concatenazione di una variabile di nome _
con la stringa che
viene subito dopo.98
Tipicamente, la variabile _
ha come valore la stringa nulla
(""
), il che produce come risultato la stringa
originale.
dcgettext()
,
dcngettext()
e bindtextdomain()
, il programma awk
può essere reso eseguibile, ma
tutti i messaggi verranno inviati nella lingua originale del programma.
Per esempio:
function bindtextdomain(dir, domain) { return dir } function dcgettext(string, domain, category) { return string } function dcngettext(string1, string2, number, domain, category) { return (number == 1 ? string1 : string2) }
printf
o
sprintf()
non è portabile.
Per supportare gettext()
nella programmazione in linguaggio C,
molte versioni C di sprintf()
supportano specificatori posizionali.
Ma la cosa funziona solo se nella chiamata di funzione sono stati specificati
argomenti a sufficienza. Molte
versioni di awk
passano i formati e gli argomenti di printf
,
senza modificarli, alla funzione di libreria in linguaggio C sprintf()
,
ma solo un formato e un argomento alla volta. Quel che succede se si usa una
specificazione posizionale resta indeterminato.
Tuttavia, poiché le specificazioni posizionali sono usate principalmente
per le stringhe di formattazione tradotte, e poiché le versioni
non-GNU di awk
non utilizzano mai le stringhe tradotte, ciò non
dovrebbe, in pratica, causare problemi.
Successivo: Gawk internazionalizzato, Precedente: I18N per traduttore, Su: Internazionalizzazione [Contenuti][Indice]
Vediamo ora un esempio dettagliato di come internazionalizzare e localizzare
un semplice programma awk
, usando come nostro programma sorgente
originale il file guide.awk:
BEGIN { TEXTDOMAIN = "guide" bindtextdomain(".") # per la fase di test print _"Don't Panic" print _"The Answer Is", 42 print "Pardon me, Zaphod who?" }
Eseguire ‘gawk --gen-pot’ per creare il file .pot:
$ gawk --gen-pot -f guide.awk > guide.pot
Questo produce:
#: guide.awk:4 msgid "Don't Panic" msgstr "" #: guide.awk:5 msgid "The Answer Is" msgstr ""
Questo modello di portable object va salvato e riutilizzato per ogni
lingua in cui l’applicazione viene tradotta. La stringa msgid
è
seguita dalla stringa originale da tradurre, e la stringa msgstr
conterrà la traduzione.
NOTA: Le stringhe non aventi come prefisso un trattino basso non sono inserite nel file guide.pot.
Successivamente, i messaggi devono essere tradotti. Questa è una traduzione in un ipotetico dialetto dell’inglese, chiamato “Mellow”:99
$ cp guide.pot guide-mellow.po Aggiungere traduzioni al file guide-mellow.po …
Ecco le traduzioni:
#: guide.awk:4 msgid "Don't Panic" msgstr "Hey man, relax!" #: guide.awk:5 msgid "The Answer Is" msgstr "Like, the scoop is"
Il passo successivo è di creare la directory che contenga il file binario
con le traduzioni dei messaggi (file .mo [message object]) e
creare in quella directory il file guide.mo.
Si presume che il file in questione debba essere usato nella localizzazione
en_US.UTF-8
, perché si deve usare un nome di localizzazione che sia
noto alle routine del comando C gettext
.
La disposizione delle directory qui utilizzata è standard per il comando
GNU gettext
sui sistemi GNU/Linux. Altre versioni di
gettext
possono usare una disposizione differente:
$ mkdir en_US.UTF-8 en_US.UTF-8/LC_MESSAGES
Il programma di utilità msgfmt
effettua la conversione dal file
leggibile, in formato testo, .po nel file, in formato binario,
.mo.
Per default, msgfmt
crea un file di nome messages.
A questo file dev’essere assegnato un nome appropriato, e va messo nella
directory predisposta (usando l’opzione -o) in modo che
gawk
sia in grado di trovarlo:
$ msgfmt guide-mellow.po -o en_US.UTF-8/LC_MESSAGES/guide.mo
Infine, eseguiamo il programma per provare se funziona:
$ gawk -f guide.awk -| Hey man, relax! -| Like, the scoop is 42 -| Pardon me, Zaphod who?
Se le tre funzioni che rimpiazzano dcgettext()
, dcngettext()
,
e bindtextdomain()
(vedi Portabilità nell'I18N)
sono contenute in un file di nome libintl.awk,
è possibile eseguire guide.awk senza modificarlo, nel modo seguente:
$ gawk --posix -f guide.awk -f libintl.awk -| Don't Panic -| The Answer Is 42 -| Pardon me, Zaphod who?
Successivo: Sommario I18N, Precedente: Esempio I18N, Su: Internazionalizzazione [Contenuti][Indice]
gawk
stesso è internazionalizzatoIl comando gawk
stesso è stato internazionalizzato
usando il pacchetto GNU gettext
.
(GNU gettext
è descritto in
maniera esauriente in
GNU gettext
utilities.)
Al momento in cui questo libro è stato scritto, la versione più recente di
GNU gettext
è
versione 0.19.4.
Se esiste una traduzione dei messaggi di gawk
,
gawk
invia messaggi, avvertimenti, ed errori fatali
utilizzando la lingua locale.
Precedente: Gawk internazionalizzato, Su: Internazionalizzazione [Contenuti][Indice]
gawk
usa il comando GNU gettext
per consentire
l’internazionalizzazione e la localizzazione di programmi awk
.
Un dominio di testo di un programma identifica il programma, e consente di
raggruppare tutti i messaggi e gli altri dati del programma in un solo posto.
sprintf()
e printf
per modificare la posizione del valore
degli argomenti nelle stringhe di formato e nell’output. Ciò è utile nella
traduzione di stringhe di
formattazione dei messaggi.
awk
standard.
gawk
è stato internazionalizzato e viene
distribuito con traduzioni in molte lingue dei messaggi inviati in fase di
esecuzione.
Successivo: Calcolo con precisione arbitraria, Precedente: Internazionalizzazione, Su: Top [Contenuti][Indice]
awk
Sarebbe bello se i programmi per il calcolatore funzionassero perfettamente la
prima volta che vengono eseguiti, ma nella vita reale questo accade raramente,
qualunque sia la complessità dei programmi. Perciò la maggior parte dei
linguaggi di programmazione hanno a disposizione degli strumenti che facilitano
la ricerca di errori (debugging) nei programmi, e awk
non fa
eccezione.
Il debugger di gawk
è di proposito costruito sul modello del
debugger da riga di comando
GNU Debugger (GDB).
Se si ha familiarità con GDB, sarà facile imparare come usare gawk
per eseguire il debug dei propri programmi.
• Debugging: | Introduzione al debugger di gawk .
| |
• Esempio di sessione di debug: | Esempio di sessione di debug. | |
• Lista dei comandi di debug: | Principali comandi di debug. | |
• Supporto per Readline: | Supporto per Readline. | |
• Limitazioni: | Limitazioni e piani per il futuro. | |
• Sommario sul debug: | Sommario sul debug. |
Successivo: Esempio di sessione di debug, Su: Debugger [Contenuti][Indice]
gawk
Questa sezione, dopo un’introduzione sul debug in generale, inizia
la trattazione del debug in gawk
.
• Nozioni sul debug: | Generalità sul debug. | |
• Terminologia nel debug: | Ulteriori nozioni sul debug. | |
• Debug di Awk: | Eseguire il debug di Awk. |
Successivo: Terminologia nel debug, Su: Debugging [Contenuti][Indice]
(Se si sono usati dei debugger in altri linguaggi, si può andare direttamente alla Debug di Awk.)
Naturalmente, un programma di debug non può correggere gli errori al posto del programmatore, perché non può sapere quello che il programmatore o gli utenti considerano un “bug” e non una “funzionalità”. (Talvolta, anche noi umani abbiamo difficoltà nel determinarlo.) In quel caso, cosa ci si può aspettare da un tale strumento? La risposta dipende dal linguaggio su cui si effettua il debug, comunque in generale ci si può attendere almeno questo:
Tutti questi strumenti sono di grande aiuto e permettono di usare l’abilità che si possiede e la comprensione che si ha degli obiettivi del programma per trovare dove si verificano i problemi (o, in alternativa, per comprendere meglio la logica di un programma funzionante, di cui si sia l’autore, o anche di un programma scritto da altri).
Successivo: Debug di Awk, Precedente: Nozioni sul debug, Su: Debugging [Contenuti][Indice]
Prima di entrare nei dettagli, dobbiamo introdurre diversi importanti concetti che valgono per tutti i debugger. La seguente lista definisce i termini usati nel resto di questo capitolo:
Durante la loro esecuzione i programmi normalmente chiamano delle funzioni. Una funzione può a sua volta chiamarne un’altra, o può richiamare se stessa (ricorsione). La catena di funzioni chiamate (il programma principale chiama A, che chiama B, che chiama C) può essere vista come una pila di funzioni in esecuzione: la funzione correntemente in esecuzione è quella in cima alla pila, e quando questa finisce (ritorna al chiamante), quella immediatamente sotto diventa la funzione attiva. Tale pila (stack) è chiamata call stack (pila delle chiamate).
Per ciascuna funzione della pila delle chiamate (call stack), il sistema mantiene un’area di dati che contiene i parametri della funzione, le variabili locali e i valori di ritorno, e anche ogni altra informazione “contabile” necessaria per gestire la pila delle chiamate. Quest’area di dati è chiamata stack frame.
Anche gawk
segue questo modello, e permette l’accesso alla pila
delle chiamate e a ogni stack frame. È possibile esaminare la pila
delle chiamate, e anche sapere da dove ciascuna funzione sulla pila è stata
invocata. I comandi che stampano la pila delle chiamate stampano anche le
informazioni su ogni stack frame (come vedremo più avanti in dettaglio).
Durante le operazioni di debug, spesso si preferisce lasciare che il programma venga eseguito finché non raggiunge un certo punto, e da quel punto in poi si continua l’esecuzione un’istruzione alla volta. Il modo per farlo è quello di impostare un punto d’interruzione all’interno del programma. Un punto d’interruzione è il punto dove l’esecuzione del programma dovrebbe interrompersi (fermarsi), in modo da assumere il controllo dell’esecuzione del programma. Si possono aggiungere e togliere quanti punti d’interruzione si vogliono.
Un punto d’osservazione è simile a un punto d’interruzione. La differenza è che i punti d’interruzione sono orientati attorno al codice; fermano il programma quando viene raggiunto un certo punto nel codice. Un punto d’osservazione, invece, fa fermare il programma quando è stato cambiato il valore di un dato. Questo è utile, poiché a volte succede che una variabile riceva un valore errato, ed è difficile rintracciare il punto dove ciò accade solo leggendo il codice sorgente. Usando un punto d’osservazione, si può fermare il programma in qualunque punto vi sia un’assegnazione di variabile, e di solito si individua il codice che genera l’errore abbastanza velocemente.
Precedente: Terminologia nel debug, Su: Debugging [Contenuti][Indice]
awk
Il debug di un programma awk
ha delle particolarità proprie, che
non sono presenti in programmi scritti in altri linguaggi.
Prima di tutto, il fatto che i programmi awk
ricevano generalmente
l’input riga per riga da uno o più file e operino su tali righe usando regole
specifiche, rende particolarmente agevole organizzare l’esame
dell’esecuzione del programma facendo riferimento a tali regole.
Come vedremo, ogni
regola awk
viene trattata quasi come una chiamata di funzione, col
proprio specifico blocco di istruzioni.
Inoltre, poiché awk
è un linguaggio deliberatamente molto
conciso, è facile perdere di vista tutto ciò che avviene “dentro”
ogni riga di codice awk
. Il debugger dà l’opportunità di
guardare le singole istruzioni primitive la cui esecuzione è innescata
dai comandi di alto livello di awk
.
Successivo: Lista dei comandi di debug, Precedente: Debugging, Su: Debugger [Contenuti][Indice]
gawk
Per illustrare l’uso di gawk
come debugger, vediamo un esempio di
sessione di debug. Come esempio verrà usata l’implementazione awk
del comando POSIX uniq
descritta in precedenza (vedi Programma uniq).
• Invocazione del debugger: | Come far partire il debugger. | |
• Trovare il bug: | Trovare il bug. |
Successivo: Trovare il bug, Su: Esempio di sessione di debug [Contenuti][Indice]
Per avviare il debugger in gawk
si richiama il comando esattamente
come al solito, specificando solo un’opzione aggiuntiva,
--debug, o la corrispondente opzione breve -D.
I file (o il file) che contengono
il programma e ogni codice ulteriore sono immessi sulla riga di comando come
argomenti a una o più opzioni -f. (gawk
non è progettato per
eseguire il debug di programmi scritti sulla riga di comando, ma solo per
quello di programmi che risiedono su file.)
Nel nostro caso, il debugger verrà invocato in questo modo:
$ gawk -D -f getopt.awk -f join.awk -f uniq.awk -1 file_di_input
dove entrambi i file getopt.awk e uniq.awk sono in $AWKPATH
.
(Gli utenti esperti di GDB o debugger simili dovrebbero tener presente che
questa sintassi è leggermente differente da quello che sono abituati a usare.
Col debugger di gawk
, si danno gli argomenti per eseguire il
programma nella riga di comando al debugger piuttosto che come parte del
comando run
al prompt del debugger.)
L’opzione -1 è un’opzione per uniq.awk.
Invece di eseguire direttamente il programma sul file_di_input, come
gawk
farebbe normalmente, il debugger semplicemente carica
i file sorgenti del programma, li compila internamente, e poi mostra
la riga d’invito:
gawk>
da dove si possono impartire i comandi al debugger. Sin qui non è stato ancora eseguito nessun codice.
Precedente: Invocazione del debugger, Su: Esempio di sessione di debug [Contenuti][Indice]
Poniamo di avere un problema usando (una versione difettosa di) uniq.awk nella modalità “salta-campi”, perché sembra che non catturi le righe che dovrebbero essere identiche dopo aver saltato il primo campo, come:
awk, ecco un programma meraviglioso! gawk, ecco un programma meraviglioso!
Questo potrebbe accadere se noi pensassimo (come in C) che i campi in un record siano numerati prendendo come base lo zero, per cui, invece di scrivere:
campi_ultima = join(vettore_ultima, contatore_file+1, n) campi_corrente = join(vettore_corrente, contatore_file+1, m)
abbiamo scritto:
campi_ultima = join(vettore_ultima, contatore_file, n) campi_corrente = join(vettore_corrente, contatore_file, m)
La prima cosa da fare quando si tenta di indagare su un problema come questo è
quella di mettere un punto d’interruzione (breakpoint) nel programma, in modo
da poterlo vedere al lavoro e catturare quello che non va. Una posizione
ragionevole per un punto d’interruzione in uniq.awk è all’inizio della
funzione se_sono_uguali()
, che confronta la riga corrente con la precedente.
Per impostare il punto d’interruzione, usare il comando b
(breakpoint):
gawk> b se_sono_uguali -| Breakpoint 1 impostato al file `uniq.awk', riga 63
Il debugger mostra il file e il numero di riga dove si trova il punto d’interruzione. Ora bisogna immettere ‘r’ o ‘run’ e il programma viene eseguito fino al primo punto d’interruzione:
gawk> r -| Partenza del programma: -| Mi fermo in Rule ... -| Breakpoint 1, se_sono_uguali(n, m, campi_ultima, campi_corrente, -| vettore_ultima, vettore_corrente) -| a `uniq.awk':63 -| 63 if (contatore_file == 0 && conta_caratteri == 0) gawk>
Ora possiamo osservare cosa accade all’interno del nostro programma. Prima di tutto, vediamo come siamo arrivati a questo punto. Sulla riga di comando battiamo ‘bt’ (che sta per “backtrace”), e il debugger risponde con un listato degli stack frame correnti:
gawk> bt -| #0 se_sono_uguali(n, m, campi_ultima, campi_corrente, -| vettore_ultima, vettore_corrente) -| a `uniq.awk':63 -| #1 in main() a `uniq.awk':88
Questo ci dice che la funzione se_sono_uguali()
è stata chiamata
dal programma principale alla riga 88 del file uniq.awk. (Questo non
sorprende, perché è questa l’unica chiamata a se_sono_uguali()
nel
programma, però in programmi
più complessi, sapere chi ha chiamato una funzione e con quali parametri può
essere la chiave per trovare l’origine del problema.)
Ora che siamo in se_sono_uguali()
, possiamo iniziare a guardare i valori di
alcune variabili. Immaginiamo di battere ‘p n’
(p
sta per print [stampa]). Ci aspetteremo di vedere il valore di
n
, un parametro di se_sono_uguali()
. In realtà, il debugger
ci dà:
gawk> p n -| n = untyped variable
In questo caso, n
è una variabile locale non inizializzata, perché la
funzione è stata chiamata senza argomenti (vedi Chiamate di funzione).
Una variabile più utile da visualizzare potrebbe essere la seguente:
gawk> p $0 -| $0 = "gawk, ecco un programma meraviglioso!"
All’inizio questo potrebbe lasciare un tantino perplessi, perché è la seconda
riga dell’input del test. Vediamo NR
:
gawk> p NR -| NR = 2
Come si può vedere, se_sono_uguali()
è stata chiamata solo per la seconda
riga del file. Naturalmente, ciò accade perché il nostro programma contiene
una regola per ‘NR == 1’:
NR == 1 { ultima = $0 next }
Bene, controlliamo che questa funzioni correttamente:
gawk> p ultima -| ultima = "awk, ecco un programma meraviglioso!"
Tutto ciò che è stato fatto fin qui ha verificato che il programma funziona
come previsto fino alla chiamata a se_sono_uguali()
compresa; quindi
il problema dev’essere all’interno di questa funzione. Per indagare
ulteriormente, iniziamo a “scorrere una ad una” le righe di
se_sono_uguali()
. Cominciamo col battere ‘n’ (per “next”
[successivo]):
gawk> n -| 66 if (contatore_file > 0) {
Questo ci dice che gawk
ora è pronto per eseguire la riga 66, che
decide se assegnare alle righe il trattamento speciale “salta-campi”
indicato dall’opzione sulla riga di comando -1. (Si noti che abbiamo
saltato da dov’eravamo prima, alla riga 63, a qui, perché la condizione nella
riga 63, ‘if (contatore_file == 0 && conta_caratteri == 0)’, era falsa.)
Continuando a scorrere le righe, ora raggiungiamo la divisione del record corrente e dell’ultimo:
gawk> n -| 67 n = split(ultima, vettore_ultima) gawk> n -| 68 m = split($0, vettore_corrente)
A questo punto, potremmo stare a vedere in quante parti il nostro record è stato suddiviso, quindi proviamo a osservare:
gawk> p n m vettore_ultima vettore_corrente -| n = 5 -| m = untyped variable -| vettore_ultima = array, 5 elements -| vettore_corrente = untyped variable
(Il comando p
può accettare più argomenti, analogamente
all’istruzione di awk
print
.)
Questo ci lascia piuttosto perplessi. Tutto ciò che abbiamo trovato è che ci
sono cinque elementi in vettore_ultima
; m
e vettore_corrente
non hanno valori
perché siamo alla riga 68 che non è ancora stata eseguita. Questa
informazione è abbastanza utile (ora sappiamo che nessuna delle parole è stata
lasciata fuori accidentalmente), ma sarebbe desiderabile vedere i valori
del vettore.
Una prima possibilità è quella di usare degli indici:
gawk> p vettore_ultima[0] -| "0" non presente nel vettore `vettore_ultima'
Oops!
gawk> p vettore_ultima[1] -| vettore_ultima["1"] = "awk,"
Questo metodo sarebbe piuttosto lento per un vettore con 100 elementi, per cui
gawk
fornisce una scorciatoia (che fa venire in mente un altro
linguaggio che non nominiamo):
gawk> p @vettore_ultima -| vettore_ultima["1"] = "awk," -| vettore_ultima["2"] = "ecco" -| vettore_ultima["3"] = "un" -| vettore_ultima["4"] = "programma" -| vettore_ultima["5"] = "meraviglioso!"
Finora, sembra che tutto vada bene. Facciamo un altro passo, o anche due:
gawk> n -| 69 campi_ultima = join(vettore_ultima, contatore_file, n) gawk> n -| 70 campi_corrente = join(vettore_corrente, contatore_file, m)
Bene, eccoci arrivati al nostro errore (ci spiace di aver rovinato la sorpresa). Quel che avevamo in mente era di unire i campi a partire dal secondo per creare il record virtuale da confrontare, e se il primo campo aveva il numero zero, questo avrebbe funzionato. Vediamo quel che abbiamo finora:
gawk> p campi_ultima campi_corrente -| campi_ultima = "awk, ecco un programma meraviglioso!" -| campi_corrente = "gawk, ecco un programma meraviglioso!"
Ehi! queste frasi suonano piuttosto familiari! Sono esattamente i nostri record di input originali, inalterati. Pensandoci un po’ (il cervello umano è ancora il miglior strumento di debug), ci si rende conto che eravamo fuori di uno!
Usciamo dal debugger:
gawk> q -| Il programma è in esecuzione. Esco comunque (y/n)? y
Quindi modifichiamo con un editore di testo:
campi_ultima = join(vettore_ultima, contatore_file+1, n) campi_corrente = join(vettore_corrente, contatore_file+1, m)
e il problema è risolto!
Successivo: Supporto per Readline, Precedente: Esempio di sessione di debug, Su: Debugger [Contenuti][Indice]
L’insieme dei comandi del debugger di gawk
può essere diviso nelle
seguenti categorie:
Ciascuna di esse è trattata nelle sottosezioni che seguono.
Nelle descrizioni seguenti, i comandi che possono essere abbreviati
mostrano l’abbreviazione su una seconda riga di descrizione.
Un nome di comando del debugger può essere anche troncato se la parte già scritta
non è ambigua. Il debugger ha la capacità predefinita di ripetere
automaticamente il precedente comando semplicemente battendo Invio.
Questo vale per i comandi list
, next
, nexti
,
step
, stepi
e continue
quando sono eseguiti senza
argomenti.
• Controllo dei breakpoint: | Controllo dei punti d’interruzione. | |
• Controllo esecuzione debugger: | Controllo di esecuzione. | |
• Vedere e modificare dati: | Vedere e modificare dati. | |
• Stack di esecuzione: | Lavorare con le pile. | |
• Informazioni sul debugger: | Ottenere informazioni sullo stato del programma e del debugger. | |
• Comandi vari del debugger: | Comandi vari del debugger. |
Successivo: Controllo esecuzione debugger, Su: Lista dei comandi di debug [Contenuti][Indice]
Come abbiamo già visto, la prima cosa che si dovrebbe fare in una sessione di debug è quella di definire dei punti d’interruzione, poiché altrimenti il programma verrà eseguito come se non fosse sotto il debugger. I comandi per controllare i punti d’interruzione sono:
break
[[nome-file:
]n | funzione] ["espressione"
]b
[[nome-file:
]n | funzione] ["espressione"
]Senza argomenti, imposta un punto d’interruzione alla prossima istruzione da eseguire nello stack frame selezionato. Gli argomenti possono essere uno dei seguenti:
Imposta un punto d’interruzione alla riga numero n nel file sorgente corrente.
:
nImposta un punto d’interruzione alla riga numero n nel file sorgente nome-file.
Imposta un punto d’interruzione all’ingresso (la prima istruzione eseguibile) della funzione funzione.
A ogni punto d’interruzione è assegnato un numero che può essere usato per
cancellarlo dalla lista dei punti d’interruzione usando il comando
delete
.
Specificando un punto d’interruzione, si può fornire anche una condizione.
Questa è
un’espressione awk
(racchiusa tra doppi apici) che il debugger
valuta ogni volta che viene raggiunto quel punto d’interruzione. Se la
condizione è vera, il debugger ferma l’esecuzione e rimane in attesa di un
comando. Altrimenti, continua l’esecuzione del programma.
clear
[[nome-file:
]n | funzione]Senza argomenti, cancella ogni eventuale punto d’interruzione all’istruzione successiva da eseguirsi nello stack frame selezionato. Se il programma si ferma in un punto d’interruzione, quel punto d’interruzione viene cancellato in modo che il programma non si fermi più in quel punto. Gli argomenti possono essere uno tra i seguenti:
Cancella il punto (o i punti) d’interruzione impostato/i alla riga n nel file sorgente corrente.
:
nCancella il punto (o i punti) d’interruzione impostato/i alla riga n nel file sorgente nome-file.
Cancella il punto (o i punti) d’interruzione impostato/i all’ingresso della funzione funzione.
condition
n "espressione"
Aggiunge una condizione al punto d’interruzione o al punto d’osservazione
esistente n. La condizione è un’espressione awk
racchiusa tra doppi apici che il debugger valuta ogni volta che viene
raggiunto il punto d’interruzione o il punto d’osservazione. Se la condizione è
vera, il debugger ferma l’esecuzione e attende l’immissione di un comando.
Altrimenti, il debugger continua l’esecuzione del programma. Se l’espressione
della condizione non viene specificata, tutte le condizioni esistenti vengono
rimosse (cioè, il punto d’interruzione o di osservazione viene considerato
incondizionato).
delete
[n1 n2 …] [n–m]d
[n1 n2 …] [n–m]Cancella i punti d’interruzione specificati o un intervallo di punti d’interruzione. Se non vengono forniti argomenti, cancella tutti i punti d’interruzione esistenti.
disable
[n1 n2 … | n–m]Disabilita punti d’interruzione specificati o un intervallo di essi. Senza argomenti, disabilita tutti i punti d’interruzione.
enable
[del
| once
] [n1 n2 …] [n–m]e
[del
| once
] [n1 n2 …] [n–m]Abilita specifici punti d’interruzione o un intervallo di essi. Senza argomenti, abilita tutti i punti d’interruzione. Opzionalmente, si può specificare come abilitare i punti d’interruzione:
del
Abilita dei punti d’interruzione una tantum, poi li cancella quando il programma si ferma in quel punto.
once
Abilita dei punti d’interruzione una tantum, poi li cancella quando il programma si ferma in quel punto.
ignore
n contatoreIgnora il punto d’interruzione numero n le successive contatore volte in cui viene raggiunto.
tbreak
[[nome-file:
]n | funzione]t
[[nome-file:
]n | funzione]Imposta un punto d’interruzione temporaneo (abilitato solo per la prima volta
che viene raggiunto). Gli argomenti sono gli stessi di break
.
Successivo: Vedere e modificare dati, Precedente: Controllo dei breakpoint, Su: Lista dei comandi di debug [Contenuti][Indice]
Dopo che i punti d’interruzione sono pronti, si può iniziare l’esecuzione del programma, osservando il suo comportamento. Ci sono più comandi per controllare l’esecuzione del programma di quelli visti nei precedenti esempi:
commands
[n]silent
end
Imposta una lista di comandi da eseguire subito dopo l’arresto del programma in
un punto d’interruzione o di osservazione. n è il numero del punto
d’interruzione o di osservazione. Se non si specifica un numero, viene usato
l’ultimo numero che è stato specificato. I comandi veri e propri seguono,
a cominciare dalla riga successiva, e hanno termine col comando end
.
Se il comando silent
è nella lista, i consueti messaggi sull’arresto del
programma a un punto d’interruzione e la riga sorgente non vengono stampati.
Qualsiasi comando nella lista che riprende l’esecuzione (p.es.,
continue
) pone fine alla lista (un end
implicito), e i comandi
successivi vengono ignorati.
Per esempio:
gawk> commands > silent > printf "Un punto d'interruzione silenzioso; i = %d\n", i > info locals > set i = 10 > continue > end gawk>
continue
[contatore]c
[contatore]Riprende l’esecuzione del programma. Se si riparte da un punto d’interruzione e viene specificato contatore, il punto d’interruzione in quella posizione viene ignorato per le prossime contatore volte prima di fermarsi nuovamente.
finish
Esegue fino a quando lo stack frame selezionato completa l’esecuzione. Stampa il valore restituito.
next
[contatore]n
[contatore]Continua l’esecuzione alla successiva riga sorgente, saltando le chiamate di
funzione. L’argomento contatore controlla il numero di ripetizioni
dell’azione, come in step
.
nexti
[contatore]ni
[contatore]Esegue una o contatore istruzioni, comprese le chiamate di funzione.
return
[valore]Cancella l’esecuzione di una chiamata di funzione. Se valore (una stringa o un numero) viene specificato, è usato come valore di ritorno della funzione. Se usato in un frame diverso da quello più interno (la funzione correntemente in esecuzione; cioè, il frame numero 0), ignora tutti i frame più interni di quello selezionato, e il chiamante del frame selezionato diventa il frame più interno.
run
r
Avvia/riavvia l’esecuzione del programma. Quando il programma viene riavviato, il debugger mantiene i punti d’interruzione e di osservazione, la cronologia dei comandi, la visualizzazione automatica di variabili, e le opzioni del debugger.
step
[contatore]s
[contatore]Continua l’esecuzione finché il controllo non raggiunge una diversa riga del sorgente nello stack frame corrente, eseguendo ogni funzione chiamata all’interno della riga. Se viene fornito l’argomento contatore, esegue il numero di istruzioni specificate prima di fermarsi, a meno che non s’imbatta in un punto d’interruzione o di osservazione.
stepi
[contatore]si
[contatore]Esegue una o contatore istruzioni, comprese le chiamate di funzione.
(Per una spiegazione su cosa s’intende per “istruzione” in gawk
,
si veda l’output mostrato sotto dump
nella
Comandi vari del debugger.)
until
[[nome-file:
]n | funzione]u
[[nome-file:
]n | funzione]Senza argomenti, prosegue l’esecuzione finché non viene raggiunta una riga dopo la riga corrente nello stack frame corrente. Se viene specificato un argomento, prosegue l’esecuzione finché non viene raggiunto il punto specificato, o lo stack frame corrente non termina l’esecuzione.
Successivo: Stack di esecuzione, Precedente: Controllo esecuzione debugger, Su: Lista dei comandi di debug [Contenuti][Indice]
I comandi per vedere e modificare variabili all’interno di gawk
sono:
display
[var | $
n]Aggiunge la variabile var (o il campo $n
) alla lista di
visualizzazione. Il valore della variabile o del campo è visualizzato ogni
volta che il programma s’interrompe.
Ogni variabile aggiunta alla lista è identificata da un numero univoco:
gawk> display x -| 10: x = 1
La riga qui sopra mostra il numero di elemento assegnato, il nome della
variabile e il suo
valore corrente. Se la variabile di display fa riferimento a un parametro di
funzione, è cancellata silenziosamente dalla lista non appena l’esecuzione
raggiunge un contesto dove la variabile con quel nome non esiste più.
Senza argomenti, display
mostra i valori correnti degli elementi della
lista.
eval "istruzioni awk"
Valuta istruzioni awk nel contesto del programma in esecuzione.
Si può fare qualsiasi cosa che un programma awk
farebbe: assegnare
valori a variabili, chiamare funzioni, e così via.
eval
param, …end
Questa forma di eval
è simile alla precedente, solo che permette di
definire
“variabili locali” che esistono nel contesto delle istruzioni awk,
invece di usare variabili o parametri di funzione già definiti nel programma.
print
var1[,
var2 …]p
var1[,
var2 …]Stampa i valori di una o più variabili o campi di gawk
.
I campi devono essere indicizzati usando delle costanti:
gawk> print $3
Questo stampa il terzo campo del record di input (se il campo specificato non esiste, stampa il ‘campo nullo’). Una variabile può essere un elemento di un vettore, avente come indice una stringa di valore costante. Per stampare il contenuto di un vettore, si deve anteporre il simbolo ‘@’ al nome del vettore:
gawk> print @a
L’esempio stampa gli indici e i corrispondenti valori di tutti gli elementi
del vettore a
.
printf
formato [,
arg …]Stampa un testo formattato. Il formato può includere sequenze di protezione, come ‘\n’ (vedi Sequenze di protezione). Non viene stampato nessun ritorno a capo che non sia stato specificato esplicitamente.
set
var=
valoreAssegna un valore costante (numero o stringa) a una variabile o a un campo di
awk
.
I valori di stringa devono essere racchiusi tra doppi apici
("
…"
).
Si possono impostare anche delle variabili speciali di awk
, come
FS
, NF
, NR
, e così via.
watch
var | $
n ["espressione"
]w
var | $
n ["espressione"
]Aggiunge la variabile var (o il campo $n
) alla lista dei
punti d’osservazione. Il debugger quindi interrompe il programma ogni volta
che il valore della variabile o del campo cambia. A ogni elemento osservato
viene assegnato un numero che può essere usato per cancellarlo dalla lista
usando il comando unwatch
[non-osservare più].
Definendo un punto d’osservazione, si può anche porre una condizione, che è
un’espressione awk
(racchiusa tra doppi apici) che il debugger valuta
ogni volta che viene raggiunto il punto d’osservazione. Se la condizione è
vera, il debugger interrompe l’esecuzione e rimane in attesa di un comando.
Altrimenti, gawk
prosegue nell’esecuzione del programma.
undisplay
[n]Rimuove l’elemento numero n (o tutti gli elementi, se non vi sono argomenti) dalla lista delle visualizzazioni automatiche.
unwatch
[n]Rimuove l’elemento numero n (o tutti gli elementi, se non vi sono argomenti) dalla lista dei punti d’osservazione.
Successivo: Informazioni sul debugger, Precedente: Vedere e modificare dati, Su: Lista dei comandi di debug [Contenuti][Indice]
Ogni volta che si esegue un programma che contiene chiamate di funzione,
gawk
mantiene una pila contenente la lista delle chiamate di funzione
che hanno portato al punto in cui il programma si trova in ogni momento. È
possibile vedere a che punto si trova il programma, e anche muoversi
all’interno della pila per vedere qual era lo stato delle cose nelle funzioni
che hanno chiamato quella in cui ci si trova. I comandi per far questo sono:
backtrace
[contatore]bt
[contatore]where
[contatore]Stampa a ritroso una traccia di tutte le chiamate di funzione (stack frame), o
i dei contatore frame più interni se contatore > 0. Stampa i
contatore frame più esterni se contatore < 0. La tracciatura a
ritroso mostra il nome e gli argomenti di ciascuna funzione, il sorgente
nome-file, e il numero di riga. L’alias where
per backtrace
viene mantenuto per i vecchi utenti di GDB che potrebbero essere abituati a
quel comando.
down
[contatore]Sposta contatore (default 1) frame sotto la pila verso il frame più interno. Poi seleziona e stampa il frame.
frame
[n]f
[n]Seleziona e stampa lo stack frame n. Il frame 0 è quello correntemente in esecuzione, o il frame più interno, (chiamata di funzione); il frame 1 è il frame che ha chiamato quello più interno. Il frame col numero più alto è quello per il programma principale. Le informazioni stampate comprendono il numero di frame, i nomi delle funzioni e degli argomenti, i file sorgenti e le righe sorgenti.
up
[contatore]Sposta contatore (default 1) frame sopra la pila verso il frame più esterno. Poi seleziona e stampa il frame.
Successivo: Comandi vari del debugger, Precedente: Stack di esecuzione, Su: Lista dei comandi di debug [Contenuti][Indice]
Oltre che vedere i valori delle variabili, spesso si ha necessità di ottenere
informazioni di altro tipo sullo stato del programma e dello stesso ambiente di
debug. Il debugger di gawk
ha un comando che fornisce
quest’informazione, chiamato convenientemente info
. info
è usato con uno dei tanti argomenti che dicono esattamente quel che si vuol
sapere:
info
cosai
cosaIl valore di cosa dovrebbe essere uno dei seguenti:
args
Elenca gli argomenti del frame selezionato.
break
Elenca tutti i punti d’interruzione attualmente impostati.
display
Elenca tutti gli elementi della lista delle visualizzazioni automatiche.
frame
Dà una descrizione degli stack frame selezionati.
functions
Elenca tutte le definizioni delle funzioni compresi i nomi-file e i numeri di riga.
locals
Elenca le variabili locali dei frame selezionati.
source
Stampa il nome del file sorgente corrente. Ogni volta che il programma si interrompe, il file sorgente corrente è il file che contiene l’istruzione corrente. Quando il debugger viene avviato per la prima volta, il file sorgente corrente è il primo file incluso attraverso l’opzione -f. Il comando ‘list nome-file:numero-riga’ può essere usato in qualsiasi momento per cambiare il sorgente corrente.
sources
Elenca tutti i sorgenti del programma.
variables
Elenca tutte le variabili locali.
watch
Elenca tutti gli elementi della lista dei punti d’osservazione.
Ulteriori comandi permettono di avere il controllo sul debugger, la capacità di salvare lo stato del debugger e la capacità di eseguire comandi del debugger da un file. I comandi sono:
option
[nome[=
valore]]o
[nome[=
valore]]Senza argomenti, visualizza le opzioni del debugger disponibili e i loro valori correnti. ‘option nome’ mostra il valore corrente dell’opzione così denominata. ‘option nome=valore’ assegna un nuovo valore all’opzione. Le opzioni disponibili sono:
history_size
Imposta il numero massimo di righe da mantenere nel file della cronologia ./.gawk_history. Il valore di default è 100.
listsize
Specifica il numero di righe che list
deve stampare. Il valore di
default è 15.
outfile
Invia l’output di gawk
in un file; l’output del debugger è
visualizzato comunque anche
nello standard output. Assegnare come valore stringa vuota (""
)
reimposta l’output solo allo standard output.
prompt
Cambia la riga per l’immissione dei comandi del debugger. Il valore di default è ‘gawk> ’.
save_history
[on
| off
]Salva la cronologia dei comandi nel file ./.gawk_history.
L’impostazione di default è on
.
save_options
[on
| off
]Salva le opzioni correnti nel file ./.gawkrc all’uscita.
L’impostazione di default è on
.
Le opzioni sono lette di nuovo all’avvio della sessione successiva.
trace
[on
| off
]Attiva o disattiva il tracciamento delle istruzioni. L’impostazione di default
è off
.
save
nome-fileSalva i comandi eseguiti nella sessione corrente nel nome-file indicato,
in modo da poterli ripetere in seguito usando il comando source
.
source
nome-fileEsegue comandi contenuti in un file; un errore in un comando non impedisce
l’esecuzione dei comandi successivi. In un file di comandi sono consentiti
i commenti (righe che iniziano con ‘#’).
Le righe vuote vengono ignorate; esse non
ripetono l’ultimo comando.
Non si può riavviare il programma mettendo più di un comando run
nel file. Inoltre, la lista dei comandi può includere altri comandi
source
; in ogni caso, il debugger di gawk
non richiama lo
stesso file più di una volta per evitare ricorsioni infinite.
Oltre al comando source
, o al posto di esso, si possono usare le opzioni
sulla riga di comando -D file o --debug=file
per eseguire comandi da un file in maniera non interattiva
(vedi Opzioni).
Precedente: Informazioni sul debugger, Su: Lista dei comandi di debug [Contenuti][Indice]
Ci sono alcuni altri comandi che non rientrano nelle precedenti categorie, come i seguenti:
dump
[nome-file]Riversa il byte code del programma nello standard output o nel file
definito in nome-file. Questo stampa una rappresentazione delle
istruzioni interne che gawk
esegue per implementare i comandi
awk
in un programma. Ciò può essere molto istruttivo, come
dimostra il seguente riversamento parziale del codice offuscato di
Davide Brini (vedi Programma signature):
gawk> dump -| # BEGIN -| -| [ 1:0xfcd340] Op_rule : [in_rule = BEGIN] [source_file = brini.awk] -| [ 1:0xfcc240] Op_push_i : "~" [MALLOC|STRING|STRCUR] -| [ 1:0xfcc2a0] Op_push_i : "~" [MALLOC|STRING|STRCUR] -| [ 1:0xfcc280] Op_match : -| [ 1:0xfcc1e0] Op_store_var : O -| [ 1:0xfcc2e0] Op_push_i : "==" [MALLOC|STRING|STRCUR] -| [ 1:0xfcc340] Op_push_i : "==" [MALLOC|STRING|STRCUR] -| [ 1:0xfcc320] Op_equal : -| [ 1:0xfcc200] Op_store_var : o -| [ 1:0xfcc380] Op_push : o -| [ 1:0xfcc360] Op_plus_i : 0 [MALLOC|NUMCUR|NUMBER] -| [ 1:0xfcc220] Op_push_lhs : o [do_reference = true] -| [ 1:0xfcc300] Op_assign_plus : -| [ :0xfcc2c0] Op_pop : -| [ 1:0xfcc400] Op_push : O -| [ 1:0xfcc420] Op_push_i : "" [MALLOC|STRING|STRCUR] -| [ :0xfcc4a0] Op_no_op : -| [ 1:0xfcc480] Op_push : O -| [ :0xfcc4c0] Op_concat : [expr_count = 3] [concat_flag = 0] -| [ 1:0xfcc3c0] Op_store_var : x -| [ 1:0xfcc440] Op_push_lhs : X [do_reference = true] -| [ 1:0xfcc3a0] Op_postincrement : -| [ 1:0xfcc4e0] Op_push : x -| [ 1:0xfcc540] Op_push : o -| [ 1:0xfcc500] Op_plus : -| [ 1:0xfcc580] Op_push : o -| [ 1:0xfcc560] Op_plus : -| [ 1:0xfcc460] Op_leq : -| [ :0xfcc5c0] Op_jmp_false : [target_jmp = 0xfcc5e0] -| [ 1:0xfcc600] Op_push_i : "%c" [MALLOC|STRING|STRCUR] -| [ :0xfcc660] Op_no_op : -| [ 1:0xfcc520] Op_assign_concat : c -| [ :0xfcc620] Op_jmp : [target_jmp = 0xfcc440] -| … -| -| [ 2:0xfcc5a0] Op_K_printf : [expr_count = 17] [redir_type = ""] -| [ :0xfcc140] Op_no_op : -| [ :0xfcc1c0] Op_atexit : -| [ :0xfcc640] Op_stop : -| [ :0xfcc180] Op_no_op : -| [ :0xfcd150] Op_after_beginfile : -| [ :0xfcc160] Op_no_op : -| [ :0xfcc1a0] Op_after_endfile : gawk>
exit
Esce dal debugger. Si veda la voce ‘quit’, più avanti in quest’elenco.
help
h
Stampa una lista di tutti i comandi del debugger di gawk
con un breve
sommario su come usarli. ‘help comando’ stampa l’informazione sul
comando comando.
list
[-
| +
| n | nome-file:
n | n–m | funzione]l
[-
| +
| n | nome-file:
n | n–m | funzione]Stampa le righe specificate (per default 15) dal file sorgente corrente
o il file chiamato nome-file. I possibili argomenti di list
sono i seguenti:
-
(Meno)Stampa righe prima delle ultime righe stampate.
+
Stampa righe dopo le ultime righe stampate.
list
senza argomenti fa la stessa cosa.
Stampa righe centrate attorno alla riga numero n.
Stampa righe dalla numero n alla numero m.
:
nStampa righe centrate attorno alla riga numero n nel file sorgente nome-file. Questo comando può cambiare il file sorgente corrente.
Stampa righe centrate attorno all’inizio della funzione function. Questo comando può cambiare il file sorgente corrente.
quit
q
Esce dal debugger. Fare il debug è divertente, ma noi tutti a volte dobbiamo far fronte ad altri impegni nella vita, e talvolta troviamo il bug e possiamo tranquillamente passare a quello successivo! Come abbiamo visto prima, se si sta eseguendo un programma, il debugger avverte quando si batte ‘q’ o ‘quit’, in modo da essere sicuri di voler realmente abbandonare il debug.
trace
[on
| off
]Abilita o disabilita la stampa continua delle istruzioni che si stanno per
eseguire, assieme alle righe di awk
che implementano.
L’impostazione di default è off
.
È auspicabile che la maggior parte dei “codici operativi” (o “opcode”)
in queste istruzioni siano sufficientemente autoesplicativi, e l’uso di
stepi
e nexti
mentre trace
è abilitato li renderà
familiari.
Successivo: Limitazioni, Precedente: Lista dei comandi di debug, Su: Debugger [Contenuti][Indice]
Se gawk
è compilato con
la libreria
GNU Readline, ci si può avvantaggiare delle sue funzionalità riguardanti il
completamento dei comandi della libreria e l’espansione della cronologia. Sono
disponibili i seguenti tipi di completamento:
Nomi dei comandi.
Nomi-file dei sorgenti. I relativi comandi sono
break
,
clear
,
list
,
tbreak
e
until
.
Argomenti di un comando non numerici.
I relativi comandi sono enable
e info
.
Interessa i nomi delle variabili globali, e gli argomenti di funzione nel
contesto corrente se
il programma è in esecuzione. I relativi comandi sono
display
,
print
,
set
e
watch
.
Successivo: Sommario sul debug, Precedente: Supporto per Readline, Su: Debugger [Contenuti][Indice]
Si spera che il lettore trovi il debugger di gawk
utile e piacevole
da usare, ma come accade per ogni programma, specialmente nelle sue prime
versioni, ha ancora delle limitazioni. Quelle di cui è bene essere al corrente sono:
gawk
),
ci si renderà conto che gran parte della manipolaziona interna di dati
in gawk
, così come in molti interpreti, è fatta su di una pila.
Op_push
, Op_pop
, e simili sono il pane quotidiano di
gran parte del codice di gawk
.
Sfortunatamente, al momento, il debugger di gawk
non consente
di esaminare i contenuti della pila.
Cioè, i risultati intermedi della valutazione delle espressioni sono sulla
pila, ma non è possibile stamparli. Invece, possono essere stampate solo
quelle variabili che sono state definite nel programma. Naturalmente, un
espediente per cercare di rimediare è di usare più variabili esplicite in
fase di debug e
poi cambiarle di nuovo per ottenere un codice forse più difficile da
comprendere, ma più ottimizzato.
awk
, ci si aspetta che chi legge conosca il significato di
/[^[:alnum:][:blank:]]/
.
gawk
è progettato per essere usato eseguendo un
programma (con tutti i suoi parametri) dalla riga di comando, come descritto
in
Invocazione del debugger. Non c’è alcun modo (al momento) di modificare
o di “entrare dentro” l’esecuzione di un programma.
Questo sembra ragionevole per un linguaggio che è usato principalmente per
eseguire programmi piccoli e che non richiedono molto tempo di esecuzione.
gawk
accetta solo codice sorgente fornito con
l’opzione -f.
Precedente: Limitazioni, Su: Debugger [Contenuti][Indice]
gawk
ha un debugger incorporato che funziona in
modo molto simile al debugger GNU, GDB.
gawk
funziona in
termini di stack frame, e si possono inserire sia punti d’interruzione
(interruzioni a un certo punto del codice) sia punti d’osservazione
(interruzioni quando il valore di un dato cambia).
gawk
, viene usata dal debugger per fornire la cronologia della riga
di comando e delle modifiche apportate durante il debug.
Successivo: Estensioni dinamiche, Precedente: Debugger, Su: Top [Contenuti][Indice]
gawk
In questo capitolo si introducono alcuni concetti base su come i
computer
eseguono i calcoli e si definiscono alcuni termini importanti.
Si continua poi descrivendo il calcolo in virgola mobile,
che è quello che awk
usa per tutte le sue operazioni aritmetiche,
e si prosegue con
una trattazione del calcolo in virgola mobile con precisione arbitraria,
una funzionalità disponibile solo in gawk
. Si passa poi a
illustrare i numeri interi a precisione arbitraria e si conclude con una
descrizione di alcuni punti sui quali gawk
e lo standard POSIX non
sono esattamente in accordo.
NOTA: La maggior parte degli utenti di
gawk
può saltare senza patemi d’animo questo capitolo. Tuttavia, se si vogliono eseguire calcoli scientifici congawk
, questo è il luogo adatto per imparare a farlo.
• Aritmetica del computer: | Una rapida introduzione alla matematica del computer. | |
• Definizioni matematiche: | Definizione dei termini usati. | |
• Funzionalità MPFR: | Funzionalità MPFR in gawk .
| |
• Cautela col calcolo in VM: | Cose da sapere. | |
• Interi a precisione arbitraria: | Calcolo con numeri interi a precisione
arbitraria con gawk .
| |
• Problemi virgola mobile POSIX: | Confronto tra standard e uso corrente. | |
• Sommario virgola mobile: | Sommario della trattazione della virgola mobile. |
Successivo: Definizioni matematiche, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
Sinora, abbiamo avuto a che fare con dati come numeri o stringhe. Ultimamente, comunque, i computer rappresentano ogni cosa in termini di cifre binarie, o bit. Una cifra decimale può assumere uno di 10 valori: da zero a nove. Una cifra binaria può assumere uno di due valori: zero o uno. Usando il sistema binario, i computer (e i programmi per computer) possono rappresentare e manipolare dati numerici e dati costituiti da caratteri. In generale, tanti più bit è possibile usare per rappresentare una determinata cosa, tanto maggiore sarà l’intervallo dei possibili valori che essa potrà assumere.
I moderni calcolatori possono eseguire calcoli numerici in almeno due modi, e spesso anche di più. Ogni tipo di calcolo usa una diversa rappresentazione (organizzazione dei bit) dei numeri. Le modalità di calcolo che ci interessano sono:
Questo è il tipo di calcolo che s’impara alle scuole elementari, usando carta e penna (o anche una calcolatrice). In teoria, i numeri possono avere un numero arbitrario di cifre su ambo i lati del separatore decimale, e il risultato di un’operazione è sempre esatto.
Alcuni sistemi moderni possono eseguire calcoli decimali direttamente, tramite apposite istruzioni disponibili nell’hardware dell’elaboratore, ma normalmente si ha necessità di una speciale libreria software che consenta di effettuare le operazioni desiderate. Ci sono anche librerie che svolgono i calcoli decimali interamente per via software.
Anche se alcuni utenti si aspettano che gawk
effettui delle
operazioni usando numeri in base decimale,100 non è questo quello che succede.
A scuola ci hanno insegnato che i valori interi erano quei numeri privi di una parte frazionaria, come 1, 42, o -17. Il vantaggio dei numeri interi è che essi rappresentano dei valori in maniera esatta. Lo svantaggio è che i numeri rappresentabili sono limitati.
Nei calcolatori, i valori interi sono di due tipi: con segno e senza segno. I valori con segno possono essere negativi o positivi, mentre i valori senza segno sono sempre maggiori o uguali a zero.
Nei sistemi informatici, il calcolo con valori interi è esatto, ma il possibile campo di variazione dei valori è limitato. L’elaborazione con numeri interi è più veloce di quella con numeri a virgola mobile.
I numeri a virgola mobile rappresentano quelli che a scuola sono chiamati numeri “reali” (cioè, quelli che hanno una parte frazionaria, come 3.1415927). Il vantaggio dei numeri a virgola mobile è che essi possono rappresentare uno spettro di valori molto più ampio di quello rappresentato dai numeri interi. Lo svantaggio è che ci sono numeri che essi non possono rappresentare in modo esatto.
I computer moderni possono eseguire calcoli su valori a virgola mobile nell’hardware dell’elaboratore, entro un intervallo di valori limitato. Ci sono inoltre librerie di programmi che consentono calcoli, usando numeri a virgola mobile, di precisione arbitraria.
POSIX awk
usa numeri a virgola mobile a doppia precisione,
che possono gestire più cifre rispetto ai numeri a virgola mobile a
singola precisione. gawk
ha inoltre funzionalità, descritte
in dettaglio più sotto, che lo mettono in grado di eseguire
calcoli con i numeri a virgola mobile con precisione arbitraria.
I calcolatori operano con valori interi e a virgola mobile su diversi intervalli. I valori interi normalmente hanno una dimensione di 32 bit o 64 bit. I valori a virgola mobile a singola precisione occupano 32 bit, mentre i valori a virgola mobile a doppia precisione occupano 64 bit. I valori a virgola mobile sono sempre con segno. Il possibile campo di variazione dei valori è mostrato in Tabella 15.1.
Rappresentazione numerica | Valore minimo | Valore massimo |
---|---|---|
Interi con segno a 32-bit | -2.147.483.648 | 2.147.483.647 |
Interi senza segno a 32-bit | 0 | 4.294.967.295 |
Interi con segno a 64-bit | -9.223.372.036.854.775.808 | 9.223.372.036.854.775.807 |
Interi senza segno a 64-bit | 0 | 18.446.744.073.709.551.615 |
Virgola mobile, singola precisione (circa) | 1,175494-38 | 3,40282338 |
Virgola mobile, singola precisione (circa) | 2,225074-308 | 1,797693308 |
Tabella 15.1: Intervalli dei valori per diverse rappresentazioni numeriche
Successivo: Funzionalità MPFR, Precedente: Aritmetica del computer, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
Il resto di questo capitolo usa un certo numero di termini. Di seguito vengono date alcune definizioni informali che dovrebbero essere utili per la lettura di questo documento:
L’accuratezza del calcolo sui numeri a virgola mobile indica di quanto si avvicina il calcolo al valore reale (calcolato con carta e penna).
La differenza tra quello che il risultato di un calcolo “dovrebbe dare” e quello che effettivamente dà. È meglio minimizzare l’errore quanto più possibile.
L’ordine di grandezza di un valore; alcuni bit in un valore a virgola mobile contengono l’esponente.
Un valore speciale che rappresenta l’infinito. Le operazioni tra un qualsiasi numero e l’infinito danno infinito.
Un valore a virgola mobile è formato dalla mantissa moltiplicata per 10
alla potenza dell’esponente. Per esempio, in 1,2345e67
,
la mantissa è 1,2345
.
Come i numeri vanno arrotondati, per eccesso o per difetto, quando necessario. Maggiori dettagli verranno forniti in seguito.
“Not a number” (Non un Numero).101 Un
valore speciale che risulta da un calcolo che non ha risposta come numero
reale. In tal caso, i programmi possono o ricevere un’eccezione di virgola
mobile, o restituire NaN
come risultato. Lo standard IEEE 754
consiglia che i sistemi restituiscano NaN
. Alcuni esempi:
sqrt(-1)
La radice quadrata di -1 ha senso nell’insieme dei numeri complessi,
ma non nell’insieme dei numeri reali,
per cui il risultato è NaN
.
log(-8)
Il logaritmo di -8 è fuori dal dominio di log()
,
per cui il risultato è NaN
.
Come la mantissa (vedi oltre in questa lista) è usualmente memorizzata. Il valore viene aggiustato in modo che il primo bit sia sempre uno, e in questo modo l’uno iniziale è supposto presente (per come viene generato il numero), ma non è memorizzato fisicamente. Questo fornisce un bit di precisione in più.
Il numero di bit usati per rappresentare un numero a virgola mobile. Più sono i bit, e maggiore è l’intervallo di cifre che si possono rappresentare. Le precisioni binaria e decimale sono legate in modo approssimativo, secondo la formula:
prec = 3.322 * dps
Qui, prec indica la precisione binaria (misurata in bit) e dps (abbreviazione di "decimal places") indica le cifre decimali.
Dall’articolo di Wikipedia sulla stabilità numerica: “I calcoli per i quali si può dimostrare che non amplificano gli errori di approssimazione sono chiamati numericamente stabili.”
Si veda l’articolo di Wikipedia su accuratezza e precisione per maggiori informazioni su questi due termini.
Sui computer moderni, l’unità di calcolo in virgola mobile usa la
rappresentazione e le operazioni definite dallo standard IEEE 754.
Tre dei tipi definiti nello standard IEEE 754 sono:
32-bit singola precisione,
64-bit doppia precisione e
128-bit quadrupla precisione.
Lo standard specifica anche formati a precisione estesa
per consentire una maggiore precisione e campi di variazione degli esponenti
più ampi. (awk
usa solo il formato a 64-bit doppia precisione.)
Tabella 15.2 elenca la precisione e i valori di campo dell’esponente per i principali formati binari IEEE 754.
Nome | Bit totali | Precisione | Esponente minimo | Esponente massimo |
---|---|---|---|---|
Singola | 32 | 24 | -126 | +127 |
Doppia | 64 | 53 | -1022 | +1023 |
Quadrupla | 128 | 113 | -16382 | +16383 |
Tabella 15.2: Valori per i principali formati IEEE
NOTA: I numeri che descrivono la precisione includono la cifra 1 iniziale implicita, il che equivale ad avere un bit in più nella mantissa.
Successivo: Cautela col calcolo in VM, Precedente: Definizioni matematiche, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
gawk
Per default, gawk
usa i valori in virgola mobile a doppia precisione
disponibili nell’hardware del sistema su cui viene eseguito.
Tuttavia, se è stato compilato in modo da includere questa funzionalità
ed è stata specificata
l’opzione da riga di comando -M, gawk
usa le librerie
GNU MPFR e GNU MP (GMP)
per effettuare calcoli sui numeri con una precisione arbitraria.
Si può verificare se il supporto a MPFR è disponibile in questo modo:
$ gawk --version -| GNU Awk 4.1.2, API: 1.1 (GNU MPFR 3.1.0-p3, GNU MP 5.0.2) -| Copyright (C) 1989, 1991-2015 Free Software Foundation. …
(I numeri di versione visualizzati possono essere diversi. Non importa; l’importante è che siano presenti GNU MPFR e GNU MP nel testo restituito.)
Inoltre, ci sono alcuni elementi disponibili nel vettore PROCINFO
per fornire informazioni sulle librerie MPFR e GMP
(vedi Variabili auto-assegnate).
La libreria MPFR dà un controllo accurato sulle precisioni e sulle modalità di arrotondamento, e dà risultati correttamente arrotondati, riproducibili e indipendenti dalla piattaforma. Con l’opzione da riga di comando -M, tutti gli operatori aritmetici e le funzioni in virgola mobile possono produrre risultati a ogni livello di precisione supportato da MPFR.
Due variabili predefinite, PREC
e ROUNDMODE
,
danno il controllo sulla precisione di elaborazione e sulla modalità di
arrotondamento. La precisione e la modalità di arrotondamento sono impostate
a livello globale per ogni operazione da eseguire.
Vedi Impostare la precisione e
Impostare modi di arrotondare
per maggiori informazioni.
Successivo: Interi a precisione arbitraria, Precedente: Funzionalità MPFR, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
L’ora di matematica è ostica!
Questa sezione fornisce un quadro dettagliato dei problemi che si presentano quando si eseguono molti calcoli in virgola mobile.102 Le spiegazioni fornite valgono sia per il calcolo in virgola mobile effettuato direttamente dall’hardware del computer, sia per quello ottenuto tramite il software per la precisione arbitraria.
ATTENZIONE: Le informazioni fornite in questa sede sono deliberatamente di tipo generale. Se si devono eseguire calcoli complessi col computer, si dovrebbero prima ottenere ulteriori informazioni, e non basarsi solo su quanto qui detto.
• Inesattezza nei calcoli: | La matematica in virgola mobile non è esatta. | |
• Ottenere la precisione: | Ottenere più precisione richiede qualche sforzo. | |
• Tentare di arrotondare: | Aggiungere cifre di precisione e arrotondare. | |
• Impostare la precisione: | Come impostare la precisione. | |
• Impostare modi di arrotondare: | Impostare le modalità di arrotondamento. |
Successivo: Ottenere la precisione, Su: Cautela col calcolo in VM [Contenuti][Indice]
Le rappresentazioni e i calcoli con numeri a virgola mobile binari sono inesatti. Semplici valori come 0,1 non possono essere rappresentati in modo preciso usando numeri a virgola mobile binari, e la limitata precisione dei numeri a virgola mobile significa che piccoli cambiamenti nell’ordine delle operazioni o la precisione di memorizzazione di operazioni intermedie può cambiare il risultato. Per rendere la situazione più difficile, nel calcolo in virgola mobile con precisione arbitraria, si può impostare la precisione prima di eseguire un calcolo, però non si può sapere con certezza quale sarà il numero di cifre decimali esatte nel risultato finale.
• Rappresentazioni inesatte: | I numeri non sono rappresentati esattamente. | |
• Confronti tra valori in VM: | Come confrontare valori in virgola mobile. | |
• Gli errori si sommano: | Gli errori diventano sempre maggiori. |
Successivo: Confronti tra valori in VM, Su: Inesattezza nei calcoli [Contenuti][Indice]
Perciò, prima di iniziare a scrivere del codice, si dovrebbe pensare al risultato che si vuole effettivamente ottenere e a cosa realmente accade. Si considerino i due numeri nel seguente esempio:
x = 0.875 # 1/2 + 1/4 + 1/8 y = 0.425
Diversamente dal numero in y
, il numero memorizzato in x
è rappresentabile esattamente nel formato binario, perché può essere
scritto come somma finita di una o più frazioni i cui denominatori sono tutti
multipli di due.
Quando gawk
legge un numero a virgola mobile dal sorgente di un
programma, arrotonda automaticamente quel numero alla precisione, quale che
sia, supportata dal computer in uso. Se si tenta di stampare il contenuto
numerico di una variabile usando una stringa di formato in uscita di
"%.17g"
, il valore restituito può non essere lo stesso numero assegnato
a quella variabile:
$ gawk 'BEGIN { x = 0.875; y = 0.425 > printf("%0.17g, %0.17g\n", x, y) }' -| 0.875, 0.42499999999999999
Spesso l’errore è talmente piccolo da non essere neppure notato, e se è stato
notato, si può sempre specificare il grado di precisione si vuole nell’output.
In genere questo è una stringa di formato come "%.15g"
che, se usata
nell’esempio precedente, dà luogo a un output identico all’input.
Successivo: Gli errori si sommano, Precedente: Rappresentazioni inesatte, Su: Inesattezza nei calcoli [Contenuti][Indice]
Poiché la rappresentazione interna del computer può discostarsi, sia pur di poco, dal valore esatto, confrontare dei valori a virgola mobile per vedere se sono esattamente uguali è generalmente una pessima idea. Questo è un esempio in cui tale confronto non funziona come dovrebbe:
$ gawk 'BEGIN { print (0.1 + 12.2 == 12.3) }' -| 0
Il metodo generalmente seguito per confrontare valori a virgola mobile consiste nel controllare se la differenza tra loro è minore di un certo valore (chiamato delta, o tolleranza). Quel che si deve decidere è qual è il valore minimo di delta adeguato. Il codice per far ciò è qualcosa del genere:
delta = 0.00001 # per esempio differenza = abs(a) - abs(b) # sottrazione dei due valori if (differenza < delta) # va bene else # non va bene
(Si presuppone che sia stata definita in qualche parte del programma una
semplice funzione che restituisce il valore assoluto di un numero,
chiamata abs()
.)
Precedente: Confronti tra valori in VM, Su: Inesattezza nei calcoli [Contenuti][Indice]
La perdita di accuratezza in un singolo calcolo con numeri a virgola mobile generalmente non dovrebbe destare preoccupazione. Tuttavia, se si calcola un valore che è una sequenza di operazioni in virgola mobile, l’errore si può accumulare e influire sensibilmente sul risultato del calcolo stesso. Qui sotto vediamo un tentativo di calcolare il valore di pi usando una delle sue rappresentazioni come somma di una serie di numeri:
BEGIN { x = 1.0 / sqrt(3.0) n = 6 for (i = 1; i < 30; i++) { n = n * 2.0 x = (sqrt(x * x + 1) - 1) / x printf("%.15f\n", n * x) } }
Quando viene eseguito, gli errori iniziali si propagano nei calcoli successivi, facendo terminare il ciclo prematuramente dopo un tentativo di divisione per zero:
$ gawk -f pi.awk -| 3.215390309173475 -| 3.159659942097510 -| 3.146086215131467 -| 3.142714599645573 … -| 3.224515243534819 -| 2.791117213058638 -| 0.000000000000000 error→ gawk: pi.awk:6: fatale: tentativo di dividere per zero
Ecco un altro esempio in cui l’inaccuratezza nelle rappresentazioni interne porta a un risultato inatteso:
$ gawk 'BEGIN { > for (d = 1.1; d <= 1.5; d += 0.1) # esegue il ciclo cinque volte (?) > i++ > print i > }' -| 4
Successivo: Tentare di arrotondare, Precedente: Inesattezza nei calcoli, Su: Cautela col calcolo in VM [Contenuti][Indice]
Può il calcolo con precisione arbitraria dare risultati esatti? Non ci sono risposte facili. Le regole standard dell’algebra spesso non valgono nei calcoli con precisione arbitraria. Tra le altre cose, le leggi distributiva e associativa non sono rispettate completamente, e l’ordine dell’operazione può essere importante per il calcolo. Errori di arrotondamento, perdite di precisione che si accumulano, e valori molto vicini allo zero sono spesso causa di problemi.
Quando gawk
verifica l’eguaglianza delle espressioni
‘0.1 + 12.2’ e ‘12.3’ usando l’aritmetica a doppia precisione della
macchina, decide che non sono uguali! (Vedi Confronti tra valori in VM.)
Si può ottenere il risultato cercato aumentando la precisione; 56 bit in
questo caso sono sufficienti:
$ gawk -M -v PREC=56 'BEGIN { print (0.1 + 12.2 == 12.3) }' -| 1
Se aggiungere più bit è una buona cosa, aggiungerne ancora di più
è meglio?
Ecco cosa succede se si usa un valore di PREC
ancora più alto:
$ gawk -M -v PREC=201 'BEGIN { print (0.1 + 12.2 == 12.3) }' -| 0
Non è un bug di gawk
o della libreria MPFR.
È facile dimenticare che il numero finito di bit usato per memorizzare il
valore spesso è solo un’approssimazione dopo un opportuno arrotondamento. Il
test di uguaglianza riesce se e solo se tutti i bit dei due operandi
sono esattamente gli stessi. Poiché questo non è necessariamente vero dopo un
calcolo in virgola mobile con una determinata precisione e con una modalità di
arrotondamento valida, un test di eguaglianza convenzionale potrebbe non
riuscire. Invece, il test riesce confrontando i due numeri per vedere se la
differenza tra di loro rientra in un delta accettabile.
In applicazioni dove sono sufficienti fino a 15 cifre decimali, il calcolo in doppia precisione eseguito dall’hardware del computer può essere una buona soluzione, e in genere è più veloce. Però bisogna tener presente che ogni operazione in virgola mobile può subire un nuovo errore di arrotondamento con conseguenze catastrofiche, come si è visto nel precedente tentativo di calcolare il valore di pi. In tali casi una precisione supplementare può aumentare la stabilità e l’accuratezza del calcolo.
Oltre a ciò, bisogna tenere conto del fatto che addizioni ripetute non sono necessariamente equivalenti a una moltiplicazione nell’aritmetica in virgola mobile. Nell’esempio visto in Gli errori si sommano:
$ gawk 'BEGIN { > for (d = 1.1; d <= 1.5; d += 0.1) # ciclo eseguito cinque volte (?) > i++ > print i > }' -| 4
non è detto che, scegliendo per PREC
un valore arbitrariamente alto, si
riesca a ottenere il risultato corretto. La riformulazione del problema in
questione è spesso il modo corretto di comportari in tali situazioni.
Successivo: Impostare la precisione, Precedente: Ottenere la precisione, Su: Cautela col calcolo in VM [Contenuti][Indice]
Invece dell’aritmetica in virgola mobile con precisione arbitraria, spesso tutto ciò di cui si ha bisogno è un aggiustamento della logica o di un diverso ordine delle operazioni nei calcoli. La stabilità e l’accuratezza del calcolo di pi nel primo esempio possono essere migliorata usando la seguente semplice trasformazione algebrica:
(sqrt(x * x + 1) - 1) / x ≡ x / (sqrt(x * x + 1) + 1)
Dopo aver fatto questo cambiamento, il programma converge verso pi in meno di 30 iterazioni:
$ gawk -f pi2.awk -| 3.215390309173473 -| 3.159659942097501 -| 3.146086215131436 -| 3.142714599645370 -| 3.141873049979825 … -| 3.141592653589797 -| 3.141592653589797
Successivo: Impostare modi di arrotondare, Precedente: Tentare di arrotondare, Su: Cautela col calcolo in VM [Contenuti][Indice]
gawk
usa una precisione di lavoro a livello globale; non tiene
traccia della precisione e accuratezza dei singoli numeri. Eseguendo
un’operazione aritmetica o chiamando una funzione predefinita, il risultato
viene arrotondato alla precisione di lavoro. La precisione di lavoro di default
è di 53 bit, modificabile usando la variabile predefinita PREC
. Si può
anche impostare il valore a una delle stringhe predefinite (non importa se
scritte in maiuscolo o minuscolo) elencate in
Tabella 15.3,
per emulare un formato binario che segue lo standard IEEE 754.
PREC | formato binario IEEE 754 |
---|---|
"half" | 16-bit mezza precisione |
"single" | 32-bit singole precisione di base |
"double" | 64-bit doppia precisione di base |
"quad" | 128-bit quadrupla precisione di base |
"oct" | 256-bit ottupla precisione |
Tabella 15.3: Stringhe di precisione predefinita per PREC
Il seguente esempio illustra gli effetti del cambiamento di precisione sulle operazioni aritmetiche:
$ gawk -M -v PREC=100 'BEGIN { x = 1.0e-400; print x + 0 > PREC = "double"; print x + 0 }' -| 1e-400 -| 0
ATTENZIONE: Diffidare delle costanti in virgola mobile! Quando si legge una costante in virgola mobile dal codice sorgente di un programma,
gawk
usa la precisione di default (quella del formatodouble
di C), a meno che non venga richiesto, tramite la variabile specialePREC
fornita sulla riga di comando, di memorizzarla internamente come un numero MPFR. Cambiare la precisione tramitePREC
nel testo del programma non cambia la precisione di una costante.Se si deve rappresentare una costante in virgola mobile con una precisione maggiore di quella di default e non è possibile usare un assegnamento a
PREC
da riga di comando, si dovrebbe definire la costante o come stringa, o come numero razionale, ove possibile. L’esempio seguente illustra le differenze tra i diversi modi di stampare una costante in virgola mobile:$ gawk -M 'BEGIN { PREC = 113; printf("%0.25f\n", 0.1) }' -| 0.1000000000000000055511151 $ gawk -M -v PREC=113 'BEGIN { printf("%0.25f\n", 0.1) }' -| 0.1000000000000000000000000 $ gawk -M 'BEGIN { PREC = 113; printf("%0.25f\n", "0.1") }' -| 0.1000000000000000000000000 $ gawk -M 'BEGIN { PREC = 113; printf("%0.25f\n", 1/10) }' -| 0.1000000000000000000000000
Precedente: Impostare la precisione, Su: Cautela col calcolo in VM [Contenuti][Indice]
La variabile ROUNDMODE
permette di controllare a livello di programma
la modalità di arrotondamento.
La corrispondenza tra ROUNDMODE
e le modalità di arrotondamento IEEE
è mostrata in Tabella 15.4.
Modalità di arrotondamento | Nome IEEE | ROUNDMODE |
---|---|---|
Arrotonda al più vicino, o a un numero pari | roundTiesToEven | "N" o "n" |
Arrotonda verso infinito | roundTowardPositive | "U" o "u" |
Arrotonda verso meno infinito | roundTowardNegative | "D" o "d" |
Arrotonda verso zero (troncamento) | roundTowardZero | "Z" o "z" |
Arrotonda al più vicino, o per eccesso | roundTiesToAway | "A" o "a" |
Tabella 15.4: Modalità di arrotondamento in gawk
ROUNDMODE
ha "N"
come valore di default, ovvero si usa la
modalità di arrotondamento IEEE 754 roundTiesToEven
.
In Tabella 15.4, il valore "A"
seleziona
roundTiesToAway
. Questo è applicabile solo se la versione in uso
della libreria MPFR lo supporta; altrimenti, l’impostazione di ROUNDMODE
ad "A"
non ha alcun effetto.
La modalità di default roundTiesToEven
è la più preferita, ma allo
stesso tempo
la meno intuitiva. Questo metodo fa la cosa ovvia per la maggior parte dei
valori, arrotondandoli per eccesso o per difetto alla cifra più prossima.
Per esempio, arrotondando 1.132 alle due cifre decimali si ottiene 1.13,
e 1.157 viene arrotondato a 1.16.
Tuttavia, se si deve arrotondare un valore posto esattamente a metà strada,
le cose non funzionano come probabilmente si insegna a scuola.
In questo caso, il numero è arrotondato alla cifra pari più prossima.
Così arrotondando 0.125 alle due cifre si arrotonda per difetto a 0.12,
ma arrotondando 0.6875 alle tre cifre si arrotonda per eccesso a 0.688.
Probabilmente ci si è già imbattuti in questa modalità di arrotondamento
usando printf
per formattare numeri a virgola mobile.
Per esempio:
BEGIN { x = -4.5 for (i = 1; i < 10; i++) { x += 1.0 printf("%4.1f => %2.0f\n", x, x) } }
produce il seguente output quando viene eseguito sul sistema dell’autore:103
-3.5 => -4 -2.5 => -2 -1.5 => -2 -0.5 => 0 0.5 => 0 1.5 => 2 2.5 => 2 3.5 => 4 4.5 => 4
La teoria che sta dietro alla regola
roundTiesToEven
è che gli arrotondamenti di
valori equidistanti in eccesso e in difetto si distribuiscono più o meno
uniformemente, con la possibile conseguenza che errori di arrotondamento
ripetuti tendono ad annullarsi a vicenda. Questa è la modalità di
arrotondamento di default per funzioni e operatori di calcolo secondo IEEE 754.
Le altre modalità di arrotondamento sono usate raramente. Gli arrotondamenti
verso l’infinito (roundTowardPositive
) e verso il meno infinito
(roundTowardNegative
) vengono spesso usati per eseguire calcoli su
intervalli, dove si adotta questa modalità di arrotondamento per calcolare
i limiti superiore e inferiore per l’intervallo di valori in uscita.
La modalità
roundTowardZero
può essere usata per convertire numeri a virgola mobile
in numeri interi. La modalità di arrotondamento roundTiesToAway
arrotonda il risultato al numero più vicino, e in caso di equidistanza
arrotonda per eccesso.
Qualche esperto di analisi numerica dirà che la scelta dello stile di arrotondamento ha un grandissimo impatto sul risultato finale, e consiglierà di attendere sino al risultato finale dopo ogni arrotondamento. Invece, spesso si possono evitare problemi legati a errori di arrotondamento impostando all’inizio la precisione a un valore sufficientemente maggiore della precisione desiderata, in modo che il cumulo degli errori di arrotondamento non influisca sul risultato finale. Se si ha il dubbio che i risultati del calcolo contengano un’accumulazione di errori di arrotondamento, occorre, per accertare la cosa, controllare se si verifica una differenza significativa nell’output cambiando la modalità di arrotondamento.
Successivo: Problemi virgola mobile POSIX, Precedente: Cautela col calcolo in VM, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
gawk
Quando viene specificata l’opzione -M,
gawk
esegue tutti i calcoli sui numeri interi usando gli interi a
precisione arbitraria della libreria GMP. Qualsiasi numero che appaia come un
intero in un sorgente o in un file-dati è memorizzato come intero a precisione
arbitraria. La dimensione del numero intero ha come limite solo la memoria
disponibile. Per esempio, il seguente programma calcola
5432,
il cui risultato è oltre i limiti degli ordinari valori a virgola mobile a
doppia precisione dei processori:
$ gawk -M 'BEGIN { > x = 5^4^3^2 > print "numero di cifre =", length(x) > print substr(x, 1, 20), "...", substr(x, length(x) - 19, 20) > }' -| numero di cifre = 183231 -| 62060698786608744707 ... 92256259918212890625
Se invece si dovesse calcolare lo stesso valore usando valori a virgola mobile con precisione arbitraria, la precisione necessaria per il risultato corretto (usando la formula ‘prec = 3.322 * dps’) sarebbe 3.322 x 183231, o 608693.
Il risultato di un’operazione aritmetica tra un intero e un valore a virgola mobile è un valore a virgola mobile con precisione uguale alla precisione di lavoro. Il seguente programma calcola l’ottavo termine nella successione di Sylvester104 usando una ricorrenza:
$ gawk -M 'BEGIN { > s = 2.0 > for (i = 1; i <= 7; i++) > s = s * (s - 1) + 1 > print s > }' -| 113423713055421845118910464
Il risultato mostrato differisce dal numero effettivo, 113.423.713.055.421.844.361.000.443, perché la precisione di default di 53 bit non è suffciente per rappresentare esattamente il risultato in virgola mobile. Si può o aumentare la precisione (in questo caso bastano 100 bit), o sostituire la costante in virgola mobile ‘2.0’ con un intero, per eseguire tutti i calcoli usando l’aritmetica con gli interi per ottenere l’output corretto.
A volte gawk
deve convertire implicitamente un intero con precisione
arbitraria in un valore a virgola mobile con precisione arbitraria.
Ciò si rende necessario
principalmente perché la libreria MPFR non sempre prevede l’interfaccia
necessaria per elaborare interi a precisione arbitraria o numeri di tipo
eterogeneo come richiesto da un’operazione o funzione. In tal caso, la
precisione viene impostata al minimo valore necessario per una conversione
esatta, e non viene usata la precisione di lavoro. Se
questo non è quello di cui si ha bisogno o che si vuole, si può ricorrere a un
sotterfugio e convertire preventivamente l’intero in un valore a virgola
mobile, come qui di seguito:
gawk -M 'BEGIN { n = 13; print (n + 0.0) % 2.0 }'
Si può evitare completamente questo passaggio specificando il numero come valore a virgola mobile fin dall’inizio:
gawk -M 'BEGIN { n = 13.0; print n % 2.0 }'
Si noti che, per questo specifico esempio, probabilmente è meglio semplicemente specificare:
gawk -M 'BEGIN { n = 13; print n % 2 }'
Dividendo due interi a precisione arbitraria con ‘/’ o con ‘%’, il
risultato è tipicamente un valore a virgola mobile con precisione arbitraria
(a meno che il risultato non sia un numero intero esatto).
Per eseguire divisioni intere o calcolare moduli con interi a precisione
arbitraria, usare la funzione predefinita
intdiv()
(vedi Funzioni numeriche).
Si può simulare la funzione intdiv()
in awk
standard
usando questa funzione definita dall’utente:
# intdiv --- fa una divisione intera function intdiv(numerator, denominator, result) { split("", result) numerator = int(numerator) denominator = int(denominator) result["quotient"] = int(numerator / denominator) result["remainder"] = int(numerator % denominator) return 0.0 }
Il seguente programma d’esempio, proposto da Katie Wasserman,
usa intdiv()
per
calcolare le cifre di pi al numero di cifre significative
che si è scelto di impostare:
# pi.awk --- calcola le cifre di pi BEGIN { cifre = 100000 due = 2 * 10 ^ cifre pi = due for (m = cifre * 4; m > 0; --m) { d = m * 2 + 1 x = pi * m intdiv(x, d, risultato) pi = risultato["quotient"] pi = pi + due } print pi }
Quando gli fu chiesto dell’algoritmo usato, Katie rispose:
Non è quello più noto ma nemmeno quello più incomprensibile. È la variante di Eulero al metodo di Newton per il calcolo del Pi greco. Si vedano le righe (23) - (25) nel sito: http://mathworld.wolfram.com/PiFormulas.html.
L’algoritmo che ho scritto semplicemente espande il moltiplicare per 2 e lavora dall’espressione più interna verso l’esterno. Ho usato questo per programmare delle calcolatrici HP perché è piuttosto facile da adattare ai dispositivi di scarsa memoria con dimensioni di parola piuttosto piccole. Si veda http://www.hpmuseum.org/cgi-sys/cgiwrap/hpmuseum/articles.cgi?read=899.
Successivo: Sommario virgola mobile, Precedente: Interi a precisione arbitraria, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
Per diverso tempo, awk
ha convertito le stringhe dall’aspetto non
numerico nel valore numerico zero, quando richiesto. Per di più, la
definizione originaria del linguaggio e lo standard POSIX originale prevedevano
che awk
riconoscesse solo i numeri decimali (base 10), e non i numeri
ottali (base 8) o esadecimali (base 16).
Le modifiche nel linguaggio degli standard POSIX 2001 e 2004 possono essere
interpretate nel senso che awk
debba fornire delle funzionalità
aggiuntive. Queste sono:
0xDEADBEEF
). (Da notare: valore dei dati letti,
non costanti facenti parte del codice sorgente.)
Il primo problema è che entrambe le modifiche sono deviazioni evidenti dalla prassi consolidata:
gawk
crede che supportare i valori a virgola mobile
esadecimali, nello specifico, sia sbagliato, e che non sia mai stata intenzione
dell’autore originale di introdurlo nel linguaggio.
Il secondo problema è che il manutentore di gawk
crede che questa
interpretazione dello standard, che richiede una certa dimestichezza col
linguaggio giuridico per essere compresa, non sempre è stata
colta dai normali sviluppatori. In altre parole, “Sappiamo come siete
arrivati sin qui, ma non pensiamo che questo sia il posto dove volete essere.”
Recependo queste argomentazioni, e cercando nel contempo di assicurare la
compatibilità con le versioni precedenti dello standard, lo standard POSIX 2008
ha aggiunto delle formulazioni esplicite per consentire l’uso da parte di
awk
, solo a richiesta, dei valori a virgola mobile esadecimali e
dei valori speciali
“not a number” e infinito.
Sebbene il manutentore di gawk
continui a credere che introdurre
queste funzionalità sia sconsigliabile, ciò nonostante, sui sistemi che
supportano i valori in virgola mobile IEEE, sembra giusto fornire
qualche
possibilità di usare i valori NaN e infinito. La soluzione implementata
in gawk
è questa:
gawk
non
interviene. I valori di stringa sono passati direttamente alla funzione
strtod()
della libreria di sistema, e se quest’ultima restituisce
senza errori un valore numerico,
esso viene usato.105
Per definizione, i risultati non sono portabili su diversi sistemi;
e sono anche piuttosto sorprendenti:
$ echo nanny | gawk --posix '{ print $1 + 0 }' -| nan $ echo 0xDeadBeef | gawk --posix '{ print $1 + 0 }' -| 3735928559
gawk
interpreta i quattro valori di stringa
‘+inf’,
‘-inf’,
‘+nan’
e
‘-nan’
in modo speciale, producendo i corrispondenti valori numerici speciali.
Il segno iniziale serve per segnalare a gawk
(e all’utente)
che il valore è realmente numerico. I numeri a virgola mobile esadecimali
non sono consentiti (a meno di non usare anche --non-decimal-data,
che non è consigliabile). Per esempio:
$ echo nanny | gawk '{ print $1 + 0 }' -| 0 $ echo +nan | gawk '{ print $1 + 0 }' -| nan $ echo 0xDeadBeef | gawk '{ print $1 + 0 }' -| 0
gawk
ignora la distinzione maiuscole/minuscole nei quattro valori
speciali. Così, ‘+nan’ e ‘+NaN’ sono la stessa cosa.
Precedente: Problemi virgola mobile POSIX, Su: Calcolo con precisione arbitraria [Contenuti][Indice]
awk
standard usa valori a virgola mobile
a doppia precisione.
PREC
per impostare la precisione in bit, e
ROUNDMODE
per impostare la modalità di arrotondamento tra quelle
previste nello standard IEEE 754.
gawk
esegue calcoli su interi a precisione
arbitraria usando la libreria GMP. In tal modo si ha una maggiore velocità e
una più efficiente allocazione dello spazio rispetto all’uso di MPFR per
eseguire gli stessi calcoli.
gawk
è in disaccordo con lo standard POSIX.
È importante averlo ben presente.
Successivo: Storia del linguaggio, Precedente: Calcolo con precisione arbitraria, Su: Top [Contenuti][Indice]
gawk
È possibile aggiungere nuove funzioni, scritte in C o C++, a gawk
usando librerie caricate dinamicamente. Questa funzionalità è disponibile
su sistemi che supportano le funzioni C dlopen()
e dlsym()
.
Questo capitolo descrive come creare estensioni usando codice scritto
in C o C++.
Chi è completamente digiuno di programmazione in C può tranquillamente
saltare questo capitolo, ma potrebbe valer la pena di dare un’occhiata
alla documentazione sulle estensioni che sono installate insieme a
gawk
(vedi Esempi di estensione),
e alle informazioni sul progetto gawkextlib
(vedi gawkextlib).
Gli esempi di estensione sono automaticamente compilati e installati quando
si installa gawk
.
NOTA: Se si specifica l’opzione --sandbox, le estensioni non sono disponibili (vedi Opzioni).
• Introduzione alle estensioni: | Cos’è un’estensione. | |
• Licenza delle estensioni: | Una nota riguardo al tipo di licenza. | |
• Panoramica sul meccanismo delle estensioni: | Una panoramica sul meccanismo delle estensioni. | |
• Descrizione dell'API delle estensioni: | Una descrizione completa dell’API. | |
• Trovare le estensioni: | Come gawk trova le estensioni
compilate.
| |
• Esempio di estensione: | Esempio di codice C di un’estensione. | |
• Esempi di estensione: | Le estensioni di esempio incluse con
gawk .
| |
• gawkextlib: | Il progetto gawkextlib .
| |
• Sommario delle estensioni: | Sommario delle estensioni. | |
• Esercizi sulle estensioni: | Esercizi. |
Successivo: Licenza delle estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
Un’estensione (talora chiamata plug-in) è un frammento di codice
compilato esternamente che gawk
può caricare in fase di esecuzione
per ottenere funzionalità ulteriori, che vanno ad aggiungersi a quelle di
gawk
descritte nel resto di questo Documento.
Le estensioni sono utili perché consentono (ovviamente) di estendere le
funzionalità di gawk
. Per esempio, possono permettere l’uso di
chiamate di sistema (come chdir()
per cambiare directory)
e di altre routine di libreria C potenzialmente utili. Come per la maggior
parte del software, “il cielo è il limite”; se si riesce a immaginare
qualcosa che si vuol fare e che è possibile programmare in C o C++,
si può scrivere un’estensione che lo faccia!
Le estensioni sono scritte in C o C++, usando l’API (Application
Programming Interface) definita per questo scopo dagli sviluppatori di
gawk
. Il resto di questo capitolo descrive
le possibilità offerte dall’API e come usarle,
e illustra una piccola estensione di esempio. Inoltre, sono documentati
gli esempi di estensione inclusi nella distribuzione di gawk
e viene descritto il progetto gawkextlib
.
Vedi Progetto delle estensioni, per una disamina degli obiettivi e del
progetto del meccanismo delle estensioni.
Successivo: Panoramica sul meccanismo delle estensioni, Precedente: Introduzione alle estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
Ogni estensione dinamica dev’essere distribuita in base a una licenza che sia compatibile con la licenza GNU GPL (vedi Copia).
Per far sapere a gawk
che la licenza è quella corretta,
l’estensione deve definire il simbolo globale
plugin_is_GPL_compatibile
. Se tale simbolo non è stato definito,
gawk
termina con un messaggio di errore fatale al momento del
caricamente dell’estensione.
Il tipo dichiarato per il suddetto simbolo dev’essere int
. Esso non
deve tuttavia essere presente in ogni sezione allocata.
Il controllo in essere si limita a constatare che quel simbolo esiste a
livello globale.
Qualcosa del genere può essere sufficiente:
int plugin_is_GPL_compatible;
Successivo: Descrizione dell'API delle estensioni, Precedente: Licenza delle estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
La comunicazione tra
gawk
e un’estensione è bidirezionale. Dapprima, quando
un’estensione è caricata, gawk
le passa un puntatore a una struttura
(struct
) i cui campi sono dei puntatori di funzione.
Questo si può vedere in Figura 16.1.
L’estensione è in grado di chiamare funzioni all’interno di gawk
utilizzando questi puntatori a funzione, in fase di esecuzione, senza aver
bisogno di accedere (in fase di compilazione), ai simboli di gawk
.
Uno di questi puntatori a funzione punta a una funzione che serve per
“registrare” nuove funzioni.
Questo è mostrato in Figura 16.2.
Nella direzione opposta, l’estensione registra le sue nuove funzioni
con gawk
passando dei puntatori che puntano alle funzioni che
implementano la nuova funzionalità, (p.es. do_chdir()
). gawk
associa il puntatore a funzione con un nome ed è in grado di chiamarlo in
seguito, usando una convenzione di chiamata predefinita.
Questo è mostrato in Figura 16.3.
La funzione do_xxx()
, a sua volta, utilizza i puntatori a
funzione nella struttura (struct
) API per svolgere il proprio compito,
come aggiornare variabili o vettori, stampare messaggi, impostare la
variabile ERRNO
, e così via.
Delle macro di servizio rendono la chiamata effettuata utilizzando i puntatori simile a quella delle funzioni normali, in modo che il codice sorgente delle estensioni rimanga sufficientemente leggibile e comprensibile.
Sebbene tutto ciò possa sembrare piuttosto complesso, il risultato è che il codice sorgente dell’estensione è abbastanza intuitivo da scrivere e da leggere. Lo si può constatare nell’estensione di esempio filefuncs.c (vedi Esempio di estensione), come pure nel codice testext.c, che testa l’interfaccia di programmazione (API).
Ecco alcuni ulteriori dettagli:
gawk
do_xxx
, che memorizzano opzioni della riga di comando come
do_lint
, do_profiling
, e così via (vedi Variabili dell'estensione API).
Questi valori sono solo informativi: un’estensione non può modificarli
all’interno di gawk
. Oltre a ciò, il tentativo di assegnare loro
dei valori produce un errore quando l’estensione viene compilata.
gawk
, in modo che un’estensione possa controllare se il
comando gawk
che l’ha caricata è in grado di supportare le
funzionalità utilizzate nell’estensione. (Discrepanze tra le versioni
“non dovrebbero” accadere, ma si sa come vanno queste cose.)
Vedi Versione dell'estensione per ulteriori dettagli.
Successivo: Trovare le estensioni, Precedente: Panoramica sul meccanismo delle estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
Il codice sorgente scritto in C o C++ per un’estensione deve includere il
file di intestazione
gawkapi.h, che dichiara le funzioni e definisce i tipi di dati
usati per comunicare con gawk
.
Questa
(non breve) sezione descrive l’API in detttaglio.
• Intro funzioni API delle estensioni: | Introduzione alle funzioni dell’API. | |
• Tipi di dati generali: | I tipi di dati. | |
• Funzioni di allocazione memoria: | Funzioni per allocare memoria. | |
• Funzioni di costruzione: | Funzioni per creare valori. | |
• Funzioni di registrazione: | Funzioni per registrare cose con
gawk .
| |
• Stampare messaggi: | Funzioni per stampare messaggi. | |
• Aggiornare ERRNO : | Funzioni per aggiornare ERRNO .
| |
• Richiedere valori: | Come ottenere un valore. | |
• Accedere ai parametri: | Funzioni per accedere ai parametri. | |
• Accedere alla tabella simboli: | Funzioni per accedere alle variabili globali | |
• Manipolazione di vettori: | Funzioni per lavorare coi vettori. | |
• Ridirezione API: | Come accedere alla ridirezioni e modificarle. | |
• Variabili dell'estensione API: | Variabili fornite dall’API. | |
• Codice predefinito di un'estensione API: | Codice predefinito di interfaccia API. | |
• Modifiche dalla versione API 1: | Modifiche dalla versione 1 dell’API. |
Successivo: Tipi di dati generali, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
L’accesso a funzionalità interne a gawk
è effettuato
con una chiamata che usa i puntatori a funzione resi disponibili
all’estensione.
Puntatori a funzioni API sono previsti per i seguenti tipi di operazioni:
Tutti questi elementi sono spiegati dettagliatamente nel resto di questo capitolo.
ERRNO
o annullarne il valore.
Alcune osservazioni riguardo all’uso dell’API:
Elemento C | File d’intestazione |
---|---|
EOF | <stdio.h> |
valori di errno | <errno.h> |
FILE | <stdio.h> |
NULL | <stddef.h> |
memcpy() | <string.h> |
memset() | <string.h> |
size_t | <sys/types.h> |
struct stat | <sys/stat.h> |
Per ragioni di portabilità, specialmente per sistemi
che non sono interamente aderenti agli standard, occorre assicurarsi di
includere i file corretti nel modo corretto. Questa richiesta mira
a mantenere il file gawkapi.h ordinato, invece che farlo diventare
un’accozzaglia di problemi di portabilità, quale si può vedere in alcune
parti del codice sorgente di gawk
.
inline
. Se il compilatore in
uso non supporta questa parola chiave, si dovrebbe specificare sulla
riga di comando il parametro ‘-Dinline=''’ oppure usare gli strumenti
Autotools GNU e includere un file
config.h nel codice sorgente delle estensioni.
gawk
puntano ad aree
di memoria gestite da gawk
e dovrebbero essere trattati
dall’estensione come in sola lettura. Le aree di memoria che contengono tutte le stringhe passate a
gawk
dall’estensione devono provenire da una chiamata a
gawk_malloc()
, gawk_calloc()
o gawk_realloc()
,
e sono gestite da gawk
da quel punto in avanti.
struct
che mappano dei valori
come sono visti da awk
. Un valore può essere un numero double
(a virgola mobile, in doppia precisione), una stringa o un
vettore (come è il caso per i vettori multidimensionali o nella creazione di
un nuovo vettore).
I valori di tipo stringa sono costituiti da un puntatore e da una lunghezza, poiché nella stringa possono essere presenti dei caratteri NUL (zeri binari, che normalmente marcano la fine di una stringa).
NOTA: Di proposito,
gawk
immagazzina le stringhe usando la codifica multibyte correntemente in uso (come definita dalle variabili d’ambienteLC_xxx
) e non usando dei caratteri larghi (ovvero due byte per ogni carattere). Ciò riflette il modo con cuigawk
memorizza le stringhe internamente, e anche il modo in cui i caratteri sono verosimilmente letti dai file in input e scritti nei file in output.
NOTA: I valori di una stringa passati a un’estensione da
gawk
hanno sempre un carattere NUL alla fine (come delimitatore). Quindi è possibile usare senza inconvenienti tali valori di stringa per chiamare funzioni di libreria standard e routine di sistema. Tuttavia, poichégawk
consente che all’interno di una stringa di dati possano essere presenti caratteri NUL, si dovrebbe controllare che la lunghezza di ogni stringa passata un’estensione coincida con il valore restituito dalla funzionestrlen()
per la stringa stessa.
In ogni caso, se la richiesta e il tipo effettivo della variabile non corrispondono, la funzione di accesso restituisce “false” e fornisce il tipo proprio della variabile, in modo che l’estensione possa, p.es., stampare un messaggio di errore (del tipo “ricevuto uno scalare, invece del vettore previsto”).
Si possono chiamare le funzioni dell’API usando i puntatori a funzione direttamente, ma l’interfaccia non è molto elegante. Per permettere al codice sorgente delle estensioni di assomigliare di più a un codice normale, il file di intestazione gawkapi.h definisce parecchie macro da usare nel codice sorgente dell’estensione. Questa sezione presenta le macro come se si trattasse di funzioni.
Successivo: Funzioni di allocazione memoria, Precedente: Intro funzioni API delle estensioni, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
Ho un vero rapporto di amore/odio con le unioni.
Questo è ciò che contraddistingue le unioni: il compilatore è in grado di accomodare le cose in modo da far coesistere amore e odio.
L’estensione API definisce un certo numero di semplici tipi di dato e strutture di uso generale. Ulteriori strutture di dati, più specializzate, saranno introdotte nelle successive sezioni, insieme alle funzioni che ne fanno uso.
I tipi di dati e le strutture di uso generale sono le seguenti:
typedef void *awk_ext_id_t;
Un valore di questo tipo è trasmesso da gawk
a un’estensione nel
momento in cui viene caricata. Tale valore dev’essere restituito
a gawk
come primo parametro di ogni funzione API.
#define awk_const …
Questa macro genera delle ‘costanti’ nel momento in cui si compila
un’estensione, e non genera nulla quando si compila il comando gawk
vero e proprio. Ciò rende alcuni
campi nelle strutture dei dati dell’API non alterabili dal codice sorgente
dell’estensione, ma consente al comando gawk
di usarle secondo
necessità.
typedef enum awk_bool {
awk_false = 0,
awk_true
} awk_bool_t;
Un semplice tipo di variabile booleana.
typedef struct awk_string {
char *str; /* dati veri e propri */
size_t len; /* lunghezza degli stessi, in caratteri */
} awk_string_t;
Questo rappresenta una stringa modificabile. gawk
è responsabile
per la gestione della memoria utilizzata, se ha fornito il valore della
stringa. Altrimenti, assume il possesso della memoria in questione.
Questa memoria dev’essere resa disponibile chiamando una delle funzioni
gawk_malloc()
, gawk_calloc()
o gawk_realloc()
!
Come già detto, la rappresentazione delle stringhe in memoria usa la codifica multibyte corrente.
typedef enum {
AWK_UNDEFINED,
AWK_NUMBER,
AWK_STRING,
AWK_REGEX,
AWK_STRNUM,
AWK_ARRAY,
AWK_SCALAR, /* accesso opaco a una variabile */
AWK_VALUE_COOKIE /* per aggiornare un valore
già creato */
} awk_valtype_t;
L’elenco enum
indica di che tipo è un certo valore.
È usato nella seguente struttura struct
.
typedef struct awk_value {
awk_valtype_t val_type;
union {
awk_string_t s;
double d;
awk_array_t a;
awk_scalar_t scl;
awk_value_cookie_t vc;
} u;
} awk_value_t;
Un “valore di awk
”.
Il campo val_type
indica che tipo di valore union
contiene,
e ogni campo è del tipo appropriato.
#define str_value u.s
#define strnum_value str_value
#define regex_value str_value
#define num_value u.d
#define array_cookie u.a
#define scalar_cookie u.scl
#define value_cookie u.vc
L’uso di queste macro rende più facile da seguire l’accesso ai campi di
awk_value_t
.
typedef void *awk_scalar_t;
La variabili scalari possono essere rappresentate da un tipo opaco. Questi
valori sono ottenuti da gawk
e in seguito gli vengono restituiti.
Questo argomento è discusso in maniera generale nel testo che segue questa
lista, e più in dettaglio
in
Tabella simboli tramite cookie.
typedef void *awk_value_cookie_t;
Un “value cookie” è un tipo di variabile opaca, e rappresenta un valore nascosto. Anche questo argomento è discusso in maniera generale nel testo che segue questa lista, e più in dettaglio in Valori nascosti.
I valori di tipo scalare in awk
sono numeri, stringhe, strnum
o regexp fortemente tipizzate.
La struttura awk_value_t
rappresenta valori.
Il campo val_type
indica cosa contiene union
.
Rappresentare numeri è facile: l’API usa una variabile C di tipo
double
. Le stringhe richiedono
uno sforzo maggiore. Poiché
gawk
consente che le stringhe contengano dei byte NUL
(a zeri binari) nel valore di una stringa, una stringa dev’essere
rappresentata da una coppia di campi che contengono il puntatore al dato vero
e proprio e la lunghezza della stringa.
È questo è il tipo awk_string_t
.
Un valore di tipo strnum (stringa numerica) è rappresentato come una
stringa e consiste di dati in input forniti dall’utente che appaiono essere
numerici.
Quando una funzione di estensione crea un valore di tipo strnum, il
risultato è una stringa che viene marcata come immessa dall’utente. La
successiva analisi da parte di gawk
servirà poi a determinare se
la stringa appare essere un numero, e va quindi trattata come strnum,
invece che come una normale stringa di caratteri.
Ciò è utile nei casi un cui una funzione di estensione desideri fare qualcosa
di paragonabile alla funzione split
, la quale imposta l’attributo
di strnum agli elementi di vettore che crea.
Per esempio, un’estensione che implementi la divisione di record CSV
(Comma Separated Values, i cui elementi sono delimitati da virgole)
potrebbe voler usare questa funzionalità. Un’altra situazione in cui ciò
è utile è quello di una funzione che richieda campi-dati ad una banca di
dati. La funzione PQgetvalue()
della banca dati PostgreSQ, per
esempio, restituisce una stringa che può essere numerica o di tipo carattere,
a seconda del contesto.
I valori di regexp fortemente tipizzate
(vedi Costanti regexp forti non sono molto utili nelle funzioni di
estensione. Le funzioni di estensione possono stabilire di averli ricevuti,
e crearne, attribuendo valori di tipo scalare. In alternativa, è possibile
esaminare il testo della regexp utilizzando campi regex_value.str
e regex_value.len
.
Identificativi (cioè, nomi di variabili globali) possono essere
associati sia a valori scalari che a vettori. Inoltre, gawk
consente veri vettori di vettori, in cui ogni singolo elemento di un vettore
può a sua volta essere un vettore. La spiegazione dei vettori è rinviata
a
Manipolazione di vettori.
La varie macro sopra elencate facilitano l’uso degli elementi delle
union
come se
fossero campi in una struct
; è questa una pratica comunemente adottata
nella scrittura di programmi in C. Questo tipo di codice è più semplice da
scrivere e da leggere, ma resta una responsabilità del programmatore
assicurarsi che il campo val_type
rifletta correttamente il tipo
del valore contenuto nella struttura awk_value_t
.
Dal punti di vista concettuale, i primi tre campi dell’union
(numero,
stringa, e vettore) sono sufficienti per lavorare con i valori awk
.
Tuttavia, poiché l’API fornisce routine per ottenere e modificare
il valore di una variabile scalare globale usando solo il nome della
variabile, si ha qui una perdita di efficienza: gawk
deve cercare
la variabile ogni volta che questa è utilizzata e modificata. Questo
è un probelma reale, non solo un problema teorico.
Per questo motivo, se si sa che una certa estensione passerà molto tempo
a leggere e/o modificare il valore di una o più variabili scalari, si può
ottenere uno scalar cookie106
per quella variabile, e poi usare
il cookie per ottenere il valore della variabile o per modificarne il
valore.
Il tipo awk_scalar_t
contiene uno scalar cookie, e la macro
scalar_cookie
fornisce accesso al valore di quel tipo
nella struttura awk_value_t
.
Dato uno scalar cookie, gawk
può trovare o modificare
direttamente il valore, come richiesto, senza bisogno di andarlo
a cercare ogni volta.
Il tipo awk_value_cookie_t
e la macro value_cookie
sono simili.
Se si pensa di dover usare
lo stesso valore numerico o la stessa stringa per una o più
variabili, si può creare il valore una volta per tutte, mettendo da parte un
value cookie per quel valore, e in seguito specificare quel
value cookie quando si desidera impostare il valore di una variabile.
Ciò consente di risparmiare spazio in memoria all’interno del processo
di gawk
e riduce il tempo richiesto per creare il valore.
Successivo: Funzioni di costruzione, Precedente: Tipi di dati generali, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
L’API fornisce alcune funzioni per effettuare allocazioni di memoria
che possono essere passate a gawk
, e anche un certo numero di
macro che possono tornare utili.
Questa
sottosezione le presenta come prototipi di funzione, nel modo
con cui il codice dell’estensione potrebbe usarle:
void *gawk_malloc(size_t size);
Chiama la versione corretta di malloc()
per allocare memoria,
che può in seguito essere messa a disposizione di gawk
.
void *gawk_calloc(size_t nmemb, size_t size);
Chiama la versione corretta di calloc()
per allocare memoria che
che può in seguito essere messa a disposizione di gawk
.
void *gawk_realloc(void *ptr, size_t size);
Chiama la versione corretta di realloc()
per allocare memoria
che può in seguito essere messa a disposizione di gawk
.
void gawk_free(void *ptr);
Chiama la versione corretta di free()
per liberare memoria che
era stata allocata con
gawk_malloc()
, gawk_calloc()
o gawk_realloc()
.
L’API deve fornire queste funzioni perché è possibile
che un’estensione sia stata compilata e costruita usando una versione
diversa della libreria C rispetto a quella usata per il programma eseguibile
gawk
.107
Se gawk
usasse la propria versione di free()
per liberare
della memoria acquisita tramite una differente versione di malloc()
,
il risultato sarebbe molto probabilmente differente da quello atteso.
Due macro di utilità possono essere usate per allocare memoria
tramite gawk_malloc()
e
gawk_realloc()
. Se l’allocazione non riesce, gawk
termina l’esecuzione con un messaggio di errore fatale.
Queste macro dovrebbero essere usate come se fossero dei richiami a
procedure che non restituiscono un codice di ritorno:
#define emalloc(pointer, type, size, message) …
Gli argomenti per questa macro sono i seguenti:
pointer
La variabile di tipo puntatore che punterà alla memoria allocata.
type
Il tipo della variabile puntatore. Questo è usato per definire il tipo
quando si chiama gawk_malloc()
.
size
Il numero totale di byte da allocare.
message
Un messaggio da anteporre all’eventuale messaggio di errore fatale. Questo è solitamente il nome della funzione che sta usando la macro.
Per esempio, si potrebbe allocare il valore di una stringa così:
awk_value_t risultato; char *message; const char greet[] = "non v'allarmate!"; emalloc(message, char *, sizeof(greet), "myfunc"); strcpy(message, greet); make_malloced_string(message, strlen(message), & risultato);
#define erealloc(pointer, type, size, message) …
Questo è simile a emalloc()
, ma chiama gawk_realloc()
invece che gawk_malloc()
.
Gli argomenti sono gli stessi della macro emalloc()
.
Successivo: Funzioni di registrazione, Precedente: Funzioni di allocazione memoria, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
L’API fornisce varie funzioni di costruzione per creare valori di tipo stringa e di tipo numerico, e anche varie macro di utilità. Questa sottosezione le presenta tutte come prototipi di funzione, nel modo in cui il codice sorgente di un’estensione le userebbe:
static inline awk_value_t *
make_const_string(const char *stringa, size_t lunghezza, awk_value_t *risultato);
Questa funzione mette il valore di una stringa nella variabile
awk_value_t
puntata da risultato
. La funzione presuppone che
stringa
sia una costante stringa C
(o altri dati che formano una stringa), e automaticamente crea una
copia dei dati che sarà immagazzinata in risultato
.
Viene restituito il puntatore risultato
.
static inline awk_value_t *
make_malloced_string(const char *stringa, size_t lunghezza, awk_value_t *risultato);
Questa funzione mette il valore di una stringa nella variabile
awk_value_t
puntata da risultato
. Si presuppone che
stringa
sia un valore ‘char *’
che punta a dati ottenuti in precedenza per mezzo di
gawk_malloc()
, gawk_calloc()
o gawk_realloc()
.
L’idea è che questi dati siano comunicati direttamente a gawk
,
che se ne assume la responsabilità.
Viene restituito il puntatore risultato
.
static inline awk_value_t *
make_null_string(awk_value_t *risultato);
Questa funzione specializzata crea una stringa nulla (il valore “undefined”)
nella variabile awk_value_t
puntata da risultato
.
Viene restituito il puntatore risultato
.
static inline awk_value_t *
make_number(double num, awk_value_t *risultato);
Questa funzione crea semplicemente un valore numerico nella variabile
awk_value_t
, puntata da risultato
.
static inline awk_value_t *
make_const_user_input(const char *stringa, size_t lunghezza, awk_value_t *risultato);
Questa funzione è identica a make_const_string()
, ma la stringa è
marcata come input dell’utente, che dovrà essere trattata come strnum
se il contenuto della stringa appare essere numerico.
static inline awk_value_t *
make_malloced_user_input(const char *stringa, size_t lunghezza, awk_value_t *risultato);
Questa funzione è identica a make_malloced_string()
, ma la stringa è
marcata come input dell’utente, che dovrà essere trattata come strnum
se il contenuto della stringa appare essere numerico.
static inline awk_value_t *
make_const_regex(const char *stringa, size_t lunghezza, awk_value_t *risultato);
Questa funzione crea un valore di regexp fortemente tipizzata, allocando una
copia della stringa.
stringa
è l’espressione regolare, di lunghezza lunghezza
.
static inline awk_value_t *
make_malloced_regex(const char *stringa, size_t lunghezza, awk_value_t *risultato);
Questa funzione crea un valore di regexp fortemente tipizzata.
stringa
è l’espressione regolare, di lunghezza lunghezza
.
Si aspetta che stringa
sia un valore di tipo ‘char *’ che punta a
dati ottenuti in precedenza tramite una chiamata a
gawk_malloc()
, gawk_calloc()
o gawk_realloc()
.
Successivo: Stampare messaggi, Precedente: Funzioni di costruzione, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
Questa sezione descrive le funzioni dell’API per
registrare parti di un’estensione con gawk
.
• Funzioni di estensione: | Registrare funzioni di estensione. | |
• Funzioni di exit callback: | Registrare una exit di callback. | |
• Stringa di versione Estensioni: | Registrare una stringa di versione. | |
• Analizzatori di input: | Registrare un analizzatore di input. | |
• Processori di output: | Registrare un processore di output. | |
• Processori bidirezionali: | Registrare un processore bidirezionale. |
Successivo: Funzioni di exit callback, Su: Funzioni di registrazione [Contenuti][Indice]
Le funzioni di estensione sono descritte dal seguente tracciato record:
typedef struct awk_ext_func { const char *name; awk_value_t *(*const function)(int num_actual_args, awk_value_t *risultato, struct awk_ext_func *finfo); const size_t max_expected_args; const size_t min_required_args; awk_bool_t suppress_lint; void *data; /* puntatore di tipo opaco a ogni informazione ulteriore */ } awk_ext_func_t;
I campi sono:
const char *name;
Il nome della nuova funzione.
Il codice sorgente a livello di awk
richiama la funzione usando
questo nome.
Il nome è una normale stringa di caratteri del linguaggio C.
I nomi di funzione devono rispettare le stesse regole che valgono per gli
identificativi awk
.
Cioè, devono iniziare o con una lettera dell’alfabeto inglese o con un
trattino basso, che possono essere seguiti da un numero qualsiasi di
lettere, cifre o trattini bassi.
L’uso di maiuscolo/minuscolo è significativo.
awk_value_t *(*const function)(int num_actual_args,
awk_value_t *risultato,
struct awk_ext_func *finfo);
Questo è un puntatore alla funzione C che fornisce la funzionalità per cui
è stata scritta l’estensione.
La funzione deve riempire l’area di memoria puntata da *risultato
con
un numero, con una stringa, oppure con una regexp.
gawk
diventa il proprietario di tutte le stringhe di memoria.
Come già detto sopra, la stringa di memoria deve essere stata ottenuta
usando gawk_malloc()
, gawk_calloc()
o gawk_realloc()
.
L’argomento num_actual_args
dice alla funzione scritta in C quanti
parametri sono stati effettivamente passati dal codice chiamante all’interno
di awk
.
La funzione deve restituire il valore di risultato
.
Questo è per utilità del codice chiamante all’interno di
gawk
.
size_t max_expected_args;
Questo è il massimo numero di argomenti che la funzione si aspetta di
ricevere.
Se chiamata con un numero di argomenti maggiore di questo, e se è stato
richiesto il controllo lint, gawk
stampa un messaggio di
avvertimento. Per ulteriori informazioni, si veda la descrizione di
suppress_lint
, più avanti in questa lista.
const size_t min_required_args;
Questo è il minimo numero di argomenti che la funzione si aspetta di
ricevere.
Se è chiamata con un numero inferiore di argomenti, gawk
stampa un messaggio di errore fatale ed esce.
awk_bool_t suppress_lint;
Questo flag dice a gawk
di non stampare un messaggio
lint se è stato richiesto un controllo lint e se sono stati
forniti più argomenti di quelli attesi. Una funzione di estensione può
stabilire se gawk
ha già stampato almeno uno di tali messaggi
controllando se ‘num_actual_args > finfo->max_expected_args’.
In tal caso, se la funzione non desidera la stampa di ulteriori messaggi,
dovrebbe impostare finfo->suppress_lint
a awk_true
.
void *data;
Questo è un puntatore di tipo opaco a tutti quei dati che una funzione
di estensione desidera avere disponibili al momento della chiamata.
Passando alla funzione di estensione la struttura awk_ext_func_t
e avendo al suo interno questo puntatore disponibile, rende possibile
scrivere un’unica funzione C o C++ che implementa più di una funzione
di estensione a livello awk
.
Una volta preparato un record che descrive l’estensione, la funzione di
estensione va registrata con gawk
usando questa funzione dell’API:
awk_bool_t add_ext_func(const char *namespace, awk_ext_func_t *func);
Questa funzione restituisce il valore true se ha successo,
oppure false in caso contrario.
Il parametro namespace
non è usato per ora; dovrebbe puntare a una
stringa vuota (""
). Il puntatore func
è l’indirizzo di una
struct
che rappresenta la funzione stessa, come descritto sopra.
gawk
non modifica ciò che è puntato da func
, ma la
funzione di estensione stessa riceve questo puntatore e può modificarlo
e farlo puntare altrove, quindi il puntatore non è stato dichiarato
di tipo costante (const
).
La combinazione di min_required_args
, max_expected_args
,
e suppress_lint
può ingenerare confusione. Ecco delle linee-guida
sul da farsi.
Impostare min_required_args
and max_expected_args
a zero e
impostare suppress_lint
ad awk_true
.
Impostare min_required_args
al minimo richiesto.
Impostare max_expected_args
a zero e
impostare suppress_lint
ad awk_true
.
Impostare min_required_args
al minimo richiesto.
Impostare max_expected_args
al massimo atteso.
Impostare suppress_lint
ad awk_false
.
Impostare min_required_args
al minimo richiesto.
Impostare max_expected_args
al massimo atteso.
Impostare suppress_lint
ad awk_false
.
Nella funzione di estensione, controllare che num_actual_args
non
ecceda f->max_expected_args
. Se il massimo è superato, stampare
un messaggio di errore fatale.
Successivo: Stringa di versione Estensioni, Precedente: Funzioni di estensione, Su: Funzioni di registrazione [Contenuti][Indice]
Una funzione exit callback è una funzione che gawk
invoca
prima di completare l’esecuzione del programma.
Siffatte funzioni sono utili se ci sono dei compiti generali di “pulizia”
che dovrebbero essere effettuati nell’estensione (come chiudere connessioni a
un database o rilasciare altre risorse).
Si può registrare una tale
funzione con gawk
per mezzo della seguente funzione:
void awk_atexit(void (*funcp)(void *data, int exit_status),
void *arg0);
I parametri sono:
funcp
Un puntatore alla funzione da chiamare prima che gawk
completi
l’esecuzione. Il parametro data
sarà il valore originale di arg0
.
Il parametro exit_status
è il valore del codice di ritorno che
gawk
intende passare alla chiamata di sistema exit()
(che termina l’esecuzione del programma).
arg0
Un puntatore a un’area dati privata che gawk
mantiene perché
sia poi passata alla funzione puntata da funcp
.
Le funzioni exit callback sono chiamate in ordine inverso rispetto
a quello con cui è stata fatta la registrazione con gawk
(LIFO: Last In, First Out).
Successivo: Analizzatori di input, Precedente: Funzioni di exit callback, Su: Funzioni di registrazione [Contenuti][Indice]
Si può registrare una stringa di versione che indica il nome e la versione
di una data estensione a gawk
, come segue:
void register_ext_version(const char *version);
Registra la stringa puntata da version
con gawk
.
Si noti che gawk
non copia la stringa version
, e
quindi questa stringa non dovrebbe essere modificata.
gawk
stampa tutte le stringhe con le versioni di estensione
registrate, quando viene invocato specificando l’opzione --version.
Successivo: Processori di output, Precedente: Stringa di versione Estensioni, Su: Funzioni di registrazione [Contenuti][Indice]
Per default, gawk
legge file di testo come input. Il valore della
variabile RS
è usato per determinare la fine di un record, e subito
dopo la variabile FS
(o FIELDWIDTHS
o FPAT
) viene usata
per suddividerlo in campi
(vedi Leggere file).
Viene inoltre impostato il valore di RT
(vedi Variabili predefinite).
Se lo si desidera, è possibile fornire un analizzatore di input
personalizzato. Il compito di un analizzatore di input è di restituire un
record al codice di gawk
, che poi lo elaborerà, accompagnato,
se necessario, da indicatori del valore e della lunghezza dei dati da usare
per RT
.
Per fornire un analizzatore personalizzato di input, occorre innanzitutto rendere disponibili due funzioni (dove XXX è un nome che fa da prefisso all’estensione intera):
awk_bool_t XXX_can_take_file(const awk_input_buf_t *iobuf);
Questa funzione esamina l’informazione disponibile in iobuf
(che vedremo tra poco). Basandosi su tale informazione,
decide se l’analizzatore di input personalizzato andrà usato per questo file.
Se questo è il caso, dovrebbe restituire true. Altrimenti, restituirà
false. Nessuno stato (valori di variabili, etc.) dovrebbe venire
modificato all’interno di gawk
.
awk_bool_t XXX_take_control_of(awk_input_buf_t *iobuf);
Quando gawk
decide di passare il controllo del file a questo
analizzatore di input, richiamerà questa funzione.
Questa funzione a sua volta deve assegnare un valore ad alcuni campi
nella struttura awk_input_buf_t
e assicurarsi che
alcune condizioni siano verificate. Dovrebbe poi restituire true.
Se si verifica un errore di qualche tipo, i campi in questione non dovrebbero
venire riempiti, e la funzione dovrebbe restituire false; in questo caso
gawk
non utilizzerà più l’analizzatore personalizzato di input.
I dettagli sono descritti più avanti.
L’estensione dovrebbe raccogliere queste funzioni all’interno di una
struttura awk_input_parser_t
, simile a questa:
typedef struct awk_input_parser { const char *name; /* nome dell'analizzatore */ awk_bool_t (*can_take_file)(const awk_input_buf_t *iobuf); awk_bool_t (*take_control_of)(awk_input_buf_t *iobuf); awk_const struct awk_input_parser *awk_const next; /* per uso di gawk */ } awk_input_parser_t;
I campi sono:
const char *name;
Il nome dell’analizzatore di input. Questa è una normale stringa di caratteri del linguaggio C.
awk_bool_t (*can_take_file)(const awk_input_buf_t *iobuf);
Un puntatore alla funzione XXX_can_take_file()
.
awk_bool_t (*take_control_of)(awk_input_buf_t *iobuf);
Un puntatore alla funzione XXX_take_control_of()
.
awk_const struct input_parser *awk_const next;
Questa struttura è per uso di gawk
;
per questo motivo è marcata awk_const
in modo che l’estensione non
possa modificarla.
I passi da seguire sono i seguenti:
static awk_input_parser_t
e inizializzarla
adeguatamente.
gawk
usando la funzione API register_input_parser()
(descritta più sotto).
La definizione di una struttura awk_input_buf_t
è simile a questa:
typedef struct awk_input { const char *name; /* nome file */ int fd; /* descrittore di file */ #define INVALID_HANDLE (-1) void *opaque; /* area dati privata per l'analizzatore di input */ int (*get_record)(char **out, struct awk_input *iobuf, int *errcode, char **rt_start, size_t *rt_len); ssize_t (*read_func)(); void (*close_func)(struct awk_input *iobuf); struct stat sbuf; /* buffer per stat */ } awk_input_buf_t;
I campi si possono dividere in due categorie: quelli che sono usati (almeno
inizialmente) da XXX_can_take_file()
, e quelli che sono usati da
XXX_take_control_of()
. Il primo gruppo di campi, e il loro uso,
è così definito:
const char *name;
Il nome del file.
int fd;
Un descrittore di file per il file. Se gawk
riesce ad aprire
il file, il valore di fd
non sarà uguale a
INVALID_HANDLE
[descrittore non valido]. In caso contrario,
quello sarà il valore.
struct stat sbuf;
Se il descrittore di file è valido, gawk
avrà riempito i campi di
questa struttura invocando la chiamata di sistema fstat()
.
La funzione XXX_can_take_file()
dovrebbe esaminare i campi di
cui sopra e decidere se l’analizzatore di input vada usato per il file.
La decisione può dipendere da uno stato di gawk
(il valore
di una variabile definita in precedenza dall’estensione e impostata dal
codice awk
), dal nome del
file, dal fatto che il descrittore di file sia valido o no,
dalle informazioni contenute in struct stat
o da una qualsiasi
combinazione di questi fattori.
Una volta che XXX_can_take_file()
restituisce true, e
gawk
ha deciso di usare l’analizzatore personalizzato, viene
chiamata la funzione XXX_take_control_of()
. Tale funzione
si occupa di riempire il campo get_record
oppure il campo
read_func
nella struttura awk_input_buf_t
. La funzione si
assicura inoltre che fd
not sia impostato al valore
INVALID_HANDLE
. L’elenco seguente descrive i campi che
possono essere riempiti da XXX_take_control_of()
:
void *opaque;
Questo campo è usato per contenere qualiasi informazione di stato sia
necessaria per l’analizzatore di input
riguardo a questo file. Il campo è “opaco” per gawk
.
L’analizzatore di input non è obbligato a usare questo puntatore.
int (*get_record)(char **out,
struct awk_input *iobuf,
int *errcode,
char **rt_start,
size_t *rt_len);
Questo puntatore a funzione dovrebbe puntare a una funzione che crea i record in input. Tale funzione è il nucleo centrale dell’analizzatore di input. Il suo modo di operare è descritto nel testo che segue questo elenco.
ssize_t (*read_func)();
Questo puntatore a funzione dovrebbe puntare a una funzione che ha lo
stesso comportamento della chiamata di sistema standard POSIX read()
.
È in alternativa al puntatore a get_record
. Il relativo comportamento
è pure descritto nel testo che segue quest’elenco.
void (*close_func)(struct awk_input *iobuf);
Questo puntatore a funzione dovrebbe puntare a una funzione che fa
la “pulizia finale”. Dovrebbe liberare ogni risorsa allocata da
XXX_take_control_of()
. Può anche chiudere il file. Se lo fa,
dovrebbe impostare il campo fd
a INVALID_HANDLE
.
Se fd
è ancora diverso da INVALID_HANDLE
dopo la chiamata a
questa funzione, gawk
invoca la normale chiamata di sistema
close()
.
Avere una funzione di “pulizia” è facoltativo. Se l’analizzatore di input
non ne ha bisogno, basta non impostare questo campo. In questo caso,
gawk
invoca la normale chiamata di sistema close()
per il
descrittore di file, che, quindi, dovrebbe essere valido.
La funzione XXX_get_record()
svolge il lavoro di creazione dei
record in input. I parametri sono i seguenti:
char **out
Questo è un puntatore a una variabile char *
che è impostatata in modo
da puntare al record. gawk
usa una sua copia locale dei dati,
quindi l’estensione deve gestire la relativa area di memoria.
struct awk_input *iobuf
Questa è la struttura awk_input_buf_t
per il file. I campi dovrebbero
essere usati per leggere i dati (fd
) e per gestire lo stato privato
(opaque
), se necessario.
int *errcode
Se si verifica un errore, *errcode
dovrebbe essere impostato a un
valore appropriato tra quelli contenuti in <errno.h>
.
char **rt_start
size_t *rt_len
Se il concetto “fine record” è applicabile,
*rt_start
dovrebbe essere impostato per puntare ai dati da usare come
RT
, e *rt_len
dovrebbe essere impostata alla lunghezza di quel
campo. In caso contrario, *rt_len
dovrebbe essere impostata a zero.
gawk
usa una sua copia di questi dati, quindi l’estensione deve
gestire tale memoria.
Il codice di ritorno è la lunghezza del buffer puntato da
*out
oppure EOF
, se è stata raggiunta la fine del file o se
si è verificato un errore.
Poiché errcode
è sicuramente un puntatore valido, non c’è
bisogno di controllare che il valore sia NULL
. gawk
imposta *errcode
a zero, quindi non c’è bisogno di impostarlo, a meno
che non si verifichi un errore.
In presenza di un errore, la funzione dovrebbe restituire EOF
e
impostare *errcode
a un valore maggiore di zero. In questo caso, se
*errcode
non è uguale a zero, gawk
automaticamente aggiorna
la variabile ERRNO
usando il valore contenuto in *errcode
.
(In generale, impostare ‘*errcode = errno’ dovrebbe essere la
cosa giusta da fare.)
Invece di fornire una funzione che restituisce un record in input,
è possibile fornirne una che semplicemente legge dei byte, e lascia
che sia gawk
ad analizzare i dati per farne dei record. In questo
caso, i dati dovrebbero essere restituiti nella codifica multibyte propria
della localizzazione corrente.
Una siffatta funzione dovrebbe imitare il comportamento della chiamata di
sistema read()
, e riempire il puntatore read_func
con
il proprio indirizzo nella struttura awk_input_buf_t
.
Per default, gawk
imposta il puntatore read_func
in modo che
punti alla chiamata di sistema read()
. In questo modo l’estensione
non deve preoccuparsi di impostare esplicitamente questo campo.
NOTA: Occorre decidere per l’uno o per l’altro metodo: o una funzione che restituisce un record o una che restituisce dei dati grezzi. Nel dettaglio, se si fornisce una funzione che prepara un record,
gawk
la invocherà, e non chiamerà mai la funzione che fa una lettura grezza.
gawk
viene distribuito con un’estensione di esempio che legge
delle directory, restituendo un record per ogni elemento contenuto nella
directory (vedi Esempio di estensione Readdir. Questo codice sorgente può
essere usato come modello per scrivere un analizzatore di input
personalizzato.
Quando si scrive un analizzatore di input, si dovrebbe progettare (e
documentare) il modo con cui si suppone che interagisca con il codice
awk
. Si può scegliere di utilizzarlo per tutte le letture, e
intervenire solo quando è necessario, (come fa l’estensione di
esempio readdir
). Oppure lo si può utilizzare a seconda del
valore preso da una variabile awk
, come fa l’estensione XML
inclusa nel progetto gawkextlib
(vedi gawkextlib).
In quest’ultimo caso, il codice in una regola BEGINFILE
può controllare FILENAME
ed ERRNO
per decidere se
attivare un analizzatore di input (vedi BEGINFILE/ENDFILE) oppure no.
Un analizzatore di input va registrato usando la seguente funzione:
void register_input_parser(awk_input_parser_t *input_parser);
Registra l’analizzatore di input puntato da input_parser
con
gawk
.
Successivo: Processori bidirezionali, Precedente: Analizzatori di input, Su: Funzioni di registrazione [Contenuti][Indice]
Un processore di output è l’immagine riflessa di un analizzatore di input. Consente a un’estensione di prendere il controllo dell’output indirizzato verso un file che sia stato aperto con gli operatori di ridirezione di I/O ‘>’ o ‘>>’ (vedi Ridirezione).
Il processore di output è molto simile, come struttura, all’analizzatore di input:
typedef struct awk_output_wrapper { const char *name; /* nome del processore */ awk_bool_t (*can_take_file)(const awk_output_buf_t *outbuf); awk_bool_t (*take_control_of)(awk_output_buf_t *outbuf); awk_const struct awk_output_wrapper *awk_const next; /* per gawk */ } awk_output_wrapper_t;
I campi sono i seguenti:
const char *name;
Questo è il nome del processore di output.
awk_bool_t (*can_take_file)(const awk_output_buf_t *outbuf);
Questo è il puntatore a una funzione che esamina l’informazione contenuta
nella struttura awk_output_buf_t
puntata da outbuf
.
Dovrebbe restituire true se il processore di output vuole elaborare
il file, e false in caso contrario.
Nessuno stato (valori di variabili, etc.) dovrebbe essere modificato
all’interno di gawk
.
awk_bool_t (*take_control_of)(awk_output_buf_t *outbuf);
La funzione puntata da questo campo viene chiamata quando gawk
decide di consentire al processore di output di prendere il controllo del file.
Dovrebbe riempire in maniera appropriata dei campi nella struttura
awk_output_buf_t
, come descritto sotto, e restituire true se
ha successo, false in caso contrario.
awk_const struct output_wrapper *awk_const next;
Questa struttura è per uso di gawk
;
per questo motivo è marcata awk_const
in modo che l’estensione non
possa modificarlo.
La struttura awk_output_buf_t
è simile a questa:
typedef struct awk_output_buf { const char *name; /* nome del file in output */ const char *mode; /* argomento mode per fopen */ FILE *fp; /* puntatore stdio file */ awk_bool_t redirected; /* true se un processore è attivo */ void *opaque; /* per uso del processore di output */ size_t (*gawk_fwrite)(const void *buf, size_t size, size_t count, FILE *fp, void *opaque); int (*gawk_fflush)(FILE *fp, void *opaque); int (*gawk_ferror)(FILE *fp, void *opaque); int (*gawk_fclose)(FILE *fp, void *opaque); } awk_output_buf_t;
Anche qui, l’estensione definirà le funzioni XXX_can_take_file()
e XXX_take_control_of()
che esaminano e aggiornano
campi dati in awk_output_buf_t
.
I campi dati sono i seguenti:
const char *name;
Il nome del file in output.
const char *mode;
La stringa mode (come sarebbe usata nel secondo argomento della
chiamata di sistema fopen()
)
con cui il file era stato aperto.
FILE *fp;
Il puntatore FILE
da <stdio.h>
. gawk
apre il file
prima di controllare se esiste un processore di output.
awk_bool_t redirected;
Questo campo dev’essere impostato a true dalla funzione
XXX_take_control_of()
.
void *opaque;
Questo puntatore è opaco per gawk
. L’estensione dovrebbe usarlo
per contenere un puntatore a qualsiasi dato privato associato al file.
size_t (*gawk_fwrite)(const void *buf, size_t size, size_t count,
FILE *fp, void *opaque);
int (*gawk_fflush)(FILE *fp, void *opaque);
int (*gawk_ferror)(FILE *fp, void *opaque);
int (*gawk_fclose)(FILE *fp, void *opaque);
Questi puntatori dovrebbero essere impostati per puntare a funzioni
la cui azione sia equivalente a quella delle funzioni di <stdio.h>
,
se questo è cio che si desidera.
gawk
usa questi puntatori a funzione per tutti gli output.
gawk
inizializza i puntatori per puntare a funzioni interne
“di passaggio” che si limitano a chiamare le funzioni normali di
<stdio.h>
, e quindi un’estensione deve ridefinire solo le funzioni
appropriate per fare il lavoro richiesto.
La funzione XXX_can_take_file()
dovrebbe decidere in base ai
campi name
e mode
, e a ogni altro ulteriore indicatore di stato
(p.es., valori di variabili awk
) adatto allo scopo.
Quando gawk
chiama XXX_take_control_of()
, la funzione
dovrebbe riempire i rimanenti campi
in modo opportuno, tranne che per fp
, che dovrebbe essere usato
normalmente.
Il processore di output va registrato usando la seguente funzione:
void register_output_wrapper(awk_output_wrapper_t *output_wrapper);
Registra il processore di output puntato da output_wrapper
con
gawk
.
Precedente: Processori di output, Su: Funzioni di registrazione [Contenuti][Indice]
Un processore bidirezionale combina un analizzatore di input e
un processore di output per un I/O
bidirezionale usando l’operatore ‘|&’ (vedi Ridirezione).
Le strutture awk_input_parser_t
e awk_output_buf_t
sono usate nella maniera già descritta precedentemente.
Un processore bidirezionale è rappresentato dalla struttura seguente:
typedef struct awk_two_way_processor { const char *name; /* nome del processore bidirezionale */ awk_bool_t (*can_take_two_way)(const char *name); awk_bool_t (*take_control_of)(const char *name, awk_input_buf_t *inbuf, awk_output_buf_t *outbuf); awk_const struct awk_two_way_processor *awk_const next; /* per gawk */ } awk_two_way_processor_t;
I campi sono i seguenti:
const char *name;
Il nome del processore bidirezionale.
awk_bool_t (*can_take_two_way)(const char *name);
La funzione puntata da questo campo dovrebbe restituire true se
vuole gestire l’I/O bidirezionale per questo nome-file.
La funzione non dovrebbe modificare alcuno stato (valori di variabili, etc.)
all’interno di gawk
.
awk_bool_t (*take_control_of)(const char *name,
awk_input_buf_t *inbuf,
awk_output_buf_t *outbuf);
La funzione puntata da questo campo dovrebbe riempire le strutture
awk_input_buf_t
e awk_output_buf_t
puntate da inbuf
e
outbuf
, rispettivamente. Queste strutture sono già state descritte
in precedenza.
awk_const struct two_way_processor *awk_const next;
Questa struttura è per uso di gawk
;
per questo motivo è marcata awk_const
in modo che l’estensione non
possa modificarla.
Come per l’analizzatore di input e il processore di output, vanno fornite le
funzione “sì, ci penso io” e “per questo, fai tu”,
XXX_can_take_two_way()
e XXX_take_control_of()
.
Il processore bidirezionale va registrato usando la seguente funzione:
void register_two_way_processor(awk_two_way_processor_t *two_way_processor);
Registra il processore bidirezionale puntato da two_way_processor
con
gawk
.
Successivo: Aggiornare ERRNO
, Precedente: Funzioni di registrazione, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
È possibile stampare diversi tipi di messaggi di avvertimento da
un’estensione, come qui spiegato. Si noti che, per queste funzioni,
si deve fornire l’ID di estensione ricevuto da gawk
al momento in cui l’estensione è stata caricata:108
void fatal(awk_ext_id_t id, const char *format, ...);
Stampa un messaggio e poi gawk
termina immediatamente l’esecuzione.
void nonfatal(awk_ext_id_t id, const char *format, ...);
Stampa un messaggio di errore non-fatale.
void warning(awk_ext_id_t id, const char *format, ...);
Stampa un messaggio di avvertimento.
void lintwarn(awk_ext_id_t id, const char *format, ...);
Stampa un messaggio di avvertimento “lint”. Normalmente questo equivale a
stampare un messaggio di avvertimento, ma se gawk
era stato
invocato specificando l’opzione ‘--lint=fatal’,
gli avvertimenti di lint diventano messaggi di errore fatali.
Tutte queste funzioni sono per il resto simili alla famiglia di funzioni
printf()
del linguaggio C, dove il parametro format
è una
stringa contenente dei caratteri normali e delle istruzioni di formattazione,
mischiati tra loro.
Successivo: Richiedere valori, Precedente: Stampare messaggi, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
ERRNO
Le seguenti funzioni consentono l’aggiornamento della variabile
ERRNO
:
void update_ERRNO_int(int errno_val);
Imposta ERRNO
alla stringa equivalente del codice di errore
in errno_val
. Il valore dovrebbe essere uno dei codici di errore
definiti in <errno.h>
, e gawk
lo trasforma in una stringa
(qualora possibile, tradotta) usando la funzione C strerror()
.
void update_ERRNO_string(const char *string);
Imposta ERRNO
direttamente usando il valore della stringa specificata.
gawk
fa una copia del valore di stringa
.
void unset_ERRNO(void);
Annulla il valore di ERRNO
.
Successivo: Accedere ai parametri, Precedente: Aggiornare ERRNO
, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
Tutte le funzioni che restituiscono valori da gawk
funzionano allo stesso modo. Si fornisce un campo awk_valtype_t
per indicare il tipo di valore che ci si aspetta. Se il valore disponibile
corrisponde a quello richiesto, la funzione restituisce true e riempie
il campo del risultato awk_value_t
.
Altrimenti, la funzione restituisce false, e il campo val_type
indica il tipo di valore disponibile.
A quel punto si può, a seconda di quel che richiede la situazione,
stampare un messaggio di errore oppure ripetere la
richiesta specificando il tipo di valore che risulta disponibile. Questo
comportamento è riassunto nella
Tabella 16.1.
Tipo di valore reale |
---|
Stringa | Strnum | Numero | Regexp | Vettore | Indefinito | ||
---|---|---|---|---|---|---|---|
Stringa | Stringa | Stringa | Stringa | Stringa | false | false | |
Strnum | false | Strnum | Strnum | false | false | false | |
Numero | Numero | Numero | Numero | false | false | false | |
Tipo | Regexp | false | false | false | Regexp | false | false |
Richiesto | Vettore | false | false | false | false | Vettore | false |
Scalar | Scalar | Scalar | Scalar | Scalar | false | false | |
Indefinito | Stringa | Strnum | Numero | Regexp | Vettore | Indefinito | |
Value cookie | false | false | false | false | false | false |
Tabella 16.1: Tipi di valori restituiti dall’API
Successivo: Accedere alla tabella simboli, Precedente: Richiedere valori, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
Due funzioni consentono di accedere agli argomenti (parametri) passati all’estensione. Esse sono:
awk_bool_t get_argument(size_t count,
awk_valtype_t wanted,
awk_value_t *risultato);
Riempie la struttura awk_value_t
puntata da risultato
con l’argomento numero count
. Restituisce true se il tipo
dell’argomento corrisponde
a quello specificato in wanted
, e false in caso contrario.
In quest’ultimo caso,
risultato->val_type
indica il tipo effettivo dell’argomento
(vedi Tabella 16.1). La numerazione degli argomenti parte
da zero: il primo
argomento è il numero zero, il secondo è il numero uno, e così via.
wanted
indica il tipo di valore atteso.
awk_bool_t set_argument(size_t count, awk_array_t array);
Converte un parametro di tipo indefinito in un vettore; ciò permette la
chiamata per riferimento per i vettori. Restituisce false se count
è troppo elevato,
o se il tipo di argomento è diverso da undefined.
Vedi Manipolazione di vettori
per ulteriori informazioni riguardo alla creazione di vettori.
Successivo: Manipolazione di vettori, Precedente: Accedere ai parametri, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
Due insiemi di routine permettono di accedere alle variabili globali, e un insieme consente di creare e rilasciare dei valori nascosti.
• Tabella simboli per nome: | Accedere alle variabili per nome. | |
• Tabella simboli tramite cookie: | Accedere alle variabili per “cookie”. | |
• Valori nascosti: | Creare e usare valori nascosti. |
Successivo: Tabella simboli tramite cookie, Su: Accedere alla tabella simboli [Contenuti][Indice]
Le routine che seguono permettono di raggiungere e aggiornare
le variabili globali a livello di awk
per nome. Nel gergo dei
compilatori, gli identificativi di vario tipo sono noti come simboli,
da cui il prefisso “sym” nei nomi delle routine. La struttura di dati che
contiene informazioni sui simboli è chiamata Tabella dei simboli
(Symbol table).
Le funzioni sono le seguenti:
awk_bool_t sym_lookup(const char *name,
awk_valtype_t wanted,
awk_value_t *risultato);
Riempie la struttura awk_value_t
puntata da risultato
con il valore della variabile il cui nome è nella stringa name
,
che è una normale stringa di caratteri C.
wanted
indica il tipo di valore atteso.
La funzione restituisce true se il tipo effettivo della variabile è quello
specificato in wanted
, e false in caso contrario.
In quest’ultimo caso, risultato>val_type
indica il tipo effettivo
della variabile
(vedi Tabella 16.1).
awk_bool_t sym_update(const char *name, awk_value_t *valore);
Aggiorna la variabile il cui nome è contenuto nella stringa name
,
che è una normale stringa di caratteri C.
La variabile è aggiunta alla Tabella dei simboli di gawk
,
se non è già presente. Restituisce true se tutto è andato bene, e
false in caso contrario.
La modifica del tipo (da scalare a vettoriale o viceversa) di una variabile
già esistente non è consentito, e questa routine non può neppure
essere usata per aggiornare un vettore.
Questa routine non può essere usata per modificare nessuna delle variabili
predefinite (come ARGC
o NF
).
Un’estensione può andare a cercare il valore delle variabili speciali di
gawk
.
Tuttavia, con l’eccezione del vettore PROCINFO
, un’estensione
non può cambiare alcuna di queste variabili.
Successivo: Valori nascosti, Precedente: Tabella simboli per nome, Su: Accedere alla tabella simboli [Contenuti][Indice]
Uno scalar cookie è un puntatore nascosto (opaque handle) che
fornisce accesso a una
variabile globale o a un vettore. Si tratta di un’ottimizzazione, per evitare
di ricercare variabili nella Tabella dei simboli di gawk
ogni volta
che un accesso è necessario. Questo
argomento è già stato trattato in precedenza, nella
Tipi di dati generali.
Le funzioni seguenti servono per gestire gli scalar cookie:
awk_bool_t sym_lookup_scalar(awk_scalar_t cookie,
awk_valtype_t wanted,
awk_value_t *risultato);
Ottiene il valore corrente di uno scalar cookie.
Una volta ottenuto lo scalar cookie usando sym_lookup()
, si
può usare questa funzione per accedere al valore della variabile in modo
più efficiente.
Restituisce false se il valore non è disponibile.
awk_bool_t sym_update_scalar(awk_scalar_t cookie, awk_value_t *valore);
Aggiorna il valore associato con uno scalar cookie.
Restituisce false se il nuovo valore non è del tipo
AWK_STRING
, AWK_STRNUM
, AWK_REGEX
o AWK_NUMBER
.
Anche in questo caso, le variabili predefinite non possono essere aggiornate.
Non è immediatamente evidente come si lavora con gli scalar cookie o
quale sia la loro vera ragion d’essere. In teoria, le routine
sym_lookup()
e sym_update()
sono tutto ciò che occorre per
lavorare con le variabili. Per esempio, ci potrebbe essere un codice che
ricerca il valore di una variabile, valuta una condizione, e potrebbe
poi cambiare il valore della variabile a seconda dei risultati della
valutazione in modo simile a questo:
/* do_magic --- fai qualcosa di veramente grande */ static awk_value_t * do_magic(int nargs, awk_value_t *risultato) { awk_value_t valore; if ( sym_lookup("MAGIC_VAR", AWK_NUMBER, & valore) && qualche_condizione(valore.num_valore)) { valore.num_valore += 42; sym_update("MAGIC_VAR", & valore); } return make_number(0.0, risultato); }
Questo codice sembra (ed è) semplice e immediato. Qual è il problema?
Beh, si consideri cosa succede se un qualche codice a livello di awk
associato con l’estensione richiama la funzione magic()
(implementata in linguaggio C da do_magic()
), una volta per ogni
record, mentre si stanno elaborando
file contenenti migliaia o milioni di record.
La variabile MAGIC_VAR
viene ricercata nella Tabella dei simboli una o due
volte per ogni richiamo della funzione!
La ricerca all’interno della Tabella dei simboli è in realtà una pura perdita di tempo; è molto più efficiente ottenere un value cookie che rappresenta la variabile, e usarlo per ottenere il valore della variabile e aggiornarlo a seconda della necessità.109
Quindi, la maniera per usare i valori-cookie è la seguente. Per prima
cosa, la variabile di estensione va messa nella Tabella dei simboli di
gawk
usando sym_update()
, come al solito. Poi si deve ottenere
uno scalar cookie per la
variabile usando sym_lookup()
:
static awk_scalar_t magic_var_cookie; /* cookie per MAGIC_VAR */ static void inizializza_estensione() { awk_value_t valore; /* immettere il valore iniziale */ sym_update("MAGIC_VAR", make_number(42.0, & valore)); /* ottenere il value cookie */ sym_lookup("MAGIC_VAR", AWK_SCALAR, & valore); /* salvarlo per dopo */ magic_var_cookie = valore.scalar_cookie; … }
Dopo aver fatto questo, si usino le routine descritte in questa sezione
per ottenere e modificare
il valore usando il value cookie. Quindi, do_magic()
diviene ora
qualcosa del tipo:
/* do_magic --- fai qualcosa di veramente grande */ static awk_value_t * do_magic(int nargs, awk_value_t *risultato) { awk_value_t valore; if ( sym_lookup_scalar(magic_var_cookie, AWK_NUMBER, & valore) && qualche_condizione(valore.num_valore)) { valore.num_valore += 42; sym_update_scalar(magic_var_cookie, & valore); } … return make_number(0.0, risultato); }
NOTA: Il codice appena visto omette il controllo di eventuali errori, per amor di semplicità. Il codice dell’estensione dovrebbe essere più complesso e controllare attentamente i valori restituiti dalle funzioni dell’API.
Precedente: Tabella simboli tramite cookie, Su: Accedere alla tabella simboli [Contenuti][Indice]
Le routine in questa sezione permettono di creare e rilasciare
valori nascosti. Come gli scalar cookie, in teoria i valori nascosti
non sono necessari. Si possono creare numeri e stringhe usando
le funzioni descritte
in
Funzioni di costruzione. Si possono poi assegnare
quei valori a delle variabili usando sym_update()
o sym_update_scalar()
, come si preferisce.
Tuttavia, si può comprendere l’utilità di avere dei valori nascosti
se si pone mente al fatto che la memoria di ogni valore di stringa
deve essere ottenuta tramite gawk_malloc()
,
gawk_calloc()
o gawk_realloc()
.
Se ci sono 20 variabili, e tutte hanno per valore la stessa stringa,
si devono creare 20 copie identiche della stringa.110
Chiaramente è più efficiente, se possibile, creare il valore una sola volta,
e fare in modo che gawk
utilizzi quell’unico valore per molte
variabili. Questo è ciò che la routine in
questa
sezione permette
di fare. Le funzioni sono le seguenti:
awk_bool_t create_value(awk_value_t *valore, awk_value_cookie_t *risultato);
Crea una stringa o un valore numerico nascosti, da valore
, in
vista di un successivo assegnamento di valore. Sono consentiti solo valori di
tipo AWK_NUMBER
, AWK_REGEX
ed AWK_STRING
.
Ogni altro tipo è rifiutato.
Il tipo AWK_UNDEFINED
potrebbe essere consentito, ma in questo caso
l’efficienza del programma ne soffrirebbe.
awk_bool_t release_value(awk_value_cookie_t vc);
Libera la memoria associata con un value cookie ottenuto mediante
create_value()
.
Si usano i value cookie in modo dimile a quello con cui si usano gli scalar cookie. Nella routine di inizializzazione dell’estensione, si crea il value cookie:
static awk_value_cookie_t answer_cookie; /* static value cookie */ static void inizializza_estensione() { awk_value_t value; char *long_string; size_t long_string_len; /* codice precedente */ … /* … riempire long_string e long_string_len … */ make_malloced_string(long_string, long_string_len, & value); create_value(& value, & answer_cookie); /* creare cookie */ … }
Una volta che il valore è creato, si può usare come valore per un numero qualsiasi di variabili:
static awk_value_t * do_magic(int nargs, awk_value_t *risultato) { awk_value_t new_value; … /* come in precedenza */ value.val_type = AWK_VALUE_COOKIE; value.value_cookie = answer_cookie; sym_update("VAR1", & value); sym_update("VAR2", & value); … sym_update("VAR100", & value); … }
Usare value cookie in questo modo permette di risparmiare parecchia
memoria, poiché tutte le variabili da VAR1
a VAR100
condividono
lo stesso valore.
Ci si potrebbe chiedere, “Questa condivisione crea problemi?
Cosa succede se il codice awk
assegna un nuovo valore a VAR1
;
sono modificate anche tutte le altre variabili?”
Buona domanda! La risposta è che no, non è un problema.
Internamente, gawk
usa
un contatore dei riferimenti alle stringhe. Questo significa
che molte variabili possono condividere lo stesso valore di tipo stringa,
e gawk
mantiene traccia del loro uso. Quando il valore di
una variabile viene modificato, gawk
semplicemente diminuisce di
uno il contatore dei riferimenti del vecchio valore, e aggiorna la variabile
perché usi il nuovo valore.
Infine, come parte della pulizia al termine del programma
(vedi Funzioni di exit callback)
si deve liberare ogni valore nascosto che era stato creato, usando
la funzione release_value()
.
Successivo: Ridirezione API, Precedente: Accedere alla tabella simboli, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
La struttura di dati primaria111
in awk
è il vettore associativo
(vedi Vettori).
Le estensioni devono essere in grado di manipolare vettori awk
.
L’API fornisce varie strutture di dati per lavorare con vettori,
funzioni per lavorare con singoli elementi di un vettore, e funzioni per
lavorare con interi vettori. È prevista anche la possibilità di
“appiattire” un vettore in modo da rendere facile a un programma scritto in
C la “visita” di tutti gli elementi del vettore.
Le strutture dati per i vettori sono facilmente integrabili con le
strutture dati per variabili scalari, per facilitare sia l’elaborazione, sia
la creazione di veri vettori di vettori (vedi Tipi di dati generali).
• Tipi di dati per i vettori: | Tipi dati per lavorare coi vettori. | |
• Funzioni per i vettori: | Funzioni per lavorare coi vettori. | |
• Appiattimento di vettori: | Come appiattire i vettori. | |
• Creazione di vettori: | Come creare e popolare vettori. |
Successivo: Funzioni per i vettori, Su: Manipolazione di vettori [Contenuti][Indice]
I tipi di dato associati con i vettori sono i seguenti:
typedef void *awk_array_t;
Se si richiede il valore di una variabile contenuta in un vettore, si ottiene
un valore del tipo awk_array_t
. Questo valore è
opaco112 per l’estensione; identifica in maniera univoca il
vettore ma può solo essere usato come parametro di una funzione dell’API,
o essere ricevuto da una funzione dell’API. Questo è molto simile al modo
in cui i valori ‘FILE *’ sono usati con le routine di libreria di
<stdio.h>
.
typedef struct awk_element {
/* puntatore di servizio
a lista collegata, non usato da gawk */
struct awk_element *next;
enum {
AWK_ELEMENT_DEFAULT = 0, /* impostato da gawk */
AWK_ELEMENT_DELETE = 1 /* impostato dall'estensione */
} flags;
awk_value_t index;
awk_value_t value;
} awk_element_t;
awk_element_t
è un elemento di vettore “appiattito”.
awk
produce un vettore di questo tipo all’interno della struttura
awk_flat_array_t
(si veda poco più avanti).
Singoli elementi di vettore possono essere marcati per essere cancellati.
Nuovi elementi del vettore devono essere aggiunti individualmente, uno per
volta, usando una funzione API apposita. I campi sono i seguenti:
struct awk_element *next;
Questo puntatore è presente come ausilio a chi scrive un’estensione. Permette a un’estensione di creare una lista collegata (linked list) di nuovi elementi che possono essere aggiunti a un vettore con un singolo ciclo che percorre tutta la lista.
enum { … } flags;
Un insieme di valori di flag che passano informazione tra l’estensione
e gawk
. Per ora c’è solo un flag disponibile:
AWK_ELEMENT_DELETE
.
Se lo si imposta, gawk
elimina l’elemento in questione dal vettore
originale, dopo che il vettore “appiattito” è stato rilasciato.
index
value
L’indice e il valore di un elemento, rispettivamente.
Tutta la memoria puntata da index
e valore
appartiene
a gawk
.
typedef struct awk_flat_array {
awk_const void *awk_const opaque1; /* per uso di gawk */
awk_const void *awk_const opaque2; /* per uso di gawk */
awk_const size_t count; /* quanti elementi nel vettore */
awk_element_t elements[1]; /* saranno ``appiattiti'' */
} awk_flat_array_t;
Questo è un vettore appiattito. Quando un’estensione ottiene da
gawk
questa struttura, il vettore elements
ha una dimensione
reale di count
elementi.
I puntatori opaque1
e opaque2
sono per uso di gawk
;
come tali, sono marcati come awk_const
in modo che l’estensione non
possa modificarli.
Successivo: Appiattimento di vettori, Precedente: Tipi di dati per i vettori, Su: Manipolazione di vettori [Contenuti][Indice]
Le funzioni seguenti permettono di gestire singoli elementi di un vettore:
awk_bool_t get_element_count(awk_array_t a_cookie, size_t *count);
Per il vettore rappresentato da a_cookie
, restituisce in *count
il numero di elementi in esso contenuti. Ogni sottovettore è conteggiato come
se fosse un solo elemento.
Restituisce false se si verifica un errore.
awk_bool_t get_array_element(awk_array_t a_cookie,
const awk_value_t *const index,
awk_valtype_t wanted,
awk_value_t *risultato);
Per il vettore rappresentato da a_cookie
, restituisce in *risultato
il valore dell’elemento il cui indice è index
.
wanted
specifica il tipo di valore che si vuole ritrovare.
Restituisce false se wanted
non coincide con il tipo di dato o
se index
non è nel vettore (vedi Tabella 16.1).
Il valore per index
può essere numerico, nel qual caso gawk
lo converte in una stringa. Usare valori non interi è possibile, ma
richiede di comprendere il modo con cui tali valori sono convertiti in stringhe
(vedi Conversione); per questo motivo, è meglio usare numeri interi.
Come per tutte le stringhe passate a gawk
da
un’estensione, la memoria che contiene il valore della stringa con chiave
index
deve essere stata acquisita utilizzando le funzioni
gawk_malloc()
, gawk_calloc()
o gawk_realloc()
, e
gawk
rilascerà (al momento opportuno) la relativa memoria.
awk_bool_t set_array_element(awk_array_t a_cookie,
const awk_value_t *const index,
const awk_value_t *const value);
Nel vettore rappresentato da a_cookie
, crea o modifica
l’elemento il cui indice è contenuto in index
.
I vettori ARGV
ed ENVIRON
non possono essere modificati,
mentre il vettore PROCINFO
è modificabile.
awk_bool_t set_array_element_by_elem(awk_array_t a_cookie,
awk_element_t element);
Come set_array_element()
, ma prende l’indice index
e
il valore value
da element
. Questa è una macro di utilità.
awk_bool_t del_array_element(awk_array_t a_cookie,
const awk_value_t* const index);
Elimina dal vettore, rappresentato da a_cookie
, l’elemento con
l’indice specificato.
Restituisce true se l’elemento è stato rimosso o false se
l’elemento non era presente nel vettore.
Le seguenti funzioni operano sull’intero vettore:
awk_array_t create_array(void);
Crea un nuovo vettore a cui si possono aggiungere elementi. Vedi Creazione di vettori per una trattazione su come creare un nuovo vettore e aggiungervi elementi.
awk_bool_t clear_array(awk_array_t a_cookie);
Svuota il vettore rappresentato da a_cookie
.
Restituisce false in presenza di qualche tipo di problema, true
in caso contrario. Il vettore non viene eliminato ma, dopo aver chiamato
questa funzione, resta privo di elementi. Questo è equivalente a usare
l’istruzione delete
(vedi Cancellazione).
awk_bool_t flatten_array_typed(awk_array_t a_cookie, awk_flat_array_t **data, awk_valtype_t index_type, awk_valtype_t value_type);
Per il vettore rappresentato da a_cookie
, crea una struttura
awk_flat_array_t
e la riempie con indici e valori del tipo richiesto.
Imposta il puntatore il cui indirizzo è passato in data
per puntare a
questa struttura.
Restituisce true se tutto va bene o false in caso contrario.
Vedi Appiattimento di vettori,
per una trattazione su come appiattire un vettore per poterci lavorare.
awk_bool_t flatten_array(awk_array_t a_cookie, awk_flat_array_t **data);
Per il vettore rappresentato da a_cookie
, crea una struttura
awk_flat_array_t
e la riempie con indici di tipo AWK_STRING
e
valori AWK_UNDEFINED
.
Questa funzione è resa obsoleta da flatten_array_typed()
.
È fornita come macro, e mantenuta per convenienza e per compatibilità a
livello di codice sorgente con la precedente versione dell’API.
awk_bool_t release_flattened_array(awk_array_t a_cookie,
awk_flat_array_t *data);
Quando si è finito di lavorare con un vettore appiattito, si liberi la
memoria usando questa funzione. Occorre fornire sia il cookie del vettore
originale, sia l’indirizzo della struttura da liberare,
awk_flat_array_t
.
La funzione restituisce true se tutto va bene, false in caso contrario.
Successivo: Creazione di vettori, Precedente: Funzioni per i vettori, Su: Manipolazione di vettori [Contenuti][Indice]
Appiattire un vettore vuol dire creare una struttura che rappresenta l’intero vettore in modo da facilitare la visita dell’intero vettore da parte del codice in C . Parte del codice in extension/testext.c fa questo, ed è anche un bell’esempio di come utilizzare l’API.
Questa parte del codice sorgente sarà descritta un po’ per volta.
Ecco, per iniziare, lo script gawk
che richiama l’estensione di test:
@load "testext" BEGIN { n = split("blacky rusty sophie raincloud lucky", pets) printf("pets ha %d elementi\n", length(pets)) ret = dump_array_and_delete("pets", "3") printf("dump_array_and_delete(pets) ha restituito %d\n", ret) if ("3" in pets) printf("dump_array_and_delete() NON ha rimosso l'indice \"3\"!\n") else printf("dump_array_and_delete() ha rimosso l'indice \"3\"!\n") print "" }
Questo codice crea un vettore usando la funzione split()
(vedi Funzioni per stringhe)
e poi chiama dump_array_and_delete()
. Questa funzione ricerca
il vettore il cui nome è passato come primo argomento, ed
elimina l’elemento il cui indice è passato come secondo argomento.
Il codice awk
stampa poi il valore restituito e controlla che
l’elemento sia stato effettivamente cancellato. Ecco il codice C che
costituisce la funzione
dump_array_and_delete()
. È stato leggermente modificato per facilitare
l’esposizione.
La prima parte dichiara variabili, imposta il valore di ritorno di default
in risultato
, e controlla che la funzione
sia stata chiamata con il numero corretto di argomenti:
static awk_value_t * dump_array_and_delete(int nargs, awk_value_t *risultato) { awk_value_t valore, valore2, valore3; awk_flat_array_t *flat_array; size_t count; char *name; int i; assert(risultato != NULL); make_number(0.0, risultato); if (nargs != 2) { printf("dump_array_and_delete: nargs errato " "(%d dovrebbe essere 2)\n", nargs); goto out; }
La funzione poi prosegue un passo per volta, come segue. Il primo passo è ricuperare il nome del vettore, passato come primo argomento, seguito dal vettore stesso. Se una di queste operazioni non riesce, viene stampato un messaggio di errore e si ritorna al chiamante:
/* trasforma in un vettore piatto il vettore passato come argomento e lo stampa */ if (get_argument(0, AWK_STRING, & value)) { name = valore.str_value.str; if (sym_lookup(name, AWK_array, & value2)) printf("dump_array_and_delete: sym_lookup di %s effettuato\n", name); else { printf("dump_array_and_delete: sym_lookup di %s non riuscito\n", name); goto out; } } else { printf("dump_array_and_delete: get_argument(0) non riuscito\n"); goto out; }
Per controllo, e per assicurarsi che il codice C veda
lo stesso numero di elementi del codice awk
,
il secondo passo è quello di ottenere il numero di elementi nel vettore
e stamparlo:
if (! get_element_count(valore2.array_cookie, & count)) { printf("dump_array_and_delete: get_element_count non riuscito\n"); goto out; } printf("dump_array_and_delete: il vettore in input ha %lu elementi\n", (unsigned long) count);
Il terzo passo è quello di appiattire il vettore, e quindi
controllare che il numero di elementi nella struttura awk_flat_array_t
sia uguale a quello appena trovato:
if (! flatten_array_typed(valore2.array_cookie, & flat_array, AWK_STRING, AWK_UNDEFINED)) { printf("dump_array_and_delete: non sono riuscito ad appiattire \ il vettore\n"); goto out; } if (flat_array->count != count) { printf("dump_array_and_delete: flat_array->count (%lu)" " != count (%lu)\n", (unsigned long) flat_array->count, (unsigned long) count); goto out; }
Il quarto passo è ritrovare l’indice dell’elemento
da eliminare, che era stato passato come secondo argomento.
Va tenuto presente che i contatori di argomenti passati a get_argument()
partono da zero, e che quindi il secondo argomento è quello numero uno:
if (! get_argument(1, AWK_STRING, & value3)) { printf("dump_array_and_delete: get_argument(1) non riuscito\n"); goto out; }
Il quinto passo è quello in cui si fa il “vero lavoro”. La funzione esegue
un ciclo su ogni elemento nel vettore, stampando i valori degli indici e
degli elementi. Inoltre, dopo aver trovato, tramite l’indice, l’elemento
che si vorrebbe eliminare, la funzione imposta il bit
AWK_ELEMENT_DELETE
nel campo flags
dell’elemento. Quando il vettore è stato interamente percorso, gawk
visita il vettore appiattito, ed elimina ogni elemento in cui il relativo
bit della flag sia impostato:
for (i = 0; i < flat_array->count; i++) { printf("\t%s[\"%.*s\"] = %s\n", name, (int) flat_array->elements[i].index.str_value.len, flat_array->elements[i].index.str_value.str, valrep2str(& flat_array->elements[i].valore)); if (strcmp(valore3.str_value.str, flat_array->elements[i].index.str_value.str) == 0) { flat_array->elements[i].flags |= AWK_ELEMENT_DELETE; printf("dump_array_and_delete: ho marcato l'elemento \"%s\" " "per eliminazione\n", flat_array->elements[i].index.str_value.str); } }
Il sesto passo è liberare il vettore appiattito. Questo segnala a
gawk
che l’estensione non sta più usando il vettore,
e che dovrebbe eliminare gli elementi marcati per l’eliminazione.
gawk
libera anche ogni area di memoria che era stata allocata,
e quindi non si dovrebbe più usare il puntatore (flat_array
in
questo codice) dopo aver chiamato release_flattened_array()
:
if (! release_flattened_array(valore2.array_cookie, flat_array)) { printf("dump_array_and_delete: non riesco a liberare \ il vettore appiattito\n"); goto out; }
Infine, poiché tutto è andato bene, la funzione imposta il codice di ritorno a "successo", e lo restituisce quando esce:
make_number(1.0, risultato); out: return risultato; }
Ecco l’output ottenuto eseguendo questa parte del test:
pets ha 5 elementi dump_array_and_delete: sym_lookup di pets effettuato dump_array_and_delete: il vettore in input ha 5 elementi pets["1"] = "blacky" pets["2"] = "rusty" pets["3"] = "sophie" dump_array_and_delete: ho marcato l'elemento "3" per eliminazione pets["4"] = "raincloud" pets["5"] = "lucky" dump_array_and_delete(pets) ha restituito 1 dump_array_and_delete() ha rimosso l'indice "3"!
Precedente: Appiattimento di vettori, Su: Manipolazione di vettori [Contenuti][Indice]
Oltre a lavorare con vettori creati da codice awk
, si possono
creare vettori a cui aggiungere elementi secondo le esigenze, che poi
il codice awk
può utilizzare e manipolare.
Ci sono due punti importanti da tener presente quando di creano vettori dal codice di un’estensione:
gawk
. Solo dopo aver fatto questo è possibile aggiungere elementi
al vettore.
Analogamente, se si installa un nuovo vettore come sottovettore di un vettore già esistente, occorre prima aggiungere il nuovo vettore al suo "genitore" per poter poi aggiungere degli elementi allo stesso.
Quindi, il modo giusto per costruire un vettore è di lavorare “dall’alto
verso il basso”. Creare il vettore, e subito aggiungerlo alla Tabella dei
simboli di gawk
usando sym_update()
, o installarlo come
elemento in un vettore già esistente usando set_array_element()
.
Un esempio di codice è fornito più sotto.
gawk
, dopo aver usato
sym_update()
per definire un vettore
in gawk
, si deve innanzitutto ricuperare il cookie
del vettore dal valore passato a sym_update()
, in questo modo:
awk_value_t val; awk_array_t new_array; new_array = create_array(); val.val_type = AWK_ARRAY; val.array_cookie = new_array; /* aggiunge il vettore alla Tabella dei simboli */ sym_update("array", & val); new_array = val.array_cookie; /* QUESTO È OBBLIGATORIO */
Se si sta installando un vettore come sottovettore, occorre anche
ricuperare il cookie del vettore dopo aver chiamato set_element()
.
Il seguente codice C è una semplice estensione di test per creare un vettore
con due elementi normali e con un sottovettore. Le direttive iniziali
#include
e le solite dichiarazione di variabili sono state omesse per
amor di brevità
(vedi Codice predefinito di un'estensione API).
Il primo passo è creare un nuovo vettore e poi aggiungerlo alla
Tabella dei simboli:
/* create_new_array --- creare un vettore denominato */ static void create_new_array() { awk_array_t a_cookie; awk_array_t sottovettore; awk_value_t index, valore; a_cookie = create_array(); valore.val_type = AWK_array; valore.array_cookie = a_cookie; if (! sym_update("new_array", & value)) printf("create_new_array: sym_update(\"nuovo_vettore\") \ non riuscito!\n"); a_cookie = valore.array_cookie;
Si noti come a_cookie
è reimpostato dal campo array_cookie
nella struttura valore
.
Il secondo passo aggiunge due elementi normali a nuovo_vettore
:
(void) make_const_string("salve", 5, & index); (void) make_const_string("mondo", 5, & value); if (! set_array_element(a_cookie, & index, & value)) { printf("fill_in_array: set_array_element non riuscito\n"); return; } (void) make_const_string("risposta", 8, & index); (void) make_number(42.0, & value); if (! set_array_element(a_cookie, & index, & value)) { printf("fill_in_array: set_array_element non riuscito\n"); return; }
Il terzo passo è creare il sottovettore e aggiungerlo al vettore:
(void) make_const_string("sottovettore", 12, & index); sottovettore = create_array(); valore.val_type = AWK_array; valore.array_cookie = subarray; if (! set_array_element(a_cookie, & index, & value)) { printf("fill_in_array: set_array_element non riuscito\n"); return; } sottovettore = valore.array_cookie;
Il passo finale è di aggiungere al sottovettore un suo proprio elemento:
(void) make_const_string("pippo", 5, & index); (void) make_const_string("pluto", 5, & value); if (! set_array_element(sottovettore, & index, & value)) { printf("fill_in_array: set_array_element non riuscito\n"); return; } }
Ecco uno script di esempio che carica l’estensione e quindi stampa il valore di tutti gli elementi del vettore, invocando nuovamente se stessa nel caso che un particolare elemento sia a sua volta un vettore:
@load "subarray" function dumparray(name, vettore, i) { for (i in vettore) if (isarray(vettore[i])) dumparray(name "[\"" i "\"]", vettore[i]) else printf("%s[\"%s\"] = %s\n", name, i, vettore[i]) } BEGIN { dumparray("new_array", new_array); }
Ecco il risultato dell’esecuzione dello script:
$ AWKLIBPATH=$PWD ./gawk -f subarray.awk -| new_array["sottovettore"]["pippo"] = pluto -| new_array["salve"] = mondo -| new_array["risposta"] = 42
(Vedi Trovare le estensioni per ulteriori dettagli sulla
variabile d’ambiente AWKLIBPATH
.)
Successivo: Variabili dell'estensione API, Precedente: Manipolazione di vettori, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
La seguente funzione consente alle estensioni di accedere e di manipolare delle ridirezioni.
awk_bool_t get_file(const char *name,
size_t name_len,
const char *tipofile,
int fd,
const awk_input_buf_t **ibufp,
const awk_output_buf_t **obufp);
Ricerca il file name
nella tabella interna di ridirezione di
gawk
.
Se name
è NULL
o name_len
è zero, restituisce
i dati del file in input correntemente aperto il cui nome è memorizzato in
FILENAME
.
(Questa chiamata non usa l’argomento filetype
, che, quindi, può essere
lasciato indefinito).
Se il file non è già aperto, tenta di aprirlo.
L’argomento filetype
deve terminare con uno zero binario, e dovrebbe
dovrebbe avere uno di questi valori:
">"
Un file aperto in output.
">>"
Un file aperto in output, record aggiunti a fine file, dopo quelli già esistenti [append].
"<"
Un file aperto in input.
"|>"
Una pipe aperta in output.
"|<"
Una pipe aperta in input.
"|&"
Un coprocesso bidirezionale.
In caso di errore, restituisce il valore awk_false
.
Altrimenti, restituisce
awk_true
, insieme a ulteriori informazioni sulla ridirezione
nei puntatori ibufp
e obufp
.
Per ridirezioni di input il valore *ibufp
non dovrebbe essere
NULL
, mentre *obufp
dovrebbe essere NULL
.
Per ridirezioni di output,
il valore di *obufp
non dovrebbe essere NULL
, e *ibufp
dovrebbe essere NULL
. Per coprocessi bidirezionali, nessuno dei due
valori dovrebbe essere NULL
.
Normalmente, l’estensione è interessata a (*ibufp)->fd
e/o fileno((*obufp)->fp)
. Se il file non è già
aperto, e l’argomento fd
non è negativo, gawk
userà quel descrittore di file invece di aprire il file nella
maniera solita. Se l’fd
non è negativo, ma il file esiste già,
gawk
ignora l’fd
e restituisce il file esistente. È
responsabilità del chiamante notare che né l’fd
nella struttura
restituita awk_input_buf_t
, né l’fd
nella struttura restituita
awk_output_buf_t
contiene il valore richiesto.
Si noti che fornire un descrittore di file non è al momento supportato
per le pipe. Tuttavia, l’utilizzo di un descrittore di file
dovrebbe essere possibile per socket in input, output,
aggiunta-a-fine-file (append), e bidirezionale (coprocessi).
Se filetype
è bidirezionale, gawk
presuppone che sia un
socket! Si noti che nel caso
bidirezionale i descrittori di file in input e output possono essere
differenti.
Per essere sicuri che tutto sia andato bene, si deve controllare che uno dei due
corrisponda alla richiesta.
Si prevede che questa funzione API verrà usata per parallelizzare l’I/O e rendere disponibile una libreria per i socket.
Successivo: Codice predefinito di un'estensione API, Precedente: Ridirezione API, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
L’API fornisce due insiemi di variabili. Il primo insieme contiene
informazioni sulla versione dell’API (sia la versione dell’estensione
compilata, che quella di gawk
). Il secondo
insieme contiene informazioni su come gawk
è stato invocato.
• Versione dell'estensione: | Informazioni sulla versione API. | |
• Variabili informative di estens. API: | Variabili che forniscono informationi
sull’invocazione di gawk .
|
Successivo: Variabili informative di estens. API, Su: Variabili dell'estensione API [Contenuti][Indice]
L’API fornisce sia un numero di versione “principale” che uno “secondario”. Le versioni dell’API sono disponibili al momento della compilazione, come definizioni per il preprocessore C, a supporto della compilazione condizionale, e come elencazione di costanti per facilitare il debug:
versione API | Definiz. Preprocessore C | Costante di elenco |
---|---|---|
Major | gawk_api_major_version | GAWK_API_MAJOR_VERSION |
Minor | gawk_api_minor_version | GAWK_API_MINOR_VERSION |
Tabella 16.2: Costanti delle versioni API gawk
La versione secondaria aumenta quando nuove funzioni sono aggiunte all’API.
Tali nuove funzioni sono sempre aggiunte alla fine della struct
dell’API.
La versione principale aumenta (e la versione secondaria torna a zero) se qualche tipo di dati cambia dimensione o si modifica l’ordine dei campi, o se qualcuna delle funzioni esistenti cambia il livello di versione.
Può capitare che un’estensione sia stata compilata con una versione
dell’API ma caricata da una versione di gawk
che ne usa una
differente. Per questo motivo, la versione principale e quella secondaria
dell’API della versione in uso di gawk
sono incluse nella
struct
dell’API come costanti intere in sola lettura:
api->major_version
La versione principale di gawk
in esecuzione.
api->minor_version
La versione secondaria di gawk
in esecuzione.
Dipende dall’estensione decidere se ci sono incompatibilità con l’API. Tipicamente, basta un controllo di questo tipo:
if (api->major_version != GAWK_API_MAJOR_VERSION || api->minor_version < GAWK_API_MINOR_VERSION) { fprintf(stderr, "estensione_pippo: discordanza di versione \ con gawk!\n"); fprintf(stderr, "\tLa mia versione (%d, %d), versione gawk \ (%d, %d)\n", GAWK_API_MAJOR_VERSION, GAWK_API_MINOR_VERSION, api->major_version, api->minor_version); exit(1); }
Questo codice è incluso nella macro generica dl_load_func()
presente in gawkapi.h (trattata
in
Codice predefinito di un'estensione API).
Precedente: Versione dell'estensione, Su: Variabili dell'estensione API [Contenuti][Indice]
L’API fornisce accesso a parecchie variabili che descrivono
se le opzioni della riga di comando corrispondenti sono state specificate
quando gawk
è stato chiamato. Le variabili sono:
do_debug
Questa variabile è true se gawk
è stato invocato con l’opzione --debug.
do_lint
Questa variabile è true se gawk
è stato invocato con l’opzione --lint.
do_mpfr
Questa variabile è true se gawk
è stato invocato con l’opzione --bignum.
do_profile
Questa variabile è true se gawk
è stato invocato con l’opzione --profile.
do_sandbox
Questa variabile è true se gawk
è stato invocato con l’opzione --sandbox.
do_traditional
Questa variabile è true se gawk
è stato invocato con l’opzione --traditional.
Il valore di do_lint
può cambiare se il codice awk
modifica la variabile predefinita LINT
(vedi Variabili predefinite).
Gli altri valori non dovrebbero cambiare durante l’esecuzione.
Successivo: Modifiche dalla versione API 1, Precedente: Variabili dell'estensione API, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
Come già detto (vedi Panoramica sul meccanismo delle estensioni), le definizioni di funzioni qui presentate sono in realtà delle macro. Per usare queste macro, l’estensione deve fornire una piccola quantità di codice predefinito (variabili e funzioni) nella parte iniziale del file sorgente, usando dei nomi standard, come descritto qui sotto. Il codice predefinito in questione è anche descritto nel file di intestazione gawkapi.h:
/* Codice predefinito: */ int plugin_is_GPL_compatible; static gawk_api_t *const api; static awk_ext_id_t ext_id; static const char *ext_version = NULL; /* o … = "qualche stringa" */ static awk_ext_func_t func_table[] = { { "name", do_name, 1, 0, awk_false, NULL }, /* … */ }; /* O: */ static awk_bool_t (*init_func)(void) = NULL; /* OPPURE: */ static awk_bool_t init_mia_estensione(void) { … } static awk_bool_t (*init_func)(void) = init_mia_estensione; dl_load_func(func_table, qualche_nome, "name_space_in_quotes")
Queste variabili e funzioni sono:
int plugin_is_GPL_compatible;
Qui si dichiara che l’estensione è compatibile con la licenza GNU GPL (vedi Copia).
Se l’estensione non ha questa variabile, non verrà caricata da gawk
(vedi Licenza delle estensioni).
static gawk_api_t *const api;
Questa variabile globale static
dovrebbe essere impostata per
puntare al puntatore
gawk_api_t
che gawk
passa alla funzione (dell’estensione)
dl_load()
. Questa variabile è usata da tutte le macro.
static awk_ext_id_t ext_id;
Questa variabile globale static
dovrebbe essere impostata al valore
awk_ext_id_t
che gawk
passa alla funzione dl_load()
.
Questa variabile è usata da tutte le macro.
static const char *ext_version = NULL; /* o … = "qualche stringa" */
Questa variabile globale static
dovrebbe essere impostata
a NULL
oppure puntare a una stringa che contiene il nome e la
versione dell’estensione.
static awk_ext_func_t func_table[] = { … };
Questo è un vettore di una o più strutture awk_ext_func_t
,
come descritto in precedenza (vedi Funzioni di estensione).
Può essere usato in seguito per più chiamate a
add_ext_func()
.
static awk_bool_t (*init_func)(void) = NULL;
OR
static awk_bool_t init_mia_estensione(void) { … }
static awk_bool_t (*init_func)(void) = init_mia_estensione;
Se qualche lavoro di inizializzazione è necessario, si dovrebbe definire una
funzione all’uopo (crea variabili, apre file, etc.)
e poi definire il puntatore init_func
che punti alla funzione
stessa.
La funzione dovrebbe restituire awk_false
se non va a buon fine
o awktrue
se tutto va bene.
Se un’inizializzazione non è necessaria, si definisca il puntatore e
lo si inizializzi a NULL
.
dl_load_func(func_table, qualche_nome, "nome_spazio_tra_doppi_apici")
Questa macro genera una funzione dl_load()
che farà
tutte le inizializzazioni necessarie.
Lo scopo di tutte le variabili e dei vettori è di far sì che la
funzione dl_load()
(richiamata dalla macro dl_load_func()
)
faccia tutto il lavoro standard necessario, qui descritto:
gawk
o se la versione secondaria
dell’estensione è maggiore di quella di gawk
, stampa un messaggio
di errore fatale ed esce.
func_table
.
Se qualche caricamento non riesce, stampa un messaggio di
avvertimento ma continua l’esecuzione.
init_func
non è NULL
, chiama la
funzione da esso puntata. Se questa restituisce awk_false
, stampa un
messaggio di avvertimento.
ext_version
non è NULL
, registra la
stringa di versione con gawk
.
Precedente: Codice predefinito di un'estensione API, Su: Descrizione dell'API delle estensioni [Contenuti][Indice]
La versione API corrente non è compatibile a livello binario con la
versione 1 dell’API.
Le funzioni di estensione vanno ricompilate per poterle usare con la versione
corrente di gawk
.
Fortunatamente, fatti salvi alcuni possibili avvertimenti a livello di
compilazione, l’API rimane compatibile a livello di codice sorgente con la
precedente versione API. Le differenze più rilevanti sono gli ulteriori
campi nella struttura awk_ext_func_t
, e l’aggiunta del terzo argomento
nella funzione di implementazione in linguaggio C.
Successivo: Esempio di estensione, Precedente: Descrizione dell'API delle estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
gawk
trova le estensioni compilateLe estensioni compilate vanno installate in una directory dove
gawk
possa trovarle. Se gawk
è configurato e
installato nella maniera di default, la directory dove trovare le
estensioni è /usr/local/lib/gawk. Si può anche specificare un
percorso di ricerca contenente una lista di directory da esaminare per la
ricerca di estensioni compilate.
Vedi AWKLIBPATH (Variabile) per ulteriori dettagli.
Successivo: Esempi di estensione, Precedente: Trovare le estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
In qualunque posto vai, là tu sei.
Due utili funzioni che non sono in awk
sono chdir()
(per
permettere a un programma awk
di cambiare directory di lavoro) e
stat()
(per far sì che un programma awk
possa raccogliere informazioni
su un dato file).
Per illustrare l’azione dell’API, questa sezione fornisce
queste funzioni a gawk
in un’estensione.
• Descrizione interna file: | Quello che le nuove funzioni faranno | |
• Operazioni interne file: | Codice per gestire file all’interno | |
• Usare operazioni interne file: | Come usare un’estensione esterna |
Successivo: Operazioni interne file, Su: Esempio di estensione [Contenuti][Indice]
chdir()
e stat()
Questa
sezione mostra come usare le nuove funzioni a
livello di awk
una volta che siano state integrate nell’interprete
del programma gawk
in esecuzione. Usare chdir()
è molto
semplice. Richiede un solo argomento, la nuova directory su cui
posizionarsi:
@load "filefuncs" … newdir = "/home/arnold/funstuff" ret = chdir(newdir) if (ret < 0) { printf("non riesco a passare a %s: %s\n", newdir, ERRNO) > "/dev/stderr" exit 1 } …
Il valore restituito è negativo se la chiamata a chdir()
non è riuscita,
ed ERRNO
(vedi Variabili predefinite) è impostato a una stringa
che descrive l’errore.
Usare stat()
è un po’ più complicato. La funzione scritta in C
stat()
riempie una struttura che ha una certa quantità di informazioni.
La maniera corretta per immagazzinarle in awk
è quella di riempire
un vettore associativo con le informazioni appropriate:
file = "/home/arnold/.profile"
ret = stat(file, fdata)
if (ret < 0) {
printf("non è stato possibile eseguire stat
per %s: %s\n",
file, ERRNO) > "/dev/stderr"
exit 1
}
printf("dimensione di %s è %d byte\n", file, fdata["size"])
La funzione stat()
svuota sempre il vettore che contiene i dati,
anche nel caso che la chiamata a stat()
non riesca. I seguenti
elementi vengono restituiti dalla funzione:
"name"
Il nome del file oggetto della chiamata a stat()
.
"dev"
"ino"
I numeri di device e di inode, rispettivamente.
"mode"
Il modo del file, in formato numerico. Questo include sia il tipo di file che i suoi permessi di accesso.
"nlink"
Il numero di collegamenti fisici del file (stesso file con diversi nomi).
"uid"
"gid"
Gli identificativi di utente e di gruppo del possessore del file.
"size"
La dimensione in byte del file.
"blocks"
Il numero di blocchi su disco realmente occupati dal file. Questo può non essere proporzionale alla dimensione del file se il file ha delle lacune [ossia se solo alcune parti del file esistono veramente, il resto non è ancora stato riempito].
"atime"
"mtime"
"ctime"
La data e ora dell’ultimo accesso, modifica, e aggiornamento dell’inode,
rispettivamente. Questi sono delle marcature temporali numeriche
(misurate in secondi dal
01 gennaio 1970), che possono essere formattate dalla funzione
strftime()
(vedi Funzioni di tempo).
"pmode"
La modalità stampabile (“printable mode”) del file.
Questo è una stringa che rappresenta
il tipo del file e i permessi di accesso, come sono visualizzati da
‘ls -l’—per esempio, "drwxr-xr-x"
.
"type"
Una stringa stampabile che descrive il tipo di file. Il valore è uno dei seguenti:
"blockdev"
"chardev"
Il file è un dispositico a blocchi o a caratteri (“file speciale”).
"directory"
Il file è una directory.
"fifo"
Il file è una pipe denominata (nota anche come FIFO [First In First Out]).
"file"
Il file è un file normale.
"socket"
Il file è un socket AF_UNIX
(“Unix domain”) nel
filesystem.
"symlink"
Il file è un collegamento simbolico.
"devbsize"
La dimensione di un blocco per l’elemento indicizzato da "blocks"
.
Questa informazione è derivata dalla costante DEV_BSIZE
definita in <sys/param.h>
nella maggior parte dei sistemi,
o dalla costante S_BLKSIZE
in <sys/stat.h>
nei sistemi BSD.
Per alcuni altri sistemi il valore si basa su una conoscenza a priori
delle caratteristiche di un particolare sistema.
Se non si riesce a determinare il valore, viene
restituito quello di default, che è 512.
Possono essere presenti diversi altri elementi, a seconda del
sistema operativo e del tipo di file.
Si può controllarne la presenza dal programma awk
per mezzo
dell’operatore in
(vedi Visitare elementi):
"blksize"
La dimensione preferita di un blocco per effettuare operazioni di I/O sul file.
Questo campo non è presente nella struttura C stat
di tutti i sistemi
che rispettano lo standard POSIX.
"linkval"
Se il file è un collegamento simbolico, questo elemento è il nome del file puntato dal collegamento simbolico (cioè, il valore del collegamento).
"rdev"
"major"
"minor"
Se il file è un dispositivo a blocchi o a caratteri, questi valori rappresentano il numero del dispositivo e, rispettivamente, le componenti principale e secondaria di quel numero.
Successivo: Usare operazioni interne file, Precedente: Descrizione interna file, Su: Esempio di estensione [Contenuti][Indice]
chdir()
e stat()
Questo è il codice C per queste estensioni.113
Il file include alcuni file di intestazione standard, e poi il file di intestazione gawkapi.h, che fornisce le definizioni dell’API. A queste seguono le dichiarazioni di variabili, necessarie per usare le macro dell’API e il codice predefinito (vedi Codice predefinito di un'estensione API):
#ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <assert.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include "gawkapi.h" #include "gettext.h" #define _(msgid) gettext(msgid) #define N_(msgid) msgid #include "gawkfts.h" #include "stack.h" static const gawk_api_t *api; /* per consentire il funzionamento delle macro di utilità */ static awk_ext_id_t *ext_id; static awk_bool_t init_filefuncs(void); static awk_bool_t (*init_func)(void) = init_filefuncs; static const char *ext_version = "filefuncs extension: version 1.0"; int plugin_is_GPL_compatible;
Per convenzione, per una funzione awk
di nome pippo()
,
la funzione C che la implementa è chiamata do_pippo()
. La funzione
dovrebbe avere due argomenti. Il primo è un numero int
, chiamato
nargs
, che rappresenta il numero di argomenti passato alla funzione.
Il secondo è un puntatore a una struttura awk_value_t
, normalmente
chiamata risultato
:
/* do_chdir --- fornisce funzione chdir() caricata dinamicamente per gawk */ static awk_value_t * do_chdir(int nargs, awk_value_t *risultato, struct awk_ext_func *non_usata) { awk_value_t newdir; int ret = -1; assert(risultato != NULL);
La variabile newdir
rappresenta la nuova directory nella quale cambiare, che è ottenuta
tramite la funzione get_argument()
. Si noti che il primo argomento è
quello numero zero.
Se l’argomento è stato trovato con successo, la funzione invoca la chiamata di
sistema chdir()
. In caso contrario, se la chdir()
non riesce,
viene aggiornata la variabile ERRNO
:
if (get_argument(0, AWK_STRING, & newdir)) { ret = chdir(newdir.str_value.str); if (ret < 0) update_ERRNO_int(errno); }
Infine, la funzione restituisce il codice di ritorno da chdir
a
livello di awk
:
return make_number(ret, risultato); }
L’estensione stat()
è più impegnativa. Dapprima abbiamo
una funzione che trasforma la stringa di autorizzazione numerica
(mode) in una rappresentazione stampabile
(p.es., il codice ottale 0644
diviene ‘-rw-r--r--’). Questa
parte è qui omessa per brevità.
/* format_mode --- trasforma il campo mode di stat in qualcosa di leggibile */ static char * format_mode(unsigned long fmode) { … }
Viene poi una funzione per leggere dei collegamenti simbolici, anche questa omessa per brevità:
/* read_symlink --- legge un collegamento simbolico in un buffer allocato. … */ static char * read_symlink(const char *fname, size_t bufsize, ssize_t *linksize) { … }
Due funzioni ausiliarie semplificano l’immissione di valori nel
vettore che conterrà il risultato della chiamata a stat()
:
/* array_set --- imposta un elemento di un vettore */ static void array_set(awk_array_t vettore, const char *sub, awk_value_t *valore) { awk_value_t index; set_array_element(vettore, make_const_string(sub, strlen(sub), & index), valore); } /* array_set_numeric --- imposta un elemento di un vettore con un numero */ static void array_set_numeric(awk_array_t vettore, const char *sub, double num) { awk_value_t tmp; array_set(vettore, sub, make_number(num, & tmp)); }
La seguente funzione fa il grosso del lavoro per riempire il vettore dei
risultati awk_array_t
con valori ottenuti
da una struct stat
valida. Questo lavoro è fatto in una funzione
separata per supportare sia la funzione
stat()
per gawk
, che l’estensione fts()
,
che è inclusa nello stesso file, ma non
è mostrata qui
(vedi Esempio di estensione funzioni file).
La prima parte della funzione è la dichiarazione delle variabili, compresa una tabella per tradurre i tipi di file in stringhe:
/* fill_stat_array --- fa il lavoro di riempire un vettore con informazioni da stat */ static int fill_stat_array(const char *name, awk_array_t vettore, struct stat *sbuf) { char *pmode; /* mode stampabile */ const char *type = "unknown"; awk_value_t tmp; static struct ftype_map { unsigned int mask; const char *type; } ftype_map[] = { { S_IFREG, "file" }, { S_IFBLK, "blockdev" }, { S_IFCHR, "chardev" }, { S_IFDIR, "directory" }, #ifdef S_IFSOCK { S_IFSOCK, "socket" }, #endif #ifdef S_IFIFO { S_IFIFO, "fifo" }, #endif #ifdef S_IFLNK { S_IFLNK, "symlink" }, #endif #ifdef S_IFDOOR /* Stranezza Solaris */ { S_IFDOOR, "door" }, #endif /* S_IFDOOR */ }; int j, k;
Il vettore di destinazione è svuotato di elementi, e poi il codice riempie
i vari elementi prendendoli dai valori presenti in struct stat
:
/* svuota il vettore */ clear_array(vettore); /* riempie il vettore */ array_set(vettore, "name", make_const_string(name, strlen(name), & tmp)); array_set_numeric(vettore, "dev", sbuf->st_dev); array_set_numeric(vettore, "ino", sbuf->st_ino); array_set_numeric(vettore, "mode", sbuf->st_mode); array_set_numeric(vettore, "nlink", sbuf->st_nlink); array_set_numeric(vettore, "uid", sbuf->st_uid); array_set_numeric(vettore, "gid", sbuf->st_gid); array_set_numeric(vettore, "size", sbuf->st_size); array_set_numeric(vettore, "blocks", sbuf->st_blocks); array_set_numeric(vettore, "atime", sbuf->st_atime); array_set_numeric(vettore, "mtime", sbuf->st_mtime); array_set_numeric(vettore, "ctime", sbuf->st_ctime); /* per dispositivi a blocchi o carattere, aggiunge rdev, e il numero principale e secondario */ if (S_ISBLK(sbuf->st_mode) || S_ISCHR(sbuf->st_mode)) { array_set_numeric(vettore, "rdev", sbuf->st_rdev); array_set_numeric(vettore, "major", major(sbuf->st_rdev)); array_set_numeric(vettore, "minor", minor(sbuf->st_rdev)); }
L’ultima parte della funzione fa alcune aggiunte selettive al vettore di destinazione, a seconda che siano disponibili o no certi campi e/o il tipo del file. Viene poi restituito zero, per indicare che tutto è andato bene:
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE array_set_numeric(vettore, "blksize", sbuf->st_blksize); #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ pmode = format_mode(sbuf->st_mode); array_set(vettore, "pmode", make_const_string(pmode, strlen(pmode), & tmp)); /* per collegamenti simbolici, si aggiunge un campo linkval */ if (S_ISLNK(sbuf->st_mode)) { char *buf; ssize_t linksize; if ((buf = read_symlink(name, sbuf->st_size, & linksize)) != NULL) array_set(vettore, "linkval", make_malloced_string(buf, linksize, & tmp)); else warning(ext_id, _("stat: non riesco a leggere il \ collegamento simbolico `%s'"), name); } /* aggiunge il tipo di campo */ type = "unknown"; /* non dovrebbe succedere */ for (j = 0, k = sizeof(ftype_map)/sizeof(ftype_map[0]); j < k; j++) { if ((sbuf->st_mode & S_IFMT) == ftype_map[j].mask) { type = ftype_map[j].type; break; } } array_set(vettore, "type", make_const_string(type, strlen(type), & tmp)); return 0; }
Del terzo argomento passato a stat()
non si era ancora parlato.
Questo argomento è facoltativo. Se presente, dice a do_stat()
di
usare la chiamata di sistema stat()
invece della chiamata di sistema
lstat()
. Questo avviene attraverso un puntatore a funzione:
statfunc
.
statfunc
è inizializzato per puntare a lstat()
(invece che a
stat()
) per ottenere le informazioni relative al file, nel caso che
il file in questione sia un
collegamento simbolico. Tuttavia, se il terzo argomento è specificato,
statfunc
viene modificato in modo da puntare a stat()
.
Ecco la funzione do_stat()
, che inizia con la dichiarazione delle
variabili e un controllo degli argomenti passati dal chiamante:
/* do_stat --- fornisce una funzione stat() per gawk */ static awk_value_t * do_stat(int nargs, awk_value_t *risultato, struct awk_ext_func *non_usata) { awk_value_t file_param, array_param; char *name; awk_array_t vettore; int ret; struct stat sbuf; /* per default si usa lstat() */ int (*statfunc)(const char *path, struct stat *sbuf) = lstat; assert(risultato != NULL);
A questo punto inizia l’elaborazione vera e propria. Per prima cosa, la
funzione esamina gli argomenti.
Poi, ottiene le informazioni relative al file. Se la funzione chiamata
(lstat()
o stat()
) restituisce un errore, il codice imposta
ERRNO
e torna al chiamante:
/* file è il primo argomento, il vettore per contenere i risultati è il secondo */ if ( ! get_argument(0, AWK_STRING, & file_param) || ! get_argument(1, AWK_ARRAY, & array_param)) { warning(ext_id, _("stat: parametri errati")); return make_number(-1, risultato); } if (nargs == 3) { statfunc = stat; } name = file_param.str_value.str; vettore = array_param.array_cookie; /* svuota sempre il vettore all'inizio */ clear_array(vettore); /* chiama stat per il file; in caso di errore, imposta ERRNO e ritorna */ ret = statfunc(name, & sbuf); if (ret < 0) { update_ERRNO_int(errno); return make_number(ret, risultato); }
Il lavoro noioso è svolto da fill_stat_array()
, visto in
precedenza. Alla fine, la funzione restituisce il codice di ritorno
impostato da fill_stat_array()
:
ret = fill_stat_array(name, vettore, & sbuf); return make_number(ret, risultato); }
Infine, è necessario fornire la “colla” che aggrega
le nuove funzioni a gawk
.
L’estensione filefuncs
comprende anche una funzione
fts()
, qui omessa
(vedi Esempio di estensione funzioni file).
È anche prevista una funzione di
inizializzazione:
/* init_filefuncs --- routine di initializazione */ static awk_bool_t init_filefuncs(void) { … }
Siamo quasi alla fine. Serve un vettore di strutture awk_ext_func_t
per caricare ogni funzione in gawk
:
static awk_ext_func_t func_table[] = { { "chdir", do_chdir, 1, 1, awk_false, NULL }, { "stat", do_stat, 3, 2, awk_false, NULL }, … };
Ogni estensione deve avere una routine di nome dl_load()
per caricare
tutto ciò che occorre caricare. La cosa più semplice è di usare la macro
dl_load_func()
in gawkapi.h
:
/* definizione della funzione dl_load() usando la macro standard */ dl_load_func(func_table, filefuncs, "")
Abbiamo finito!
Precedente: Operazioni interne file, Su: Esempio di estensione [Contenuti][Indice]
Dopo aver scritto il codice, dev’essere possibile aggiungerlo in fase
di esecuzione all’interprete gawk
. Per prima cosa, il codice
va compilato. Supponendo che le funzioni siano in
un file di nome filefuncs.c, e che idir sia la posizione
del file di intestazione gawkapi.h,
i seguenti passi114 creano una libreria condivisa GNU/Linux:
$ gcc -fPIC -shared -DHAVE_CONFIG_H -c -O -g -Iidir filefuncs.c $ gcc -o filefuncs.so -shared filefuncs.o
Una volta creata la libreria, questa viene caricata usando la parola
chiave @load
:
# file testff.awk @load "filefuncs" BEGIN { "pwd" | getline curdir # salva la directory corrente close("pwd") chdir("/tmp") system("pwd") # verifica l'avvenuto cambio di directory chdir(curdir) # torna indietro print "Info per testff.awk" ret = stat("testff.awk", data) print "ret =", ret for (i in data) printf "data[\"%s\"] = %s\n", i, data[i] print "testff.awk modified:", strftime("%m %d %Y %H:%M:%S", data["mtime"]) print "\nInfo per JUNK" ret = stat("JUNK", data) print "ret =", ret for (i in data) printf "data[\"%s\"] = %s\n", i, data[i] print "JUNK modified:", strftime("%m %d %Y %H:%M:%S", data["mtime"]) }
La variabile d’ambiente AWKLIBPATH
dice a
gawk
dove è possibile trovare le estensioni (vedi Trovare le estensioni).
La variabile viene impostata alla directory corrente, e quindi viene eseguito
il programma:
$ AWKLIBPATH=$PWD gawk -f testff.awk -| /tmp -| Info per testff.awk -| ret = 0 -| data["blksize"] = 4096 -| data["devbsize"] = 512 -| data["mtime"] = 1412004710 -| data["mode"] = 33204 -| data["type"] = file -| data["dev"] = 2053 -| data["gid"] = 1000 -| data["ino"] = 10358899 -| data["ctime"] = 1412004710 -| data["blocks"] = 8 -| data["nlink"] = 1 -| data["name"] = testff.awk -| data["atime"] = 1412004716 -| data["pmode"] = -rw-rw-r-- -| data["size"] = 666 -| data["uid"] = 1000 -| testff.awk modified: 09 29 2014 18:31:50 -| -| Info per JUNK -| ret = -1 -| JUNK modified: 01 01 1970 02:00:00
Successivo: gawkextlib, Precedente: Esempio di estensione, Su: Estensioni dinamiche [Contenuti][Indice]
gawk
Questa sezione fornisce una breve panoramica degli esempi di
estensione inclusi nella distribuzione di gawk
. Alcune di esse
sono destinate per l’uso in produzione (p.es., le estensioni
filefuncs
, readdir
, e
inplace
). Altre sono state scritte principalmente per mostrare come
si usa l’estensione API.
• Esempio di estensione funzioni file: | L’esempio che usa funzioni file. | |
• Esempio di estensione Fnmatch: | Un’interfaccia a fnmatch() .
| |
• Esempio di estensione Fork: | Un’interfaccia a fork() e
altre funzioni di processo.
| |
• Esempio di estensione Inplace: | Consentire modifica diretta dei file. | |
• Esempio di estensione Ord: | Conversioni a valore e a stringa di caratteri. | |
• Esempio di estensione Readdir: | Un’interfaccia a readdir() .
| |
• Esempio di estensione Revout: | Semplice post-processore per invertire la stringa in output. | |
• Esempio di estensione Rev2way: | Processore bidirezionale per invertire la stringa in output. | |
• Esempio di estensione Rwarray: | Serializzare il vettore in un file. | |
• Esempio di estensione Readfile: | Leggere un intero file in una stringa. | |
• Esempio di estensione Time: | Un’interfaccia a gettimeofday()
e sleep() .
| |
• Esempio di estensione API Test: | Test per la API. |
Successivo: Esempio di estensione Fnmatch, Su: Esempi di estensione [Contenuti][Indice]
L’estensione filefuncs
include tre funzioni diverse, come descritto sotto.
L’uso è il seguente:
@load "filefuncs"
Questo è il modo per caricare l’estensione.
risultato = chdir("/qualche/directory")
La funzione chdir()
invoca a sua volta la chiamata di sistema
chdir()
per cambiare la directory corrente. Restituisce zero
se tutto va bene o un valore minore di zero in caso di errore.
In quest’ultimo caso, viene aggiornata la variabile ERRNO
.
risultato = stat("/qualche/percorso", statdata
[, follow
])
La funzione stat()
invoca a sua volta la chiamata di sistema
stat()
.
Restituisce zero se tutto va bene o un valore minore di zero in caso di
errore.
In quest’ultimo caso, viene aggiornata la variabile ERRNO
.
Per default, viene usata la chiamata di sistema lstat()
.
Tuttavia, se alla funzione viene passato un terzo argomento, questa invoca
invece stat()
.
In tutti i casi, il vettore statdata
viene preventivamente svuotato.
Quando la chiamata a stat()
riesce, viene riempito il vettore
statdata
con le informazioni ottenute dal fileystem, come segue:
Indice | Campo in struct stat | Tipo file |
---|---|---|
"name" | Il nome-file | Tutti |
"dev" | st_dev | Tutti |
"ino" | st_ino | Tutti |
"mode" | st_mode | Tutti |
"nlink" | st_nlink | Tutti |
"uid" | st_uid | Tutti |
"gid" | st_gid | Tutti |
"size" | st_size | Tutti |
"atime" | st_atime | Tutti |
"mtime" | st_mtime | Tutti |
"ctime" | st_ctime | Tutti |
"rdev" | st_rdev | Dispositivi |
"major" | st_major | Dispositivi |
"minor" | st_minor | Dispositivi |
"blksize" | st_blksize | Tutti |
"pmode" | Una versione leggibile del valore dell’autorizzazione,
come quello stampato dal comando
ls (per esempio, "-rwxr-xr-x" ) | Tutti |
"linkval" | Il valore del collegamento simbolico | Collegamenti simbolici |
"type" | Il tipo del file in formato stringa—può essere
"file" ,
"blockdev" ,
"chardev" ,
"directory" ,
"socket" ,
"fifo" ,
"symlink" ,
"door"
o
"unknown"
(non tutti i sistemi supportano tutti i tipi file) | Tutti |
flags = or(FTS_PHYSICAL, ...)
risultato = fts(pathlist, flags, filedata)
Percorre gli alberi di file elencati in pathlist
e riempie il vettore
filedata
, come descritto qui di seguito. flags
è l’operazione
OR bit a bit di parecchi valori predefiniti, pure descritti
più sotto.
Restituisce zero in assenza di errori, in caso contrario restituisce -1.
La funzione fts()
invoca a sua volta la routine di libreria C
fts()
per percorrere gerarchie di file. Invece di restituire i dati
relativi ai file uno per volta in sequenza,
riempie un vettore multidimensionale con i dati di ogni file e directory
che risiedono nelle gerarchie richieste.
Gli argomenti sono i seguenti:
pathlist
Un vettore di nomi-file. Sono usati i valori dei singoli elementi; gli indici che puntano a tali valori vengono ignorati.
flags
Questo dovrebbe essere l’OR bit a bit di uno o più dei
seguenti valori dei flag costanti predefiniti.
Almeno uno dei due flag FTS_LOGICAL
o FTS_PHYSICAL
dev’essere impostato; in caso contrario
fts()
restituisce una segnalazione di errore e imposta ERRNO
.
I flag sono:
FTS_LOGICAL
Passa in rassegna i file in modo “logico”, e quindi l’informazione restituita
per un collegamento simbolico è quella relativa al file puntato, e non al
collegamento simbolico stesso. Questo flag è mutuamente esclusivo con
FTS_PHYSICAL
.
FTS_PHYSICAL
Passa in rassegna i file in modo “fisico”, e quindi l’informazione restituita
per un collegamento simbolico è quella relativa al collegamento simbolico
stesso. Questo flag è mutuamente esclusivo con FTS_LOGICAL
.
FTS_NOCHDIR
Per migliorare le prestazioni, la routine di libreria C fts()
cambia directory mentre percorre una gerarchia di file. Questo flag
disabilita quell’ottimizzazione.
FTS_COMFOLLOW
Si accede al file puntato da un collegamento simbolico esistente in pathlist
,
anche se FTS_LOGICAL
non è stato impostato.
FTS_SEEDOT
Per default, la routine di libreria C fts()
non restituisce
informazioni per i file
"." (punto) e ".." (punto-punto). Quest’opzione richiede
l’informazione anche per "..". (L’estensione ritorna sempre
l’informazione per "."; maggiori dettagli più sotto.)
FTS_XDEV
Mentre si percorre un filesystem, non passare mai a un filesystem montato diverso da quello in cui si opera.
filedata
Il vettore filedata
contiene i risultati.
La funzione fts()
lo svuota all’inizio. In seguito viene creato
un elemento in filedata
per ogni elemento in pathlist
.
L’indice è il nome della directory o del file specificato in pathlist
.
L’elemento puntato da questo indice è a sua volta un vettore. Ci sono due
casi:
In questo caso, il vettore contiene due o tre elementi:
"path"
Il percorso completo di questo file, a partire dalla directory radice (“root”)
indicata nel vettore pathlist
.
"stat"
Questo elemento è esso stesso un vettore, contenente le stesse informazioni
fornite dalla funzione stat()
vista in precedenza a proposito del suo
argomento
statdata
. L’elemento può non essere presente se la chiamata
di sistema stat()
per il file non è riuscita.
"error"
Se qualche tipo di errore si verifica durante l’elaborazione, il vettore
conterrà anche un elemento con chiave "error"
, che è una stringa
che descrive l’errore.
In questo caso, nel vettore viene creato un elemento per ogni elemento
contenuto nella directory. Se un elemento della directory è un
file, l’azione del programma è la stessa descritta sopra per un file.
Se invece la directory contiene delle sottodirectory, l’elemento creato
nel vettore è (ricorsivamente) a sua volta un vettore che descrive la
sottodirectory. Se fra i flag è stato
specificato il flag FTS_SEEDOT
,
ci sarà anche un elemento di nome
".."
. Questo elemento sarà un vettore contenente i dati restituiti
da un’invocazione di stat()
.
Inoltre, ci sarà un elemento il cui indice è "."
.
Questo elemento è un vettore contenente gli stessi due o tre elementi che
sono messi a disposizione per un file: "path"
, "stat"
,
ed "error"
.
La funzione fts()
restituisce zero in assenza di errori.
in caso contrario, restituisce -1.
NOTA: L’estensione
fts()
non imita esattamente l’interfaccia fornita dalla routine di libreria Cfts()
, ma sceglie di fornire un’interfaccia basata sui vettori associativi, che è più adeguata per l’uso da parte di un programmaawk
. Questo implica la mancanza di una funzione di confronto, poichégawk
già prevede la possibilità di mettere facilmente nell’ordine desiderato gli elementi di un vettore. Anche se un’interfaccia modellata sufts_read()
avrebbe potuto essere fornita, è sembrato più naturale mettere a disposizione un vettore multidimensionale, che rappresenta la gerarchia dei file e le informazioni relative a ognuno di essi.
Si veda il file test/fts.awk nella distribuzione di gawk
per un esempio di uso dell’estensione fts()
.
Successivo: Esempio di estensione Fork, Precedente: Esempio di estensione funzioni file, Su: Esempi di estensione [Contenuti][Indice]
fnmatch()
Quest’estensione fornisce un’interfaccia per utilizzare la funzione di
libreria C fnmatch()
. Si usa così:
@load "fnmatch"
È questo il modo per caricare l’estensione.
risultato = fnmatch(pattern, stringa, flags)
Il valore restituito è zero se tutto va bene, oppure FNM_NOMATCH
se la funzione non ha trovato alcuna corrispondenza, o
un valore differente, diverso da zero, se si è verificato un errore.
Oltre a rendere disponibile la funzione fnmatch()
, l’estensione
di fnmatch
definisce una costante (FNM_NOMATCH
), e un vettore
con dei valori di flag, di nome FNM
.
Gli argomenti per fnmatch()
sono:
pattern
L’espressione regolare con cui confrontare nome-file
stringa
La stringa nome-file
flags
Può valere zero o essere l’OR bit a bit di uno o più flag
nel vettore FNM
I flag sono i seguenti:
Elemento del vettore | Flag corrispondente definito da fnmatch() |
---|---|
FNM["CASEFOLD"] | FNM_CASEFOLD |
FNM["FILE_NAME"] | FNM_FILE_NAME |
FNM["LEADING_DIR"] | FNM_LEADING_DIR |
FNM["NOESCAPE"] | FNM_NOESCAPE |
FNM["PATHNAME"] | FNM_PATHNAME |
FNM["PERIOD"] | FNM_PERIOD |
Ecco un esempio:
@load "fnmatch" … flags = or(FNM["PERIOD"], FNM["NOESCAPE"]) if (fnmatch("*.a", "pippo.c", flags) == FNM_NOMATCH) print "nessuna corrispondenza"
Successivo: Esempio di estensione Inplace, Precedente: Esempio di estensione Fnmatch, Su: Esempi di estensione [Contenuti][Indice]
fork()
, wait()
, e waitpid()
L’estensione fork
mette a disposizione tre funzioni, come segue:
@load "fork"
Questo è il modo per caricare l’estensione.
pid = fork()
Questa funzione crea un nuovo processo. Il valore restituito è zero nel
processo “figlio” e il numero che identifica il nuovo processo
(pid) nel processo “padre”, o -1
in caso di errore. In quest’ultimo caso, ERRNO
indica il problema.
Nel processo figlio, gli elementi PROCINFO["pid"]
e
PROCINFO["ppid"]
vengono aggiornati per riflettere i valori corretti.
ret = waitpid(pid)
Questa funzione ha un unico argomento numerico, l’identificativo del processo
di cui aspettare l’esito. Il valore di ritorno è quello restituito dalla
chiamata di sistema waitpid()
.
ret = wait()
Questa funzione attende che il primo processo “figlio” termini.
Il valore restituito è quello della chiamata di sistema wait()
.
Non c’è una funzione corrispondente alla chiamata di sistema exec()
.
Ecco un esempio:
@load "fork" … if ((pid = fork()) == 0) print "salve dal processo figlio" else print "salve dal processo padre"
Successivo: Esempio di estensione Ord, Precedente: Esempio di estensione Fork, Su: Esempi di estensione [Contenuti][Indice]
L’estensione inplace
svolge un lavoro simile a quello
dell’opzione -i nel programma di utilità GNU sed
,
che svolge delle funzioni di modifica “al volo” su ogni file in input.
Viene usato il file inplace.awk, caricato dinamicamente, per richiamare
l’estensione in maniera corretta:
# inplace --- carica e richiama l'estensione inplace. @load "inplace" # È buona cosa impostare INPLACE_SUFFIX in modo da fare # una copia di backup. # Per esempio, si potrebbe impostare INPLACE_SUFFIX a .bak # sulla riga di comando, o in una regola BEGIN. # Per default, ogni file specificato sulla riga di comando # verrà modificato sovrascrivendo il file originale. # Ma è possibile evitarlo specificando l'argomento inplace=0 # davanti al nome del file che non si desidera elaborare in questo modo. # Si può poi abilitare di nuovo l'aggiornamento diretto del file # sulla riga di comando, specificando inplace=1 prima del file # che si vuole modificare direttamente. # N.B. La funzione inplace_end() è invocata nelle regole # BEGINFILE ed END, in modo che ogni eventuale azione # in una regola ENDFILE sarà ridiretta come previsto. BEGIN { inplace = 1 # abilitato per default } BEGINFILE { if (_inplace_filename != "") inplace_end(_inplace_filename, INPLACE_SUFFIX) if (inplace) inplace_begin(_inplace_filename = FILENAME, INPLACE_SUFFIX) else _inplace_filename = "" } END { if (_inplace_filename != "") inplace_end(_inplace_filename, INPLACE_SUFFIX) }
Per ogni file elaborato, l’estensione ridirige lo
standard output verso un file temporaneo definito in modo da avere lo stesso
proprietario e le stesse autorizzazioni del file originale. Dopo che il file
è stato elaborato, l’estensione riporta lo standard output alla sua
destinazione originale.
Se INPLACE_SUFFIX
non è una stringa vuota, il file originale è
collegato a un nome-file di backup, creato aggiungendo il
suffisso al nome originale.
Infine, il file temporaneo è rinominato in modo da essere lo stesso del
nome-file originario.
Si noti che l’uso di questa funzionalità può essere controllato specificando ‘inplace=0’ sulla riga di comando, prima del nome del file che non dovrebbe essere elaborato come appena descritto. Si può richiedere ancora l’aggiornamento diretto di un file, specificando l’argomento ‘inplace=1’ davanti al nome del file da elaborare in maniera diretta.
La variabile _inplace_filename
serve per tener traccia del nome del
file corrente, in modo da non eseguire la funzione inplace_end()
prima
di aver elaborato il primo file.
Se si verifica un errore, l’estensione emette un messaggio di errore fatale per terminare l’elaborazione immediatamente, senza danneggiare il file originale.
Ecco alcuni semplici esempi:
$ gawk -i inplace '{ gsub(/pippo/, "pluto") }; { print }' file1 file2 file3
Per mantenere una copia di backup del file originale, si provi a fare così:
$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/pippo/, "pluto") } > { print }' file1 file2 file3
Si noti che, anche se l’estensione tenta di mantenere il proprietario e i permessi di accesso del file originario, non viene tentata la copia degli ulteriori permessi di accesso (ACL - Access Control Lists) del file originale.
Se il programma termina prima del previsto, come potrebbe succedere se riceve dal sistema un segnale non gestito, può lasciare come residuo un file temporaneo.
Successivo: Esempio di estensione Readdir, Precedente: Esempio di estensione Inplace, Su: Esempi di estensione [Contenuti][Indice]
ord()
e chr()
L’estensione ordchr
aggiunge due funzioni, di nome
ord()
e chr()
, come segue:
@load "ordchr"
Questo è il modo per caricare l’estensione.
numero = ord(stringa)
Restituisce il valore numerico del primo carattere in stringa
.
char = chr(number)
Restituisce una stringa il cui primo carattere è quello rappresentato
da number
.
Queste funzioni sono ispirate alle funzioni del linguaggio Pascal dallo stesso nome. Ecco un esempio:
@load "ordchr" … printf("Il valore numerico di 'A' è %d\n", ord("A")) printf("Il valore come stringa di 65 è %s\n", chr(65))
Successivo: Esempio di estensione Revout, Precedente: Esempio di estensione Ord, Su: Esempi di estensione [Contenuti][Indice]
L’estensione readdir
aggiunge un analizzatore di input
per esaminare directory.
L’uso è il seguente:
@load "readdir"
Quando quest’estensione è in uso, invece che saltare le
directory presenti sulla riga di comando, (o accedute tramite getline
),
queste sono lette, e ogni elemento della directory è restituito come
un record.
Il record consiste di tre campi. I primi due sono il numero di inode e il nome-file, separati fra loro da una barra. Nei sistemi in cui l’elemento di directory contiene il tipo del file, il record ha un terzo campo (pure separato da una barra), composto da una sola lettera, che indica il tipo del file. Le lettere e i tipi di file a cui corrispondono sono mostrate in Tabella 16.3.
Lettera | Tipo di file |
---|---|
b | Dispositivo a blocchi |
c | Dispositivo a caratteri |
d | Directory |
f | File normale |
l | Collegamento simbolico |
p | pipe con nome (FIFO) |
s | socket |
u | Tutto il resto (sconosciuto) |
Tabella 16.3: Tipi file restituiti dall’estensione readdir
Nei sistemi che non contengono l’informazione sul tipo del file, il terzo campo è sempre ‘u’.
NOTA: Nei sistemi GNU/Linux, ci sono fileystem che non supportano il campo
d_type
(si veda la pagina di manuale readdir(3)), e in questo caso il tipo di file è sempre ‘u’. Si può usare l’estensionefilefuncs
per chiamarestat()
e ottenere l’informazione corretta sul tipo di file.
Ecco un esempio:
@load "readdir" … BEGIN { FS = "/" } { print "nome-file è", $2 }
Successivo: Esempio di estensione Rev2way, Precedente: Esempio di estensione Readdir, Su: Esempi di estensione [Contenuti][Indice]
L’estensione revoutput
aggiunge un semplice processore
di output che inverte i caratteri di ogni riga in output. Serve a dimostrare
come è possibile scrivere un processore di output, anche se può essere
a prima vista vagamente divertente.
Ecco un esempio:
@load "revoutput" BEGIN { REVOUT = 1 print "non v'allarmate" > "/dev/stdout" }
L’output di questo programma è ‘etamralla'v non’.
Successivo: Esempio di estensione Rwarray, Precedente: Esempio di estensione Revout, Su: Esempi di estensione [Contenuti][Indice]
L’estensione revtwoway
aggiunge un semplice processore
bidirezionale che inverte i caratteri di ogni riga che riceve, per farla
poi rileggere dal programma awk
. Il motivo per cui è stata scritta
è quello di mostrare come si scrive un processore bidirezionale, anche se può
sembrare un programma vagamente divertente.
Il seguente esempio mostra come usarlo:
@load "revtwoway" BEGIN { cmd = "/specchio/magico" print "non v'allarmate" |& cmd cmd |& getline risultato print risultato close(cmd) }
L’output di questo programma anche in questo caso è: ‘etamralla'v non’.
Successivo: Esempio di estensione Readfile, Precedente: Esempio di estensione Rev2way, Su: Esempi di estensione [Contenuti][Indice]
L’estensione rwarray
aggiunge due funzioni,
di nome writea()
e reada()
, come segue:
@load "rwarray"
Questo è il modo per caricare l’estensione.
ret = writea(file, vettore)
Questa funzione ha come argomento una stringa, che è il nome del file
sul quale scaricare il vettore, e il vettore stesso è il secondo argomento.
writea()
è in grado di gestire vettori di vettori. Restituisce il
valore uno se completa il lavoro o zero se non va a buon fine.
ret = reada(file, vettore)
reada()
è la funzione inversa di writea()
;
legge il file il cui nome è fornito come primo argomento, riempiendo il
vettore il cui nome è il secondo argomento. Il vettore viene preventivamente
svuotato.
Anche in questo caso, il valore restituito è uno se tutto va bene o zero se
la funzione non va a buon fine.
Il vettore creato da reada()
è identico a quello scritto da
writea()
nel senso che i contenuti sono gli stessi. Tuttavia,
per come è strutturata la funzione, l’ordine di attraversamento del vettore
ricreato è quasi certamente differente da quello del vettore originale.
Poiché l’ordine di attraversamento di un vettore è, per default, indefinito
in awk
, questo non è (tecnicamente) un problema. Se serve che
l’attraversamento del vettore avvenga in un ordine preciso, si possono usare
le funzionalità di ordinamento di un vettore disponibili in gawk
(vedi Ordinamento di vettori).
Il file contiene dati in formato binario. Tutti i valori interi sono scritti in network byte order115. Tuttavia, i valori in virgola mobile a doppia precisione sono scritti come dati binari nativi. Quindi, vettori che contengono solo dati in formato stringa possono essere scaricati da un sistema con un certo ordine di byte e ripristinati su un sistema con un ordine di byte differente, anche se un test al riguardo non è mai stato fatto.
Ecco un esempio:
@load "rwarray" … ret = writea("scaricato.bin", vettore) … ret = reada("scaricato.bin", vettore)
Successivo: Esempio di estensione Time, Precedente: Esempio di estensione Rwarray, Su: Esempi di estensione [Contenuti][Indice]
L’estensione readfile
aggiunge una sola funzione
di nome readfile()
, e un analizzatore di input:
@load "readfile"
Questo è il modo per caricare l’estensione.
risultato = readfile("/qualche/persorso")
L’argomento è il nome del file da leggere. Il valore restituito è una
stringa contenente l’intero contenuto del file richiesto. In caso di errore,
la funzione restituisce la stringa vuota e imposta ERRNO
.
BEGIN { PROCINFO["readfile"] = 1 }
Inoltre, l’estensione aggiunge un analizzatore di input che è attivato se
l’elemento PROCINFO["readfile"]
esiste.
Quando l’analizzatore è attivato, ogni file in input è restituito interamente
come $0
.
La variabile RT
è impostata alla stringa nulla.
Ecco un esempio:
@load "readfile" … contents = readfile("/percorso/del/file"); if (contents == "" && ERRNO != "") { print("problema in lettura file", ERRNO) > "/dev/stderr" ... }
Successivo: Esempio di estensione API Test, Precedente: Esempio di estensione Readfile, Su: Esempi di estensione [Contenuti][Indice]
L’estensione time
aggiunge due funzioni, di nome
gettimeofday()
e sleep()
, come segue:
@load "time"
Questo è il modo per caricare l’estensione.
ora_corrente = gettimeofday()
Restituisce il numero di secondi trascorsi dalle ore 00:00 del giorno
01/01/1970 UTC come valore a virgola mobile.
Se questa informazione non è disponibile nella piattaforma in uso,
restituisce -1 e imposta ERRNO
. Il valore fornito dovrebbe
avere la precisione di una frazione di
secondo, ma la precisione effettiva può variare a seconda della
piattaforma.
Se la chiamata di sistema standard C gettimeofday()
è disponibile
nella piattaforma in uso, questo è il valore restituito. In caso contrario,
se si sta lavorando con MS-Windows, la chiamata di sistema è fatta a
GetSystemTimeAsFileTime()
.
risultato = sleep(secondi)
Il programma gawk
resta inattivo (dorme) per i secondi
specificati. Se secondi ha un valore negativo,
o la chiamata di sistema non riesce, restituisce -1 e imposta ERRNO
.
In caso contrario, restituisce zero dopo aver lasciato trascorrere
la quantità di tempo indicata.
Si noti che secondi può essere un numero a virgola mobile (non solo un
numero intero).
Dettagli di implementazione: a seconda della disponibilità nel sistema in uso,
questa funzione tenta di usare nanosleep()
o select()
per
ottenere il tempo di attesa richiesto.
Precedente: Esempio di estensione Time, Su: Esempi di estensione [Contenuti][Indice]
L’estensione testext
controlla la funzionalità di
parti dell’API delle estensioni che non sono utilizzate negli altri esempi.
Il file extension/testext.c
contiene sia il codice C per l’estensione che il codice awk
(tra i commenti del codice C) per eseguire i test. L’ambiente di test
estrae il codice sorgente awk
ed esegue i test. Si veda il file
sorgente per maggiori informazioni.
Successivo: Sommario delle estensioni, Precedente: Esempi di estensione, Su: Estensioni dinamiche [Contenuti][Indice]
gawkextlib
Il progetto gawkextlib
fornisce varie estensioni per gawk
, compresa una per
l’elaborazione dei file XML. Questa è un’evoluzione del progetto noto come
xgawk
(XML gawk
).
Al momento della stesura di questo testo, ci sono otto estensioni:
errno
gawk
)
Si può scaricare il codice del progetto gawkextlib
usando il codice sorgente mantenuto tramite
Git.
Il comando per farlo è il seguente:
git clone git://git.code.sf.net/p/gawkextlib/code gawkextlib-code
Per poter compilare e usare l’estensione XML, è necessario installare la libreria di analisi XML Expat.
Inoltre, è necessario installare gli strumenti GNU Autotools
(Autoconf,
Automake,
Libtool
e
GNU gettext
).
La semplice procedura per compilare e testare gawkextlib
è la seguente.
Dapprima, occorre compilare e installare gawk
:
cd .../percorso/del/sorgente/gawk ./configure --prefix=/tmp/newgawk Installa in /tmp/newgawk per ora make && make check Compila e controlla che tutto sia a posto make install Installa gawk
Poi, dal sito http://sourceforge.net/projects/gawkextlib/files si deve
scaricare gawkextlib
e le estensioni che si vogliono installare.
Il file README del sito spiega come compilare il codice. Se si è
installato gawk
in una posizione non-standard, occorre
specificare ./configure --with-gawk=/percorso/del/programma/gawk
per far sì che venga trovato.
Può essere necessario usare il programma di utilità sudo
per installare sia gawk
che gawkextlib
, a seconda di come
funziona il sistema su cui si lavora.
Chi scrive un’estensione e desidera condividerla con altri utenti
gawk
, può prendere in considerazione l’idea di farlo attraverso
il progetto gawkextlib
.
Si veda il sito web del progetto per maggiori informazioni.
Successivo: Esercizi sulle estensioni, Precedente: gawkextlib, Su: Estensioni dinamiche [Contenuti][Indice]
gawk
nel linguaggio C o C++ usando l’interfaccia di programmazione applicativa
(API) definita dagli sviluppatori di
gawk
.
plugin_is_GPL_compatibile
.
gawk
e un’estensione è bidirezionale.
gawk
passa all’estensione una struttura (struct
) che contiene
vari campi di dati e puntatori a funzione. L’estensione può poi chiamare
funzioni all’interno di gawk
tramite dei puntatori a funzioni
per svolgere alcuni compiti.
awk
con gawk
.
L’implementazione ha la forma di un puntatore del linguaggio C,
cui è associato un dato livello di versione.
Per convenzione, le funzioni di implementazione hanno nome
do_XXXX()
per una funzione a livello di awk
di nome
XXXX()
.
ERRNO
o annullarlo
awk
.
gawk
e memoria allocata da
un’estensione.
gawk
a un’estensione dev’essere
considerata come in sola lettura dall’estensione.
gawk
deve
essere ottenuta dalle funzioni di allocazione della memoria previste
dall’API. gawk
è responsabile per la gestione di quella memoria e
la libera quando è il momento per farlo.
gawk
in
esecuzione, in modo che un’estensione possa verificare la propria compatibilità
con la versione di gawk
da cui è stata caricata.
gawk
comprende un numero di piccoli ma utili
esempi di estensione. Il progetto gawkextlib
include diverse altre
estensioni, di maggiori dimensioni.
Per chi desideri scrivere un’estensione e metterla a disposizione della
comunità degli utenti di gawk
, il progetto gawkextlib
è il posto adatto per farlo.
Precedente: Sommario delle estensioni, Su: Estensioni dinamiche [Contenuti][Indice]
chown()
, chmod()
e umask()
nelle estensioni che
operano con i file viste
in
Operazioni interne file.
isatty()
per sapere se il file in input è un terminale.
(Suggerimento: questa funzione
normalmente usa parecchie risorse quando è richiamata; si tenti di chiamarla
una volta sola.)
Il contenuto del prompt dovrebbe provenire da una variabile che sia possibile
impostare a livello di codice awk
.
Si può inviare il prompt allo standard error. Tuttavia,
per ottenere risultati migliori, è meglio aprire un nuovo descrittore di file
(o puntatore a un file)
sul file /dev/tty e stampare il prompt su quel file, nel caso
in cui lo standard error sia stato ridiretto.
Perché lo standard error è una scelta migliore dello standard output per scrivere il prompt? Quale meccanismo di lettura andrebbe sostituito, quello che legge un record o quello che legge dei semplici byte?
gawk
, in modo
che i nomi di funzione presenti in estensioni differenti non siano in conflitto
tra loro?
Chi riesce a trovare uno schema di buona qualità è pregato di contattare il
manutentore di gawk
, per metterlo al corrente.
Successivo: Installazione, Precedente: Estensioni dinamiche, Su: Top [Contenuti][Indice]
awk
Questo Documento descrive l’implementazione GNU di awk
conforme alle specifiche POSIX. Molti degli utenti di lunga data di
awk
hanno imparato a programmare in awk
usando
l’implementazione originale di awk
presente nella versione 7 di
Unix. (Questa versione è servita da base per la versione Berkeley Unix di
awk
, attraverso la versione 4.3BSD-Reno. Successive versioni di
Berkeley Unix e, per un certo periodo, alcuni sistemi derivati da
4.4BSD-Lite, hanno usato varie versioni di gawk
come loro
awk
.) Questo capitolo descrive in breve l’evoluzione
del linguaggio awk
, facendo riferimento ad altre parti del
Documento dove si possono trovare ulteriori informazioni.
• V7/SVR3.1: | Le principali differenze tra V7 e System V Release 3.1. | |
• SVR4: | Differenze minori tra System V Release 3.1 e 4. | |
• POSIX: | Nuove funzionalità per lo standard POSIX. | |
• BTL: | Nuove funzionalità dalla versione
di awk di Brian Kernighan.
| |
• POSIX/GNU: | Le estensioni in gawk non
previste in awk POSIX.
| |
• Storia delle funzionalità: | La storia delle funzionalità di
gawk .
| |
• Estensioni comuni: | Sommario Estensioni comuni. | |
• Intervalli e localizzazione: | Come le localizzazioni influiscono sugli intervalli delle espressioni regolari. | |
• Contributori: | I maggiori contributori a gawk .
| |
• Sommario della storia: | Sommario della storia. |
Successivo: SVR4, Su: Storia del linguaggio [Contenuti][Indice]
Il liguaggio awk
si è evoluto considerevolmente tra Unix versione
7 (1978) e la nuova implementazione disponibile a partire da Unix System V
Release 3.1 (1987). Questa sezione riassume le differenze e indica
dove è possibile trovare ulteriori dettagli:
return
(vedi Funzioni definite dall'utente)
delete
(vedi Cancellazione)
do
-while
(vedi Istruzione do)
atan2()
, cos()
, sin()
, rand()
e
srand()
(vedi Funzioni numeriche)
gsub()
, sub()
e match()
(vedi Funzioni per stringhe)
close()
e system()
(vedi Funzioni di I/O)
ARGC
, ARGV
, FNR
, RLENGTH
,
RSTART
e SUBSEP
(vedi Variabili predefinite)
$0
(vedi Cambiare i campi)
for
(vedi Visitare elementi)
awk
(vedi Precedenza)
FS
(vedi Separatori di campo) e come
terzo argomento per la funzione split()
(vedi Funzioni per stringhe), invece di usare solo il primo carattere
di FS
getline
(vedi Getline)
BEGIN
ed END
(vedi BEGIN/END)
Successivo: POSIX, Precedente: V7/SVR3.1, Su: Storia del linguaggio [Contenuti][Indice]
La versione per Unix System V Release 4 (1989) di awk
ha aggiunto
queste funzionalità (alcune delle quali introdotte da gawk
):
ENVIRON
(vedi Variabili predefinite)
srand()
(vedi Funzioni numeriche)
toupper()
e tolower()
per la conversione maiuscolo/minuscolo
(vedi Funzioni per stringhe)
printf
(vedi Lettere di controllo)
"%*.*d"
) nella lista degli argomenti passati a
printf
e sprintf()
(vedi Lettere di controllo)
/pippo/
, come espressioni,
che equivalgono a usare l’operatore di ricerca di una
corrispondenza, p.es. ‘$0 ~ /pippo/’
(vedi Usare le costanti regexp)
Successivo: BTL, Precedente: SVR4, Su: Storia del linguaggio [Contenuti][Indice]
awk
Lo standard POSIX Command Language and Utilities per awk
(1992)
ha introdotto le seguenti modifiche al linguaggio:
CONVFMT
per controllare la conversione di numeri
in stringhe (vedi Conversione)
Nel 2012, un certo numero di estensioni che erano già comunemente disponibili da parecchi anni sono state finalmente aggiunte allo standard POSIX. Ecco l’elenco:
fflush()
per forzare la scrittura dei buffer
in output
(vedi Funzioni di I/O)
nextfile
(vedi Istruzione nextfile)
Vedi Estensioni comuni per una lista delle estensioni comuni non previste nello standard POSIX.
Lo standard POSIX 2008 è reperibile online a: http://www.opengroup.org/onlinepubs/9699919799/.
Successivo: POSIX/GNU, Precedente: POSIX, Su: Storia del linguaggio [Contenuti][Indice]
awk
di Brian KernighanBrian Kernighan ha reso disponibile la sua versione nel suo sito. (vedi Altre versioni).
Questa
sezione descrive estensioni comuni disponibili per la
prima volta nella sua versione di awk
:
func
come abbreviazione di function
(vedi Sintassi delle definizioni)
fflush()
per forzare la scrittura dei buffer
in output
(vedi Funzioni di I/O)
Vedi Estensioni comuni per una lista completa delle estensioni
disponibile nel suo awk
.
Successivo: Storia delle funzionalità, Precedente: BTL, Su: Storia del linguaggio [Contenuti][Indice]
gawk
non in POSIX awk
L’implementazione GNU di gawk
aggiunge molte funzionalità.
Queste possono essere disabilitate completamente sia con l’opzione
--traditional che con l’opzione
--posix
(vedi Opzioni).
Alcune funzionalità sono state introdotte e successivamente tolte
con il passare del tempo.
Questa
sezione
sintetizza le ulteriori funzionalità rispetto a POSIX awk
che sono
presenti nella versione corrente di gawk
.
ARGIND
,
BINMODE
,
ERRNO
,
FIELDWIDTHS
,
FPAT
,
IGNORECASE
,
LINT
,
PROCINFO
,
RT
e
TEXTDOMAIN
(vedi Variabili predefinite)
FS
e il terzo
argomento di split()
siano la stringa nulla
(vedi Campi di un solo carattere)
RS
sia una regexp
(vedi Record)
awk
(vedi Numeri non-decimali)
print
e printf
non provocano
necessariamente la fine del programma
(vedi Continuazione dopo errori)
BEGINFILE
ed ENDFILE
(vedi BEGINFILE/ENDFILE)
switch
(vedi Istruzione switch)
awk
:
close()
che consente di chiudere
un solo lato dell’I/O di una pipe bidirezionale aperta verso un
coprocesso (vedi I/O bidirezionale)
gsub()
e sub()
se è stata specificata l’opzione --posix
length()
accetta come argomento il nome di un vettore
e restituisce il numero di elementi nel vettore
(vedi Funzioni per stringhe)
match()
per contenere eventuali sottoespressioni individuate all’interno di una
regexp
(vedi Funzioni per stringhe)
printf
per facilitare
le traduzioni di messaggi
(vedi Ordinamento di printf)
split()
,
per designare un vettore che contenga il testo dei separatori di campo
(vedi Funzioni per stringhe)
gawk
:
gensub()
, patsplit()
e strtonum()
per una gestione di testi più potente
(vedi Funzioni per stringhe)
asort()
e asorti()
per l’ordinamento di vettori
(vedi Ordinamento di vettori)
mktime()
, systime()
e strftime()
per lavorare con date e ore
(vedi Funzioni di tempo)
and()
,
compl()
,
lshift()
,
or()
,
rshift()
e
xor()
per la manipolazione a livello di bit
(vedi Funzioni a livello di bit)
isarray()
per controllare se una variabile è un vettore
oppure no
(vedi Funzioni per i tipi)
bindtextdomain()
, dcgettext()
e dcngettext()
per l’internazionalizzazione
(vedi I18N per programmatore)
intdiv()
per effettuare divisioni a numeri interi e
ottenere il resto della divisione
(vedi Funzioni numeriche)
AWKPATH
per specificare un percorso di ricerca
per l’opzione -f della riga di comando
(vedi Opzioni)
AWKLIBPATH
per specificare un percorso di ricerca
per l’opzione -l della riga di comando
(vedi Opzioni)
gawk
versione 4.0:
gawk
versione 4.1:
gawk
versione 4.2:
Successivo: Estensioni comuni, Precedente: POSIX/GNU, Su: Storia del linguaggio [Contenuti][Indice]
gawk
Questa
sezione descrive le funzionalità in gawk
in aggiunta a quelle di POSIX awk
,
nell’ordine in cui sono state rese disponibili in gawk
.
La versione 2.10 di gawk
ha introdotto le seguenti funzionalità:
AWKPATH
per specificare un percorso di ricerca
per l’opzione -f della riga di comando
(vedi Opzioni)
IGNORECASE
e i suoi effetti
(vedi Maiuscolo-Minuscolo).
La versione 2.13 di gawk
ha ha introdotto le seguenti funzionalità:
FIELDWIDTHS
e i suoi effetti
(vedi Dimensione costante).
systime()
e strftime()
per ottenere
e stampare data e ora
(vedi Funzioni di tempo).
La versione 2.14 di gawk
ha introdotto le seguenti funzionalità:
next file
per passare immediatamente al successivo
file-dati (vedi Istruzione nextfile).
La versione 2.15 di gawk
ha introdotto le seguenti funzionalità:
ARGIND
, che permette di controllare la posizione di FILENAME
nel vettore ARGV
.
ERRNO
, che contiene il messaggio di errore del sistema quando
getline
restituisce -1 o close()
non termina con successo.
La versione 3.0 di gawk
ha introdotto le seguenti funzionalità:
IGNORECASE
modificato, diventa applicabile al confronto tra stringhe,
come pure alle operazioni su regexp
(vedi Maiuscolo-Minuscolo).
RT
, che contiene il testo in input che è stato individuato da RS
(vedi Record).
gensub()
per migliorare la manipolazione di testi
(vedi Funzioni per stringhe).
strftime()
prevede un formato di data e ora di default,
in modo da poter essere chiamata senza alcun argomento.
(vedi Funzioni di tempo).
FS
e il terzo argomento della funzione
split()
siano delle stringhe nulle
(vedi Campi di un solo carattere).
RS
sia una regexp
(vedi Record).
next file
è diventata nextfile
(vedi Istruzione nextfile).
fflush()
di
BWK awk
(BWK allora lavorava ai Bell Laboratories;
vedi Funzioni di I/O).
awk
per Unix Version 7
(vedi V7/SVR3.1).
awk
. (Brian lavorava
ancora ai Bell Laboratories all’epoca.) Quest’opzione è stata in seguito
rimossa, sia dal suo awk
che da gawk
.
La versione 3.1 di gawk
ha introdotto le seguenti funzionalità:
BINMODE
, per sistemi non aderenti allo standard POSIX,
che consente I/O binario per file in input e/o output
(vedi Uso su PC).
LINT
, che controlla dinamicamente gli avvertimenti emessi da lint.
PROCINFO
, un vettore che fornisce informazioni correlate con il
processo in esecuzione.
TEXTDOMAIN
, per impostare il dominio testuale in cui internazionalizzare
un’applicazione (vedi Internazionalizzazione).
awk
.
(vedi Numeri non-decimali).
close()
per permettere di
chiudere uno dei lati di una pipe bidirezionale aperta con un coprocesso
(vedi I/O bidirezionale).
match()
per
avere a disposizione le diverse sottoespressioni individuate all’interno
di una regexp
(vedi Funzioni per stringhe).
printf
per
facilitare la traduzione di messaggi
(vedi Ordinamento di printf).
asort()
e asorti()
per l’ordinamento di vettori
(vedi Ordinamento di vettori).
bindtextdomain()
, dcgettext()
e dcngettext()
per l’internationalizzazione
(vedi I18N per programmatore).
extension()
e la possibilità di aggiungere
nuove funzioni predefinite dinamicamente
(vedi Estensioni dinamiche).
mktime()
per generare date e ore
(vedi Funzioni di tempo).
and()
, or()
, xor()
, compl()
,
lshift()
, rshift()
e strtonum()
(vedi Funzioni a livello di bit).
pgawk
, la
versione profilatrice di gawk
, per produrre profili di esecuzione
di programmi awk
(vedi Profilare).
gawk
di usare il carattere di separazione decimale proprio della localizzazione
nell’elaborazione dei dati in input
(vedi Conversione).
gettext
per i messaggi emessi da gawk
(vedi Gawk internazionalizzato).
sub()
e gsub()
(vedi Dettagli ostici).
length()
è stata estesa per accettare un vettore come
argomento, e restituire in tal caso il numero di elementi nel vettore
(vedi Funzioni per stringhe).
strftime()
accetta un terzo argomento per
dare la possibilità di stampare data e ora nel formato UTC
(vedi Funzioni di tempo).
La versione 4.0 di gawk
ha introdotto le seguenti funzionalità:
FPAT
, che permette di specificare una regexp che individua
i campi, invece che individuare il separatore tra i campi
(vedi Separazione in base al contenuto).
PROCINFO["sorted_in"]
, il ciclo
‘for(indice in pippo)’ ordina
gli indici, prima di iniziare il ciclo. Il valore di questo elemento
permette di controllare l’ordinamento degli indici prima di iniziare il
ciclo che li visita tutti
(vedi Controllare visita).
PROCINFO["strftime"]
, che contiene la stringa di formato
di default per strftime()
(vedi Funzioni di tempo).
break
e continue
non sono più consentiti fuori da un ciclo,
anche se si è specificata l’opzione --traditional
(vedi Istruzione break e anche la
Istruzione continue).
fflush()
, nextfile
e ‘delete array’
sono consentite anche se è stata specificata l’opzione --posix o
--traditional, poiché questi costrutti sono ora inclusi
nello standard POSIX.
asort()
e asorti()
permette di specificare il tipo di ordinamento desiderato
(vedi Funzioni per stringhe).
fflush()
è stato modificato per corrispondere
a quello di BWK awk
e per lo standard POSIX; ora sia ‘fflush()’ che ‘fflush("")’
forzano la scrittura di tutte le ridirezioni in output aperte
(vedi Funzioni di I/O).
isarray()
determina se un elemento è un vettore oppure no
per rendere possibile la visita di vettori di vettori
(vedi Funzioni per i tipi).
patsplit()
che
fornisce le stesse funzionalità di FPAT
, per suddividere delle stringhe
(vedi Funzioni per stringhe).
split()
,
che indica un vettore destinato a contenere i valori dei separatori
(vedi Funzioni per stringhe).
BEGINFILE
ed ENDFILE
(vedi BEGINFILE/ENDFILE).
switch
/ case
sono disponibili per default
(vedi Istruzione switch).
gawk
tratti l’input come composto da una
stringa di caratteri multibyte.
gawk
è stato riscritto, aggiungendo la
versione per il debug dgawk
,
con un possibile miglioramento nei tempi di esecuzione
(vedi Debugger).
strcoll()
/ wcscoll()
(vedi Confronto POSIX di stringhe).
La versione 4.1 di gawk
ha introdotto le seguenti funzionalità:
SYMTAB
, FUNCTAB
e PROCINFO["identifiers"]
(vedi Variabili auto-assegnate).
gawk
, pgawk
e dgawk
,
sono diventati uno solo, con il solo nome gawk
. Di conseguenza
le opzioni sulla riga di comando sono state modificate.
awk
.
and()
, or()
e xor()
sono state modificate
per ammettere un numero qualsiasi di argomenti, con un minimo di due
(vedi Funzioni a livello di bit).
getline
ridiretta è stata resa possibile all’interno di
BEGINFILE
ed ENDFILE
(vedi BEGINFILE/ENDFILE).
where
è stato aggiunto al debugger
(vedi Stack di esecuzione).
La versione 4.2 ha introdotto le seguenti funzionalità:
ENVIRON
) sono riflesse in quelle
rese disponibili a gawk
e in quelle di programmi che siano da esso richiamati.
Vedi Variabili auto-assegnate.
awk
.
Vedi Opzioni.
igawk
e le relative pagine di manuale non sono
più installati come parte dell’installazione di gawk
.
Vedi Programma igawk.
intdiv()
.
Vedi Funzioni numeriche.
print
e printf
non terminano il programma dopo alcuni
errori di output.
Vedi Continuazione dopo errori.
gawk
se si specifica l’opzione --posix. Dal 2013
il comportamento originario è stato ripristinato, e ora
il default per separare i campi con l’opzione --posix ammette
anche il ritorno a capo come separatore di campi.
Successivo: Intervalli e localizzazione, Precedente: Storia delle funzionalità, Su: Storia del linguaggio [Contenuti][Indice]
La tabella seguente dettaglia le estensioni comuni supportate
da gawk
, da Brian Kernighan awk
e da mawk
,
le tre versioni liberamente disponibili più usate di awk
(vedi Altre versioni).
Funzionalità | BWK awk | mawk | gawk | Standard attuale |
---|---|---|---|---|
Sequenza di protezione ‘\x’ | X | X | X | |
Stringa nulla come FS | X | X | X | |
File speciale /dev/stdin | X | X | X | |
File speciale /dev/stdout | X | X | X | |
File speciale /dev/stderr | X | X | X | |
delete senza indici | X | X | X | X |
Funzione fflush() | X | X | X | X |
length() di un vettore | X | X | X | |
Istruzione nextfile | X | X | X | X |
Operatori ** e **= | X | X | ||
Parola chiave func | X | X | ||
Variabile BINMODE | X | X | ||
RS come regexp | X | X | ||
Funzioni gestione data/ora | X | X |
Successivo: Contributori, Precedente: Estensioni comuni, Su: Storia del linguaggio [Contenuti][Indice]
Questa
sezione descrive la storia confusionaria degli intervalli
all’interno di espressioni regolari, le loro relazioni con la localizzazione,
e l’effetto da ciò determinato su diverse versioni di gawk
.
Gli strumenti originali Unix aventi a che fare con espressioni regolari stabilivano che intervalli di caratteri (come ‘[a-z]’) individuavano un carattere qualsiasi tra il primo carattere dell’intervallo e l’ultimo carattere dello stesso, entrambi inclusi. L’ordinamento era basato sul valore numerico di ogni carattere come era rappresentato all’interno del computer, nell’insieme di caratteri proprio di ogni macchina. Quindi, su sistemi che adottano la codifica ASCII, ‘[a-z]’ individua tutte le lettere minuscole, e solo quelle, in quanto i valori numerici che rappresentano le lettere dalla ‘a’ fino alla ‘z’ sono contigui. (In un sistema che adotta la codifica EBCDIC, l’intervallo ‘[a-z]’ comprende anche ulteriori caratteri non alfabetici.)
Quasi tutti i testi di introduzione allo Unix spiegavano che le espressioni di intervallo funzionavano in questo modo, e in particolare insegnavano che la maniera “corretta” per individuare le lettere minuscole era con ‘[a-z]’ e che ‘[A-Z]’ era il modo “corretto” per individuare le lettere maiuscole. E, in effetti, era proprio così.116
Lo standard POSIX 1992 introduceva l’idea di localizzazione (vedi Localizzazioni). Poiché molte localizzazioni comprendono altre lettere, oltre alle 26 lettere dell’alfabeto inglese, lo standard POSIX introduceva le classi di carattere (vedi Espressioni tra parentesi quadre) per permettere l’individuazione di differenti insiemi di caratteri, in aggiunta a quelli tradizionali presenti nell’insieme di caratteri ASCII.
Tuttavia, lo standard ha modificato l’interpretazione delle
espressioni di intervallo.
Nelle localizzazioni "C"
e "POSIX"
,
un’espressione di intervallo come
‘[a-dx-z]’ è ancora equivalente a ‘[abcdxyz]’, secondo l’ordine
della codifica ASCII.
Ma in tutte le altre localizzazioni l’ordinamento è basato su quel che
si chiama ordine di collazione.
Cosa vuol dire? In molte localizzazioni, le lettere ‘A’ e ‘a’ vengono entrambe prima di ‘B’. In altre parole, queste localizzazioni ordinano i caratteri nel modo in cui sono ordinati in un dizionario, e ‘[a-dx-z]’ non è detto che equivalga a ‘[abcdxyz]’; invece, potrebbe essere equivalente a ‘[ABCXYabcdxyz]’, per fare un esempio.
Su questo punto è opportuno insistere: molta documentazione afferma che si dovrebbe usare ‘[a-z]’ per identificare un carattere minuscolo. Ma su sistemi con localizzazioni non-ASCII, un tale intervallo potrebbe includere tutti i caratteri maiuscoli tranne ‘A’ o ‘Z’! Questo ha continuato a essere una fonte di equivoci perfino nel ventunesimo secolo.
Per dare un’idea del tipo di problemi, l’esempio seguente usa la funzione
sub()
, che effettua una sostituzione di testo all’interno di una
stringa (vedi Funzioni per stringhe). Qui, l’idea è quella di rimuovere
i caratteri maiuscoli a fine stringa:
$ echo qualcosa1234abc | gawk-3.1.8 '{ sub("[A-Z]*$", ""); print }' -| qualcosa1234a
Questo non è l’output che ci si aspettava, perché, il ‘bc’ alla fine di ‘qualcosa1234abc’ non dovrebbe essere individuato da ‘[A-Z]*’. Un tale risultato dipende dalle impostazioni di localizzazione (e quindi potrebbe non succedere sul sistema che si sta usando).
Considerazioni simili valgono per altri intervalli. Per esempio, ‘["-/]’
è perfettamente valido in ASCII, ma non è valido in molte localizzazioni
Unicode, p.es. in en_US.UTF-8
.
Il codice delle prime versioni di gawk
per individuare le
regexp non teneva conto della localizzazione, e quindi gli
intervalli potevano essere interpretati in maniera tradizionale.
Quando gawk
ha iniziato a usare metodi di ricerca di regexp
che tengono conto della localizzazione, sono iniziati i problemi;
a maggior ragione in quanto sia GNU/Linux che i venditori di versioni
commerciali di Unix
avevano iniziato a implementare localizzazioni non-ASCII,
adottandole per default. La domanda che forse si udiva più spesso
era del tipo: “Perché ‘[A-Z]’ individua lettere minuscole?!?”
Questa situazione è in essere da circa 10 anni, se non di più, e
il manutentore di gawk
si è stufato di continuare a spiegare che
gawk
stava semplicemente implementando quelli che sono gli
standard, e che il problema stava nella localizzazione dell’utente. Nella
fase di sviluppo della versione 4.0, gawk
è stato modificato
in modo da trattare sempre gli
intervalli "come si faceva prima di POSIX", a meno che non si specifichi
l’opzione --posix (vedi Opzioni).117
Fortunatamente, un po’ prima del rilascio definitivo della versione 4.0 di
gawk
, il manutentore ha appreso che lo standard 2008 aveva
modificato la definizione di intervallo, e che, al di fuori delle
localizzazioni "C"
e "POSIX"
, il significato di espressione
di intervallo era ora
indefinito.118
Adottando questo simpatico termine tecnico, lo standard permette agli
implementatori di implementare gli intervalli nella maniera che preferiscono.
Il manutentore di gawk
ha deciso di implementare la regola pre-POSIX
sia per l’individuazione di default delle regexp sia quando si
specificano le opzioni --traditional o --posix.
In ogni caso gawk
aderisce allo standard POSIX.
Successivo: Sommario della storia, Precedente: Intervalli e localizzazione, Su: Storia del linguaggio [Contenuti][Indice]
gawk
Riconoscere sempre il merito, se un merito va riconosciuto.
Questa sezione elenca le persone che hanno maggiormente contribuito
allo sviluppo di gawk
e/o alla stesura di questo Documento,
in ordine approssimativamente cronologico:
awk
per Unix,
da cui gawk
trae la maggioranza delle sue funzionalità.
gawk
.
gawk
,
rendendolo compatibile col “nuovo” awk
e
migliorandone parecchio la velocità di esecuzione.
gawk
nei sistemy Cray.
(L’ambiente Cray non è più supportato.)
gawk
di funzionare su sistemi diversi da quelli a 32 bit.
gawk
per sistemi Windows32.
(Questa versione non è più supportata.)
extension()
per aggiungere
dinamicamente nuove funzioni.
(Questa funzionalità è divenuta obsoleta a partire da gawk
4.1.)
gawk
.
gawk
di usare GNU Automake e GNU gettext
.
asort()
e anche
il codice per gestire il terzo argomento opzionale della funzione
match()
.
gawk
per OS/2.
switch
.
gawk
per parecchi anni.
gawk
in un interprete di codice a livello di byte, compreso il debugger
gawk
,
in vista della versione 4.1
gawk
4.1 è stata principalmente guidata da
Arnold Robbins e Andrew Schorr, con notevoli contributi dal
resto del team di sviluppo.
gawk
dal 1988, dapprima
aiutando David Trueman e in seguito, dal 1994 circa, come
manutentore principale.
Precedente: Contributori, Su: Storia del linguaggio [Contenuti][Indice]
awk
si è evoluto col passare degli anni. La prima
versione risale a Unix V7, circa 1978. Nel 1987, per la versione Unix
System V Release 3.1, sono state fatte al linguaggio delle modifiche
importanti, inclusa la possibilità di avere funzioni definite dall’utente.
Ulteriori modifiche sono state fatte per la versione System V Release 4, nel
1989.
Dopo di allora, sono state apportate ulteriori modifiche minori,
per implementare lo standard POSIX.
awk
di Brian Kernighan prevede un piccolo numero di estensioni
implementate di comune accordo con altre versioni di awk
.
gawk
prevede un elevato numero di estensioni rispetto
a POSIX awk
.
Queste estensioni possono essere disabilitate specificando l’opzione
--traditional o --posix.
gawk
è stata causa di malintesi nel corso degli anni. Oggi
gawk
implementa l’Interpretazione Razionale degli Intervalli
(Rational Range Interpretation), dove
intervalli nella forma ‘[a-z]’ individuano solo i caratteri
numericamente compresi tra
‘a’ e ‘z’ nella rappresentazione nativa dei caratteri in quella
particolare macchina. Normalmente quella in uso è quella ASCII,
ma può essere EBCDIC sui sistemi IBM S/390.
gawk
nel corso
degli anni. Spero che l’elenco fornito in questo capitolo sia
esauriente e attribuisca il giusto riconoscimento quando questo è dovuto.
Successivo: Note, Precedente: Storia del linguaggio, Su: Top [Contenuti][Indice]
gawk
Quest’appendice contiene istruzioni per installare gawk
sulle
varie piattaforme supportate dagli sviluppatori. Lo sviluppatore
principale supporta GNU/Linux (e Unix), mentre le altre piattaforme sono
sono curate da altri sviluppatori.
Vedi Bug
per gli indirizzi di posta elettronica di chi effettua la manutenzione
della versione specifica di una particolare piattaforma.
• Distribuzione di Gawk: | Contenuto della distribuzione di gawk .
| |
• Installazione Unix: | Installare gawk su varie versioni
di Unix.
| |
• Installazione non-Unix: | Installazioni su altri Sistemi Operativi. | |
• Bug: | Notificare problemi e bug. | |
• Altre versioni: | Altre implementazioni di awk
liberamente disponibili.
| |
• Sommario dell'installazione: | Sommario dell’installazione. |
Successivo: Installazione Unix, Su: Installazione [Contenuti][Indice]
gawk
Questa sezione spiega come ottenere la distribuzione
di gawk
, come scompattarla, e cosa è contenuto nei vari file
e nelle sottodirectory risultanti.
• Scaricare: | Come ottenere la distribuzione. | |
• Scompattazione: | Come estrarre la distribuzione. | |
• Contenuti della distribuzione: | Cosa c’è nella distribuzione. |
Successivo: Scompattazione, Su: Distribuzione di Gawk [Contenuti][Indice]
gawk
Ci sono due modi per ottenere del software GNU:
gawk
dal sito Internet
ftp.gnu.org
, nella directory /gnu/gawk.
È possibile accedere al sito sia via ftp
anonimo che via http
.
Se si dispone del programma wget
, si può utilizzarlo digitando un
comando simile a questo:
wget http://ftp.gnu.org/gnu/gawk/gawk-4.1.4.tar.gz
L’archivio che contiene il software GNU è disponibile in vari cloni (mirror) in tutto il mondo. La lista aggiornata dei siti clone è disponibile nel sito web principale della FSF. Si tenti di usare uno dei siti-clone; dovrebbero essere meno trafficati, ed è possibile che ce ne sia uno più vicino.
Si può anche scaricare la distribuzione del sorgente di gawk
dal deposito Git ufficiale; per maggiori informazioni, si veda
Accedere ai sorgenti.
Successivo: Contenuti della distribuzione, Precedente: Scaricare, Su: Distribuzione di Gawk [Contenuti][Indice]
gawk
è distribuito sotto forma di parecchi file tar
compressi con differenti programmi di compressione: gzip
,
bzip2
e xz
. Per amor di semplicità, il resto di queste istruzioni
presuppone che si stia usando quella compressa col programma GNU Gzip
(gzip
).
Una volta che si ha a disposizione la distribuzione (p.es.,
gawk-4.1.4.tar.gz),
va usato gzip
per scompattare il file e quindi tar
per estrarne i
file. Si può usare la seguente pipe per produrre la distribuzione
gawk
:
gzip -d -c gawk-4.1.4.tar.gz | tar -xvpf -
In un sistema che abbia la versione GNU di tar
, si
può far effettuare la scompattazione direttamente a tar
:
tar -xvpzf gawk-4.1.4.tar.gz
L’estrazione dei file dall’archivio crea una directory di nome gawk-4.1.4 nella directory corrente.
Il nome-file della distribuzione è nella forma
gawk-V.R.P.tar.gz.
La V rappresenta la versione maggiore di gawk
,
la R rappresenta il rilascio corrente della versione V, e
la P rappresenta un patch level, che sta a indicare che
correzioni a errori minori sono state incluse nel rilascio.
Il patch level corrente è 4, ma quando ci si procura
una distribuzione, andrà ottenuta quella con il livello più alto di
versione, rilascio e patch.
(Si noti, comunque, che livelli di patch maggiori o uguali a 70
denotano versioni “beta”, ossia versioni non destinate a essere usate
in produzione; non si dovrebbero utilizzare tali versioni, se non si è
disposti a sperimentare.)
Se non si sta usando un sistema Unix o GNU/Linux, i modi per ottenere
e scompattare la distribuzione di gawk
sono differenti.
Si dovrebbe sentire un esperto di quel sistema.
Precedente: Scompattazione, Su: Distribuzione di Gawk [Contenuti][Indice]
gawk
La distribuzione di gawk
contiene un certo numero di file
sorgente in C, di file di documentazione, di sottodirectory, e di file
utilizzati durante il processo di configurazione
(vedi Installazione Unix),
come pure parecchie sottodirectory relative a diversi sistemi operativi
non-Unix:
Questi file contengono il codice sorgente vero e proprio di gawk
.
Intestazioni C e file sorgente per routine che gawk
usa, ma che non sono parte della sua funzionalità
fondamentale. Per esempio, analisi di argomenti, controlli
di corrispondenze di espressioni regolari, e routine per
generare numeri casuali sono tutti mantenuti qui.
Un file contenente informazioni sul comando GNU gettext
e
sulle traduzioni.
Un file con alcune informazioni su chi ha scritto gawk
.
Esiste solo per placare i pedanti della Free Software Foundation.
File descrittivi: vari README ("leggimi") per gawk
sotto Unix e per
tutte le varie altre combinazioni hardware e software.
Un file che fornisce una panoramica sul processo di configurazione e installazione.
Una lista dettagliata delle modifiche apportate al codice sorgente, ai problemi risolti e ai miglioramenti introdotti.
Una lista meno recente di modifiche al codice sorgente.
Una lista di modifiche a gawk
a partire dall’ultimo rilascio
o patch.
Una lista meno recente di modifiche a gawk
.
La GNU General Public License.
Una descrizione di comportamenti nello standard POSIX per awk
che
sono lasciati indefiniti, o ai quali gawk
non può conformarsi
pienamente, come pure una lista di specifiche che lo standard POSIX dovrebbe
contenere, ma che non sono presenti.
Puntatori alla bozza originale di un breve articolo
che spiega perché gawk
è un linguaggio adatto alla
programmazione nel campo dell’intelligenza artificiale (AI).
Una breve descrizione della struttura interna a livello di byte di
gawk
[“byte code”].
Il sorgente troff
per una scheda di riferimento a cinque colori
di awk
.
Per ottenere la versione a colori è richiesta una versione recente di
troff
, come la versione GNU di troff
(groff
).
Si veda il file README.card per istruzioni su come comportarsi se è
disponibile solo una versione più vecchia di troff
.
Il sorgente troff
di una pagina di manuale [man]
che descrive gawk
.
Questa pagina è distribuita a beneficio degli utenti Unix.
Il file sorgente Texinfo di questo Documento.
Dovrebbe venire elaborato da doc/sidebar.awk
prima di essere elaborato con texi2dvi
o texi2pdf
per produrre un documento stampato, o
con makeinfo
per produrre un file Info o HTML.
Il Makefile si occupa di questa elaborazione e produce
la versione stampabile tramite i comandi
texi2dvi
o texi2pdf
.
Il file prodotto elaborando gawktexi.in tramite sidebar.awk.
Il file Info generato per questo Documento.
Il file sorgente Texinfo per
TCP/IP Internetworking with gawk
.
Dovrebbe venire elaborato con TeX
(tramite texi2dvi
o texi2pdf
)
per produrre un documento stampato o
con makeinfo
per produrre un file Info o HTML.
Il file Info generato per
TCP/IP Internetworking with gawk
.
Il sorgente troff
per una pagina di manuale relativa al
programma igawk
descritto
in
Programma igawk.
(Poiché gawk
prevede ora internamente l’uso della direttiva
@include
,
né igawk
né igawk.1 sono effettivamente installati.)
Il file in input usato durante la procedura di configurazione per generare l’effettivo Makefile da usare per creare la documentazione.
File usati dal software GNU Automake per generare
il file Makefile.in usato da Autoconf e dallo script
configure
.
Questi file e sottodirectory sono usati per configurare e compilare
gawk
per vari sistemi Unix. L’uso di molti tra questi file è spiegato
in
Installazione Unix. I rimanenti hanno una funzione di supporto
per l’infrastruttura.
La directory po contiene la traduzione in varie lingue
dei messaggi emessi da gawk
.
La directory awklib contiene una copia di extract.awk
(vedi Programma extract),
che può essere usato per estrarre i programmi di esempio dal file sorgente
Texinfo di questo Documento. Contiene anche un file
Makefile.in, che
configure
usa per generare un Makefile.
Makefile.am è usato da GNU Automake per creare Makefile.in.
Le funzioni di libreria descritte
in
Funzioni di libreria,
sono incluse come file pronti per l’uso nella distribuzione gawk
.
Essi sono installati come parte della procedura di installazione.
I rimanenti programmi contenuti in questo Documento sono disponibili
nelle appropriate sottodirectory di awklib/eg.
Il codice sorgente, le pagine di manuale, e i file di infrastruttura per
gli esempi di estensione incluse con gawk
.
Vedi Estensioni dinamiche, per ulteriori dettagli.
Ulteriori file, non-essenziali. Al momento, questa directory contiene
alcuni file da eseguire al momento di iniziare una sessione,
da installare nella directory /etc/profile.d
per essere di aiuto nella gestione delle variabili di ambiente
AWKPATH
e AWKLIBPATH
.
Vedi File da usare a inizio sessione, per ulteriori informazioni.
File necessari per compilare gawk
su sistemi conformi allo
standard POSIX.
File necessari per compilare gawk
sotto MS-Windows
(vedi Installazione su PC per i dettagli).
File necessari per compilare gawk
sotto Vax/VMS e OpenVMS
(vedi Installazione su VMS per i dettagli).
Una serie di test per
gawk
. Si può usare ‘make check’ dalla directory principale
di gawk
per provare se la serie di test funziona con la
versione in uso di gawk
.
Se gawk
supera senza errori ‘make check’, si può essere
sicuri che sia stato installato e configurato correttamente su un dato
sistema.
Successivo: Installazione non-Unix, Precedente: Distribuzione di Gawk, Su: Installazione [Contenuti][Indice]
gawk
su sistemi di tipo UnixNormalmente, si può compilare e installare gawk
immettendo
solo un paio di comandi. Comunque, se si ci si trova in un sistema
insolito, può essere necessario
dover configurare gawk
per quel dato sistema.
• Installazione veloce: | Compilare gawk sotto Unix.
| |
• File da usare a inizio sessione: | Funzioni di personalizzazione della shell. | |
• Ulteriori opzioni di configurazione: | Altre opzioni utilizzabili in fase di compilazione. | |
• Filosofia della configurazione: | Come si suppone che tutto funzioni. |
Successivo: File da usare a inizio sessione, Su: Installazione Unix [Contenuti][Indice]
gawk
per sistemi di tipo UnixQuesti normali passi di installazione dovrebbero essere sufficienti in tutti i moderni sistemi in commercio derivati da Unix, ossia GNU/Linux, sistemi basati su BSD, e l’ambiente Cygwin sotto MS-Windows.
Dopo aver estratto la distribuzione di gawk
, posizionarsi con
cd
nella directory
gawk-4.1.4. Come per la maggior parte dei
programmi GNU, occorre configurare gawk
per il sistema in uso,
eseguendo il programma configure
. Questo programma è
uno script della shell Bourne, che è stato generato automaticamente
usando il comando GNU Autoconf.
(Il software Autoconf è
descritto in dettaglio in
Autoconf—Generating Automatic Configuration Scripts,
che può essere trovato in rete sul sito
della Free Software Foundation.)
Per configurare gawk
basta eseguire configure
:
sh ./configure
Questo produce i file Makefile e config.h adatti al sistema
in uso.
Il file config.h descrive varie situazioni relative al sistema in uso.
È possibile modificare il Makefile per
cambiare la variabile CFLAGS
, che controlla
le opzioni di riga di comando da passare al compilatore C (come i livelli
di ottimizzazione o la richiesta di generare informazioni per il debug).
In alternativa, si possono specificare dei valori a piacere per
molte delle variabili di make
sulla riga di comando,
come CC
e CFLAGS
, quando
si chiama il programma
configure
:
CC=cc CFLAGS=-g sh ./configure
Si veda il file INSTALL nella distribuzione di gawk
per
tutti i dettagli.
Dopo aver eseguito configure
ed eventualmente modificato
Makefile,
va dato il comando:
make
Poco dopo, si dovrebbe avere a disposizione una versione eseguibile
di gawk
.
Questo è tutto!
Per verificare se gawk
funziona correttamente,
va dato il comando ‘make check’. Tutti i test dovrebbero terminare con
successo.
Se questi passi non producono il risultato desiderato, o se qualche
test fallisce, controllare i file nella directory README_d
per determinare se quello che è capitato è un problema noto.
Se il problema capitato non è descritto lì,
inviare una segnalazione di bug (vedi Bug).
Naturalmente, dopo aver compilato gawk
, verosimilmente
andrà installato. Per fare ciò, occorre eseguire il comando
‘make install’, disponendo delle autorizzazioni necessarie.
Come acquisirle varia da sistema a sistema, ma su molti sistemi si può
usare il comando sudo
per ottenerle. Il comando da immettere
diventa in questo caso ‘sudo make install’. È probabile che sia
necessario fornire una password, ed essere stati messi nella lista degli
utenti che possono utilizzare il comando sudo
.
Successivo: Ulteriori opzioni di configurazione, Precedente: Installazione veloce, Su: Installazione Unix [Contenuti][Indice]
La distribuzione contiene i file da usare a inizio sessione
gawk.sh e
gawk.csh, che contengono funzioni che possono essere di aiuto
nel gestire le variabili di ambiente
AWKPATH
e AWKLIBPATH
.
Su un sistema Fedora GNU/Linux, questi file dovrebbero essere installati
nella directory /etc/profile.d;
su altre piattaforme, la posizione corretta può essere differente.
gawkpath_default
Ripristina la variabile d’ambiente AWKPATH
al suo valore di default.
gawkpath_prepend
Aggiunge l’argomento all’inizio della variabile d’ambiente AWKPATH
.
gawkpath_append
Aggiunge l’argomento alla fine della variabile d’ambiente AWKPATH
.
gawklibpath_default
Reimposta la variabile d’ambiente AWKLIBPATH
al suo valore di default.
gawklibpath_prepend
Aggiunge l’argomento all’inizio della variabile d’ambiente
AWKLIBPATH
.
gawklibpath_append
Aggiunge l’argomento alla fine della variabile d’ambiente
AWKLIBPATH
.
Successivo: Filosofia della configurazione, Precedente: File da usare a inizio sessione, Su: Installazione Unix [Contenuti][Indice]
Ci sono parecchie altre opzioni che si possono utilizzare sulla riga
di comando di configure
quando si compila gawk
a partire dai sorgenti, tra cui:
--disable-extensions
Richiede di non configurare e generare le estensioni di esempio nella
directory extension. Questo è utile quando si genera
gawk
per essere eseguito su un’altra piattaforma.
L’azione di default è di controllare dinamicamente se le estensioni
possono essere configurate e compilate.
--disable-lint
Disabilita i controlli lint all’interno di gawk
. Le opzioni
--lint e --lint-old
(vedi Opzioni)
sono accettate, ma non fanno nulla, e non emettono alcun messaggio di
avvertimento.
Analogamente, se si imposta la variabile LINT
(vedi Variabili modificabili dall'utente)
questa non ha alcun effetto sul programma awk
in esecuzione.
Se si specifica l’opzione del compilatore GNU Compiler Collection (GCC) che
elimina il codice non eseguito, quest’opzione riduce di quasi
23K byte la dimensione del programma eseguibile gawk
su sistemi GNU/Linux x86_64. I risultati su altri sistemi e con
altri compilatori sono probabilmente diversi.
L’uso di questa opzione può apportare qualche piccolo miglioramento nei
tempi di esecuzione di un programma.
ATTENZIONE: Se si usa quest’opzione alcuni dei test di funzionalità non avranno successo. Quest’opzione potrà essere rimossa in futuro.
--disable-nls
Non attiva la traduzione automatica dei messaggi. Ciò normalmente non è consigliabile, ma può apportare qualche lieve miglioramento nei tempi di esecuzione di un programma.
--with-whiny-user-strftime
Forza l’uso della versione della funzione C strftime()
inclusa nella
distribuzione di gawk
, per i sistemi in cui la funzione stessa
non sia disponibile.
Si usi il comando ‘./configure --help’ per ottenere la lista completa
delle opzioni disponibili in configure
.
Precedente: Ulteriori opzioni di configurazione, Su: Installazione Unix [Contenuti][Indice]
Questa sezione interessa solo a chi abbia un minimo di familiarità con il linguaggio C e con i sistemi operativi di tipo Unix.
Il codice sorgente di gawk
, in generale, cerca di aderire, nei limiti
del possibile, a degli standard formali. Ciò significa che gawk
usa
routine di libreria che sono specificate nello standard ISO C e nello standard
POSIX per le interfacce dei sistemi operativi. Il codice sorgente di
gawk
richiede l’uso di un compilatore ISO C (standard 1990).
Molti sistemi Unix non aderiscono completamente né allo standard ISO né a
quello POSIX. La sottodirectory missing_d nella distribuzione di
gawk
contiene delle versioni sostitutive per quelle funzioni che più
frequentemente risultano essere non disponibili.
Il file config.h creato da configure
contiene definizioni che
elencano funzionalità del particolare sistema operativo nel quale si tenta di
compilare gawk
. Le tre cose descritte da questo file sono: quali
file di intestazione sono disponibili, in modo da poterli includere correttamente,
quali funzioni (presumibilmente) standard sono realmente disponibili nelle
librerie C, e varie informazioni assortite riguardo al sistema operativo
corrente. Per esempio, può non esserci l’elemento st_blksize
nella
struttura stat
. In questo caso, ‘HAVE_STRUCT_STAT_ST_BLKSIZE’ è
indefinito.
È possible che il compilatore C del sistema in uso "tragga in inganno"
configure
. Può succedere nel caso in cui non viene restituito
un errore se una funzione di libreria non è disponibile. Per superare questo
problema, si può modificare il file custom.h. Basta usare una direttiva
‘#ifdef’ appropriata per il sistema corrente, e definire, tramite
#define
, tutte le costanti che configure
avrebbe dovuto
definire, ma non è riuscito a farlo, oppure, usando #undef
annullare le
costanti che configure
ha definito, ma non avrebbe dovuto farlo. Il
file custom.h è automaticamente incluso dal file config.h.
È anche possibile che il programma configure
generato da Autoconf non
funzioni in un dato sistema per una ragione differente. Se c’è un problema, si
tenga presente che il file configure.ac è quello preso in input da
Autoconf. È possibile modificare questo file e generare una nuova versione di
configure
che funzioni sul sistema corrente (vedi Bug per
informazioni su come segnalare problemi nella configurazione di
gawk
). Lo stesso meccanismo si può usare per inviare aggiornamenti
al file configure.ac e/o a custom.h.
Successivo: Bug, Precedente: Installazione Unix, Su: Installazione [Contenuti][Indice]
Questa sezione descrive come installare gawk
su
vari sistemi non-Unix.
• Installazione su PC: | Installare e compilare gawk
su Microsoft Windows.
| |
• Installazione su VMS: | Installare gawk su VMS.
|
Successivo: Installazione su VMS, Su: Installazione non-Unix [Contenuti][Indice]
Questa sezione tratta dell’installazione e uso di gawk
su macchine con architettura Intel che eseguono qualsiasi versione di
MS-Windows.
In questa sezione, il termine “Windows32”
si riferisce a una qualsiasi versione di Microsoft Windows
95/98/ME/NT/2000/XP/Vista/7/8/10.
Si veda anche il file README_d/README.pc nella distribuzione.
• Installazione binaria su PC: | Installare una distribuzione pronta all’uso. | |
• Compilazione su PC: | Compilare gawk per Windows32.
| |
• Uso su PC: | Eseguire gawk su Windows32.
| |
• Cygwin: | Compilare ed eseguire gawk
per Cygwin.
| |
• MSYS: | Usare gawk nell’ambiente MSYS.
|
Successivo: Compilazione su PC, Su: Installazione su PC [Contenuti][Indice]
La sola distribuzione binaria predisposta supportata per i sistem MS-Windows
è quella messa a disposizione da Eli Zaretskii
progetto “ezwinports”.
Si parta da lì per installare il comando gawk
precompilato.
Successivo: Uso su PC, Precedente: Installazione binaria su PC, Su: Installazione su PC [Contenuti][Indice]
gawk
per sistemi operativi di PCgawk
può essere compilato per Windows32, usando MinGW
(per Windows32).
Il file README_d/README.pc nella distribuzione gawk
contiene ulteriori annotazioni, e il file pc/Makefile contiene
informazioni importanti sulle opzioni di compilazione.
Per compilare gawk
per Windows32, occorre copiare i file
dalla directory pc (tranne il file ChangeLog) alla
directory che contiene il resto dei sorgenti di gawk
, e quindi
chiamare make
, specificando il nome appropriato di obiettivo come
argomento, per generare gawk
. Il Makefile copiato dalla
directory pc contiene una sezione di configurazione con commenti, e può
essere necessario modificarlo perché funzioni con il programma di utilità
make
corrente.
Il Makefile contiene un certo numero di alternative, che permettono di
generare gawk
per diverse
versioni MS-DOS e Windows32. Se il comando make
è richiamato senza
specificare alcun argomento viene stampata una lista delle alternative
disponibili. Per esempio,
per generare un codice binario di gawk
nativo per MS-Windows
usando gli strumenti MinGW, scrivere ‘make mingw32’.
Successivo: Cygwin, Precedente: Compilazione su PC, Su: Installazione su PC [Contenuti][Indice]
gawk
su sistemi operativi PCSotto MS-Windows, gli ambienti Cygwin e MinGW consentono di usare sia l’operatore ‘|&’ che le operazioni su rete TCP/IP (vedi Reti TCP/IP).
Le versioni MS-Windows di gawk
ricercano i file di
programma come descritto in AWKPATH (Variabile). Comunque, gli elementi
della variabile AWKPATH
sono separati tra di loro da un punto e virgola
(anziché da due punti (:
)).
Se AWKPATH
è non impostata o ha per valore la stringa nulla, il percorso
di ricerca di default è ‘.;c:/lib/awk;c:/gnu/lib/awk’.
Sotto MS-Windows,
gawk
(come molti altri programmi di trattamento testi) converte
automaticamente la stringa di fine riga ‘\r\n’ in ‘\n’ leggendo dall’input
e ‘\n’ in ‘\r\n’ scrivendo sull’output.
La variabile speciale BINMODE
(e.c.) permette di controllare
come avvengono queste conversioni, ed è interpretata come segue:
BINMODE
è "r"
o uno,
la modalità binaria è impostata
in lettura (cioè, nessuna conversione in lettura).
BINMODE
è "w"
o due,
la modalità binaria è impostata
in scrittura (cioè, nessuna conversione in scrittura).
BINMODE
è "rw"
o "wr"
o tre,
la modalità binaria è impostata sia in lettura che in scrittura.
BINMODE=stringa-non-nulla
equivale a specificare
‘BINMODE=3’ (cioè, nessuna conversione in
lettura e scrittura). Tuttavia, gawk
emette un messaggio di
avviso se la stringa non è "rw"
o "wr"
.
La modalità di trattamento dello standard input e standard output sono
impostate una volta sola
(dopo aver letto la riga di comando, ma prima di iniziare a elaborare
qualsiasi programma awk
).
L’impostazione di BINMODE
per standard input o
standard output va fatta usando
un’appropriata opzione ‘-v BINMODE=N’ sulla riga di comando.
BINMODE
è impostato nel momento in cui un file o pipe è aperto
e non può essere cambiato in corso di elaborazione.
Il nome BINMODE
è stato scelto in analogia con mawk
(vedi Altre versioni).
mawk
e gawk
gestiscono BINMODE
in maniera simile;
tuttavia, mawk
prevede un’opzione ‘-W BINMODE=N’ e una
variabile d’ambiente che può impostare BINMODE
, RS
, e ORS
.
I file binmode[1-3].awk (nella directory gnu/lib/awk in alcune
delle distribuzioni binarie già predisposte) sono stati inclusi per rendere
disponibile l’opzione di mawk
‘-W BINMODE=N’. Questi
possono essere modificati o ignorati; in particolare, quale sia l’impostazione
di RS
che dà meno “sorprese” rimane una questione aperta.
mawk
usa ‘RS = "\r\n"’ se si imposta la modalità binaria in
lettura, il che è appropriato per file che abbiano i caratteri di fine riga in
stile MS-DOS.
Per chiarire, gli esempi seguenti impostano la modalità binaria in
scrittura per lo standard output e altri file, e impostano ORS
in modo
da ottenere la fine riga “normale” in stile MS-DOS:
gawk -v BINMODE=2 -v ORS="\r\n" …
o:
gawk -v BINMODE=w -f binmode2.awk …
Questi comandi danno lo stesso risultato dell’opzione
‘-W BINMODE=2’ in mawk
.
Quanto segue modifica il separatore di record a "\r\n"
e imposta
la modalità binaria in lettura, senza modificare le letture da standard input:
gawk -v RS="\r\n" -e "BEGIN { BINMODE = 1 }" …
o:
gawk -f binmode1.awk …
Usando i caratteri di protezione appropriati, nel primo
esempio l’impostazione di RS
può essere spostata in una regola
BEGIN
.
Successivo: MSYS, Precedente: Uso su PC, Su: Installazione su PC [Contenuti][Indice]
gawk
in ambiente Cygwingawk
può essere compilato e usato “così com’è” sotto MS-Windows se
si opera all’interno dell’ambiente Cygwin.
Questo ambiente consente un’eccellente simulazione di GNU/Linux, con l’uso di
Bash, GCC, GNU Make, e altri programmi GNU. La compilazione e l’installazione
per Cygwin è la stessa usata nei sistemi di tipo Unix:
tar -xvpzf gawk-4.1.4.tar.gz cd gawk-4.1.4 ./configure make && make check
In confronto a un sistema GNU/Linux sulla stessa macchina, l’esecuzione del passo di ‘configure’ sotto Cygwin richiede molto più tempo. Tuttavia si conclude regolarmente, e poi ‘make’ funziona come ci si aspetta.
Precedente: Cygwin, Su: Installazione su PC [Contenuti][Indice]
gawk
in ambiente MSYSNell’ambiente MSYS sotto MS-Windows, gawk
automaticamente usa la
modalità binaria per leggere e scrivere file. Non è quindi necessario usare la
variabile BINMODE
.
Questo può causare problemi con altri componenti di tipo Unix che sono stati
resi disponibile in MS-Windows, che si aspettano che gawk
faccia
automaticamente la conversione di "\r\n"
, mentre così non è.
Precedente: Installazione su PC, Su: Installazione non-Unix [Contenuti][Indice]
gawk
su Vax/VMS e OpenVMSQuesta sottosezione descrive come compilare e installare gawk
sotto VMS. Il termine classico “VMS” è usato qui anche per designare
OpenVMS.
• Compilazione su VMS: | Come compilare gawk su VMS.
| |
• Estensioni dinamiche su VMS: | Compilare estensioni dinamiche
di gawk su VMS.
| |
• Dettagli installazione su VMS: | Come installare gawk su VMS.
| |
• Esecuzione su VMS: | Come eseguire gawk su VMS.
| |
• GNV su VMS: | Il progetto VMS GNV. | |
• Vecchio Gawk su VMS: | Una versione non aggiornata arriva con alcune versioni di VMS. |
Successivo: Estensioni dinamiche su VMS, Su: Installazione su VMS [Contenuti][Indice]
gawk
su VMSPer compilare gawk
sotto VMS, esiste una procedura di comandi
DCL
che esegue tutti i comandi CC
e LINK
necessari. C’è
anche un Makefile da usare con i programmi di utilità MMS
e
MMK
. A partire della directory che contiene i file sorgente, si usi:
$ @[.vms]vmsbuild.com
o:
$ MMS/DESCRIPTION=[.vms]descrip.mms gawk
o:
$ MMK/DESCRIPTION=[.vms]descrip.mms gawk
Il comando MMK
è un quasi-clone, a codice aperto e gratuito, di
MMS
, che gestisce in maniera migliore i volumi ODS-5 con nomi-file
a caratteri maiuscoli e minuscoli. MMK
è disponibile da
https://github.com/endlesssoftware/mmk.
Avendo a che fare con volumi ODS-5 e con l’analisi sintattica estesa abilitata, il nome del parametro che specifica l’obiettivo può dover essere scritto digitando esattamente le lettere maiuscole e minuscole.
gawk
è stato testato sotto VAX/VMS 7.3 e Alpha/VMS 7.3-1 usando il
compilatore Compaq C V6.4, e sotto Alpha/VMS 7.3, Alpha/VMS 7.3-2, e IA64/VMS
8.3. Le compilazioni più recenti hanno usato il compilatore HP C V7.3 su Alpha
VMS 8.3 e su VMS 8.4, sia Alpha che IA64, hanno usato il compilatore HP C
7.3.119
Vedi GNV su VMS per informazioni su come compilare
gawk
come un kit PCSI compatible con il prodotto GNV.
Successivo: Dettagli installazione su VMS, Precedente: Compilazione su VMS, Su: Installazione su VMS [Contenuti][Indice]
gawk
in VMSLe estensioni che sono state rese disponibile su VMS possono essere costruite dando uno dei comandi seguenti:
$ MMS/DESCRIPTION=[.vms]descrip.mms extensions
o:
$ MMK/DESCRIPTION=[.vms]descrip.mms extensions
gawk
usa AWKLIBPATH
come una variabile d’ambiente
oppure come un nome logico per trovare le estensioni dinamiche.
Le estensioni dinamiche devono essere compilate con le stesse opzioni del
compilatore usate per compilare gawk
riguardanti numeri in virgola
mobile, dimensione dei puntatori e trattamento dei nomi simbolici. I computer
con architettura Alpha e Itanium dovrebbero usare i numeri in virgola mobile
col formato IEEE. La dimensione dei puntatori è 32 bit, e il trattamento dei nomi
simbolici dovrebbe richiedere il rispetto esatto di maiuscole/minuscole, con le
abbreviazioni CRC per simboli più lunghi di 32 bit.
Per Alpha e Itanium:
/name=(as_is,short) /float=ieee/ieee_mode=denorm_results
Per VAX:
/name=(as_is,short)
Le macro da usare al momento della compilazione devono essere definite prima di includere il primo file di intestazione proveniente da VMS, come segue:
#if (__CRTL_VER >= 70200000) && !defined (__VAX) #define _LARGEFILE 1 #endif #ifndef __VAX #ifdef __CRTL_VER #if __CRTL_VER >= 80200000 #define _USE_STD_STAT 1 #endif #endif #endif
Se si scrivono delle estensioni utente da eseguire su VMS, vanno fornite anche
queste definizioni. Il file config.h creato quando si compila
gawk
su VMS lo fa già; se invece si usa qualche altro file simile,
occorre ricordarsi di includerlo prima di qualsiasi file di intestazione
proveniente da VMS.
Successivo: Esecuzione su VMS, Precedente: Estensioni dinamiche su VMS, Su: Installazione su VMS [Contenuti][Indice]
gawk
su VMSPer usare gawk
, tutto ciò che serve è un comando “esterno”, che è
un simbolo DCL
il cui valore inizia col segno del dollaro.
Per esempio:
$ GAWK :== $disk1:[gnubin]gawk
Si sostituisca la posizione corrente di gawk.exe
a
‘$disk1:[gnubin]’. Il simbolo dovrebbe essere posto nel file
login.com di ogni utente che desideri eseguire gawk
,
in modo che sia definito ogni volta che l’utente inizia una sessione.
Alternativamente, il simbolo può essere messo nella procedura di sistema
sylogin.com,
in modo da permettere a tutti gli utenti di eseguire gawk
.
Se gawk
è stato installato da un kit PCSI nell’albero di
directory GNV$GNU:, il programma avrà come nome
GNV$GNU:[bin]gnv$gawk.exe, e il file di aiuto sarà chiamato
GNV$GNU:[vms_help]gawk.hlp.
Il kit PCSI installa anche un file GNV$GNU:[vms_bin]gawk_verb.cld
che può essere usato per aggiungere gawk
e awk
alla lista dei comandi DCL.
Per farlo solo nella sessione corrente si può usare:
$ set command gnv$gnu:[vms_bin]gawk_verb.cld
Oppure il sistemista VMS può usare GNV$GNU:[vms_bin]gawk_verb.cld per
aggiungere gawk
e awk
alla tabella ‘DCLTABLES’
valida per tutto il sistema.
La sintassi DCL è documentata nel file gawk.hlp.
In alternativa, l’elemento gawk.hlp può essere caricato in una libreria di aiuto VMS:
$ LIBRARY/HELP sys$help:helplib [.vms]gawk.hlp
(Una libreria specifica dell’installazione potrebbe venir usata invece della libreria standard VMS library ‘HELPLIB’.) Dopo aver installato il testo di aiuto, il comando:
$ HELP GAWK
fornisce informazioni sia sull’implementazione di gawk
sia sul linguaggio di programmazione awk
.
Il nome logico ‘AWK_LIBRARY’ può designare una posizione di default per i
file di programma awk
. Riguardo all’opzione -f, se il
nome-file specificato non contiene un dispositivo o un percorso di directory,
gawk
cerca dapprima nella directory corrente, poi nella directory
specificata dalla traduzione di ‘AWK_LIBRARY’ se il file non è stato
trovato. Se, dopo aver cercato in entrambe queste directory, il file non è
ancora stato trovato, gawk
appone il suffisso ‘.awk’ al
nome-file e ritenta la ricerca del file. Se ‘AWK_LIBRARY’ non è
definita, si usa per essa il valore di default ‘SYS$LIBRARY:’.
Successivo: GNV su VMS, Precedente: Dettagli installazione su VMS, Su: Installazione su VMS [Contenuti][Indice]
gawk
su VMSL’elaborazione della riga di comando e le convenzioni per proteggere i
caratteri sono significativamente differenti in VMS, e quindi gli esempi
presenti in questo Documento o provenienti da altre fonti necessitano
piccole modifiche. Le modifiche, tuttavia, sono veramente piccole, e
tutti i programmi awk
dovrebbero funzionare correttamente.
Ecco un paio di semplici test:
$ gawk -- "BEGIN {print ""Hello, World!""}" $ gawk -"W" version ! ma anche -"W version" o "-W version"
Si noti che il testo con caratteri maiuscoli e misti maiuscoli/minuscoli dev’essere incluso tra doppi apici.
La versione VMS di gawk
comprende un’interfaccia in stile DCL
,
oltre a quella originale, di tipo shell (si veda il file di aiuto per ulteriori
dettagli). Un effetto indesiderato della duplice analisi della riga
di comando è che se c’è solo un unico parametro (come nel programma con le
righe contenenti doppi apici), il comando diviene ambiguo. Per evitare questo
inconveniente, il flag, normalmente non necessario, -- è necessario
per forzare un esame dei parametri in stile Unix, piuttosto che nella modalità
DCL
. Se qualsiasi altra opzione preceduta dal segno - (o più
parametri, per esempio, più file-dati in input) è presente, non c’è ambiguità,
e l’opzione -- può essere omessa.
Il valore di exit
è un valore in stile Unix e viene trasformato in
una condizione VMS all’uscita del programma.
I bit di severità di VMS saranno impostati a partire dal valore dell’istruzione
exit
. Un errore grave è indicato da 1, e VMS imposta la condizione
ERROR
. Un errore fatale è indicato da 2, e VMS imposta la condizione
FATAL
. Ogni altro valore imposta la condizione SUCCESS
. Il
valore d’uscita è codificato per aderire agli standard di codifica VMS e avrà
un C_FACILITY_NO
di 0x350000
con il codice costante 0xA000
aggiunto al numero spostato a sinistra di 3 bit per far posto al codice di
severità.
Per estrarre il codice reale di ritorno dell’istruzione exit
di gawk
dalla condizione impostata da VMS, si usi:
unix_status = (vms_status .and. %x7f8) / 8
Un programma C che usa exec()
per chiamare gawk
riceverà il valore originale della exit in stile Unix.
Precedenti versioni di gawk
per VMS consideravano un codice di
ritorno a Unix di 0 come 1, un errore come 2, un errore fatale come 4, e tutti
gli altri valori erano restituiti immodificati. Questa era una violazione
rispetto alle specifiche di codifica delle condizioni di uscita di VMS.
L’aritmetica in virgola mobile VAX/VMS usa un arrotondamento statistico. Vedi Funzione round.
VMS restituisce data e ora in formato GMT, a meno che non siano stati impostati
i nomi logici SYS$TIMEZONE_RULE
o TZ
. Precedenti versioni di
VMS, come VAX/VMS 7.3, non impostano questi nomi logici.
Il percorso di ricerca di default, nella ricerca dei file di programma per
awk
specificati dall’opzione -f, è
"SYS$DISK:[],AWK_LIBRARY:"
. Il nome logico AWKPATH
può essere
usato per sostituire questo di default. Il formato di AWKPATH
è una lista
di directory, separate da virgola. Nel definirla, il valore dovrebbe essere
incluso tra doppi apici, in modo che consenta una sola traduzione, e non una
lista di ricerca multitraduzione RMS
.
Questa restrizione vale anche se si esegue gawk
sotto GNV,
in quanto la ridirezione è sempre verso un comando DCL.
Se si ridirigono dati verso un comando o un programma di utilità VMS,
l’implementazione corrente richiede la creazione di un comando VMS esterno che
esegua un file di comandi, prima di invocare gawk
.
(Questa restrizione potrebbe essere rimossa in una futura versione di
gawk
per VMS.)
Senza un tale file di comandi, i dati in input saranno presenti anche in output, prima dei dati di output veri e propri.
Ciò consente la simulazione di comandi POSIX non disponibili in VMS o l’uso di programmi di utilità GNV.
L’esempio seguente mostra come ridirigere dati da gawk
verso il
comando VMS sort
.
$ sort = "@device:[dir]vms_gawk_sort.com"
Il file di comandi deve avere il formato dell’esempio seguente.
La prima riga serve a evitare che i dati in input siano presenti anche nell’output. Dev’essere nel formato mostrato nell’esempio.
La riga seguente crea un comando esterno che prevale sul comando esterno superiore, che serve a prevenire una ricorsione infinita di file di comandi.
Il penultimo comando ridirige sys$input
su sys$command
,
per poter ottenere i dati che sono ridiretti verso il comando.
L’ultima riga esegue il comando vero e proprio. Dev’essere l’ultimo
comando, perché i dati ridiretti da gawk
saranno letti
quando il file di comandi finisce di essere eseguito.
$!'f$verify(0,0)' $ sort := sort $ define/user sys$input sys$command: $ sort sys$input: sys$output:
Successivo: Vecchio Gawk su VMS, Precedente: Esecuzione su VMS, Su: Installazione su VMS [Contenuti][Indice]
Il pacchetto VMS GNV fornisce un ambiente di sviluppo simile
a POSIX tramite una collezione di strumenti open source.
Il gawk
presente nel pacchetto base GNV è una vecchia versione.
Attualmente, il progetto GNV è in fase di riorganizzazione, con l’obiettivo
di offrire pacchetti PCSI separati per ogni componente.
Si veda https://sourceforge.net/p/gnv/wiki/InstallingGNVPackages/.
La procedura normale per compilare gawk
produce un programma
adatto a essere usato con GNV.
Il file vms/gawk_build_steps.txt nella distribuzione documenta la procedura per compilare un pacchetto PCSI compatible con GNV.
Precedente: GNV su VMS, Su: Installazione su VMS [Contenuti][Indice]
gawk
su sistemi VMSAlcune versioni di VMS includono una vecchia versione di gawk
.
Per utilizzarla, occorre definire un simbolo, come segue:
$ gawk :== $sys$common:[syshlp.examples.tcpip.snmp]gawk.exe
La versione appare essere la versione 2.15.6, che è molto vecchia. Si raccomanda di compilare e usare la versione corrente.
Successivo: Altre versioni, Precedente: Installazione non-Unix, Su: Installazione [Contenuti][Indice]
Non c’è niente di più pericoloso di un archeologo annoiato.
Se si incontrano problemi con gawk
o se si ritiene di aver trovato un
bug, si raccomanda di segnalarlo agli sviluppatori;
non c’è un impegno preciso a intervenire, ma c’è una buona possibilità che ci
si sforzi di risolverlo.
• Indirizzo Bug: | Dove inviare le segnalazioni. | |
• Usenet: | Dove non inviare le segnalazioni. | |
• Manutentori: | Manutentori di version non-*nix. |
Prima di segnalare un bug, occorre assicurarsi che sia davvero un bug. La documentazione va riletta attentamente, per controllare se dice che è possibile fare quel che si sta tentando di fare. Se non è chiaro se sia possibile fare quella particolare cosa o no, occorre segnalarlo; in questo caso si tratta di un bug nella documentazione!
Prima di segnalare un bug o di tentare di risolverlo personalmente, si tenti
di isolarlo preparando un programma awk
il più piccolo possibile, con
un file-dati in input che possa riprodurre il problema. Dopo averlo fatto, si
spedisca il programma e il file-dati, insieme a informazioni sul tipo di
sistema Unix in uso, il compilatore usato per compilare gawk
, e i
risultati esatti che gawk
ha prodotto. Inoltre andrebbe specificato
cosa ci si aspettava che il programma facesse; questo è di aiuto per decidere
se il problema è un problema di documentazione.
È importante includere il numero di versione di gawk
in uso.
Questa informazione si può ottenere con il comando ‘gawk --version’.
Una volta pronta la descrizione precisa del problema, si spedisca un messaggio di posta elettronica a “bug-gawk at gnu dot org”.
I manutentori di gawk
sono i destinatari, e riceveranno la
segnalazione di errore. Sebbene sia possibile spedire messaggi direttamente ai
manutentori, è preferibile usare l’indirizzo sopra fornito perché quella
mailing list rimane in archivio presso il Progetto GNU. Tutti i messaggi
devono essere in inglese. È questo il solo linguaggio che tutti i manutentori
conoscono. Inoltre, occorre accertarsi di spedire tutti i messaggi in formato
testo, e non (o non soltanto) in formato HTML.
NOTA: Molte distribuzioni di GNU/Linux e i vari sistemi operativi basati su BSD hanno un loro proprio canale per segnalare i bug. Se si segnala un bug usando il canale della distribuzione, una copia del messaggio andrebbe inviata anche a “bug-gawk at gnu dot org”.
Questo per due ragioni. La prima è che, sebbene alcune distribuzioni inoltrino i messaggi sui problemi “verso l’alto” alla mailing list GNU, molte non lo fanno, e quindi c’è una buona probabilità che i manutentori di
gawk
non vedano affatto il messaggio relativo al bug! La seconda ragione è che la posta diretta alla mailing list GNU è archiviata, e il poter disporre di ogni cosa all’interno del progetto GNU consente di avere a disposizione tutte le informazioni rilevanti senza dover dipendere da altre organizzazioni.
Suggerimenti non correlati a bug sono pure sempre benvenuti. Se si hanno domande riguardo a qualcosa di non chiaro nella documentazione o a proposito di funzionalità oscure, si scriva alla mailing list dei bug; si proverà a essere di aiuto nei limiti del possibile.
Successivo: Manutentori, Precedente: Indirizzo Bug, Su: Bug [Contenuti][Indice]
Ho iniziato a ignorare Usenet un paio di anni fa, e non me ne sono mai pentito. È come quando si parla di sport alla radio—ci si sente più intelligenti per aver lasciato perdere.
Siete pregati di non provare a notificare bug di gawk
scrivendo al gruppo di discussione Usenet/Internet comp.lang.awk
.
Sebbene alcuni degli sviluppatori di gawk
leggano talora i
messaggi di questo gruppo di discussione, il manutentore principale di
gawk
non lo fa più. Quindi è praticamente certo che un
messaggio inviato là non sia da lui letto.
La procedura qui descritta è la sola maniera ufficialmente riconosciuta
per notificare problemi. Davvero!
Se si riscontrano bug in una delle versioni non-Unix di gawk
, una
copia del messaggio inviato alla mailing list dei bug andrebbe spedita alla
persona che si occupa di quella versione. I manutentori sono elencati nella
lista seguente, come pure nel file README nella distribuzione
gawk
. Le informazioni nel file README dovrebbero essere
considerate come le più aggiornate, se risultano in conflitto con questo
Documento.
Le persone che si occupano delle varie versioni di gawk
sono:
Unix e sistemi POSIX | Arnold Robbins, “arnold at skeeve dot com” |
MS-Windows con MinGW | Eli Zaretskii, “eliz at gnu dot org” |
OS/2 | Andreas Buening, “andreas dot buening at nexgo dot de” |
VMS | John Malmberg, “wb8tyw at qsl.net” |
z/OS (OS/390) | Daniel Richard G. “skunk at iSKUNK.ORG” |
Dave Pitts (Maintainer Emeritus), “dpitts at cozx dot com” |
Se il problema riscontrato è riproducibile anche sotto Unix, si dovrebbe spedire una copia del messaggio anche alla mailing list “bug-gawk at gnu dot org”.
La versione generata usando gli strumenti DJGPP non è più supportata; il codice relativo resterà nella distribuzione ancora per qualche tempo, nel caso che qualche volontario desideri prenderla in carico. Se questo non dovesse succedere, la parte di codice relativa questa versione sarà rimossa dalla distribuzione.
Successivo: Sommario dell'installazione, Precedente: Bug, Su: Installazione [Contenuti][Indice]
awk
liberamente disponibiliÈ piuttosto divertente mettere commenti simili nel vostro codice awk:
// Funzionano i commenti in stile C++? Risposta: sì! certo
Ci sono alcune altre implementazioni di awk
disponibili
gratuitamente.
Questa sezione descrive in breve dove è possibile trovarle:
awk
Brian Kernighan, uno degli sviluppatori originali di Unix awk
,
ha reso disponibile liberamente la sua implementazione di awk
.
Si può scaricare questa versione dalla
sua pagina principale.
È disponibile in parecchi formati compressi:
tar
compressoÈ anche disponbile in GitHub:
git clone git://github.com/onetrueawk/awk bwkawk
Questo comando crea una copia del deposito Git
in una directory chiamata bwkawk. Se si omette questo argomento della
riga di comando git
, la copia del deposito è creata nella
directory di nome awk.
Questa versione richiede un compilatore ISO C (standard 1990); il compilatore C contenuto in GCC (la collezione di compilatori GNU) è più che sufficiente.
Vedi Estensioni comuni
per una lista di estensioni in questo awk
che non sono in
POSIX awk
.
Incidentalmente, Dan Bornstein ha creato un deposito Git che contiene tutte le
versioni di BWK awk
che è riuscito a trovare. È disponibile in
git://github.com/danfuzz/one-true-awk.
mawk
Michael Brennan ha scritto un’implementazione indipendente di awk
,
di nome mawk
. È disponibile sotto la licenza
GPL (vedi Copia),
proprio come gawk
.
Il sito di distribuzione originale di mawk
non contiene più
il codice sorgente. Una copia è disponibile in
http://www.skeeve.com/gawk/mawk1.3.3.tar.gz.
Dal 2009 è Thomas Dickey a occuparsi della manutenzione di mawk
.
Le informazioni di base sono disponibili nella
pagine web del progetto.
Il puntatore URL da cui scaricare è
http://invisible-island.net/datafiles/release/mawk.tar.gz.
Una volta scaricato,
per scompattare questo file può essere usato gunzip
.
L’installazione è simile a quella di gawk
(vedi Installazione Unix).
Vedi Estensioni comuni
per una lista di estensioni in mawk
che non sono in POSIX awk
.
awka
Scritto da Andrew Sumner,
awka
traduce i programmi awk
in C, li compila,
e prepara il codice eseguibile usando una libreria di funzioni che
implementano le funzionalità di base di awk
.
Comprende anche un certo numero di estensioni.
Il traduttore di awk
è rilasciato sotto la licenza GPL, e la
relativa libreria sotto la licenza LGPL.
Per ottenere awka
, si visiti
il sito http://sourceforge.net/projects/awka.
Il progetto sembra essere stato congelato; non ci sono state modifiche nel codice sorgente dal 2001 circa.
pawk
Nelson H.F. Beebe all’Università dello Utah ha modificato
BWK awk
per fornire informazioni di temporizzazione e profilatura.
Questo è differente dall’usare gawk
con l’opzione --profile
(vedi Profilare)
nel senso che fornisce un profilo basato sul consumo di CPU, non sul
numero di esecuzioni di una data riga di codice.
Sia può trovare sia in
ftp://ftp.math.utah.edu/pub/pawk/pawk-20030606.tar.gz
che in
http://www.math.utah.edu/pub/pawk/pawk-20030606.tar.gz.
awk
BusyBox è un programma distribuito con licenza GPL che fornisce versioni
ridotte di parecchie piccole applicazioni, all’interno di un singolo modulo
eseguibile. È stato ideato per sistemi
integrati.
Include un’implementazione completa di POSIX awk
. Quando lo si
compila occorre prestare attenzione a non eseguire ‘make install’, perché
in questo modo si andrebbero a sostituire copie di altre applicazioni nella
directory /usr/local/bin del sistema corrente. Per ulteriori
informazioni, si veda la pagina principale del progetto.
awk
per OpenSolarisLe versioni di awk
in /usr/xpg4/bin e /usr/xpg6/bin su
Solaris sono grosso modo conformi allo standard POSIX. Sono basate sul
comando awk
preparato per i PC dalla ditta Mortice Kern. È stato
possibile compilare e far funzionare questo codice sotto GNU/Linux dopo 1–2
ore di lavoro. Rendere questo codice più generalmente portabile (usando gli
strumenti GNU Autoconf e/o Automake) richiederebbe ulteriore lavoro, che non è
stato fin qui compiuto, almeno per quel che risulta a chi scrive.
Il codice sorgente era un tempo disponibile dal sito web OpenSolaris. Tuttavia, il progetto è terminato, e il sito web chiuso. Fortunatamente, il progetto Illumos mette a disposizione questa implementazione. Si possono vedere i singoli file in https://github.com/joyent/illumos-joyent/blob/master/usr/src/cmd/awk_xpg4.
jawk
Questo è un interprete per awk
scritto in Java. Dichiara di
essere un interprete completo, anche se, poiché usa funzionalità di Java
per l’I/O e per la ricerca di regexp, il linguaggio che supporta
è differente da awk
POSIX.
Ulteriori informazioni sono disponibili sulla
pagina principale del progetto.
Questo è un interprete awk
incorporabile, derivato da
mawk
. Per ulteriori informazioni, si veda
http://repo.hu/projects/libmawk/.
pawk
Questo è un modulo Python che intende introdurre funzionalità di tipo
awk
in Python. Si veda https://github.com/alecthomas/pawk per
ulteriori informazioni. (Questo programma non è correlato con la versione
modificata da Nelson Beebe di BWK awk
, descritta prima.)
awk
Questo è un interprete di awk
incorporabile. Per ulteriori
informazioni, si veda
http://code.google.com/p/qse/ e http://awk.info/?tools/qse.
QTawk
Questa è un’implementazione indipendente di awk
distribuita con la
licenza GPL. Ha un gran numero di estensioni rispetto ad awk
standard, e può non essere sintatticamente compatibile al 100% con esso. Si
veda http://www.quiktrim.org/QTawk.html per ulteriori informazioni,
compreso il manuale. Il puntatore per scaricare QuikTrim non punta all’ultima
versione: si veda http://www.quiktrim.org/#AdditionalResources per un
puntatore alla versione corrente.
Il progetto sembra essere fermo; non ci sono nuove versioni del codice a partire dal 2014 circa.
Si veda anche [in inglese] la sezione “Versions and implementations”
della voce di
Wikipedia su awk
per informazioni su ulteriori versioni.
Precedente: Altre versioni, Su: Installazione [Contenuti][Indice]
gawk
è disponibile dal sito principale
di distribuzione del Progetto GNU
ftp.gnu.org
. La maniera canonica per scaricarlo e installarlo è:
wget http://ftp.gnu.org/gnu/gawk/gawk-4.1.4.tar.gz tar -xvpzf gawk-4.1.4.tar.gz cd gawk-4.1.4 ./configure && make && make check
gawk
può essere installato anche su sistemi non-POSIX. I sistemi
correntemente supportati sono MS-Windows, usando
MSYS, MinGW, e Cygwin,
e sia Vax/VMS che OpenVMS. Le istruzioni per ognuno di questi sistemi sono
incluse in questa appendice.
gawk
in uso, come
è stata compilata, un breve programma e un file-dati che permettono di
riprodurre il problema.
awk
disponibili
gratuitamente. Molte rispettano lo standard POSIX; altre un po’ meno.
Successivo: Concetti fondamentali, Precedente: Installazione, Su: Top [Contenuti][Indice]
Quest’appendice contiene informazioni che interessano soprattutto le persone
che aggiornano e mantengono gawk
. L’intero contenuto si applica
specificatamente a gawk
e non ad altre implementazioni.
• Modalità di compatibilità: | Come inibire alcune estensioni di
gawk .
| |
• Aggiunte: | Fare aggiunte a gawk .
| |
• Future estensioni: | Nuove funzionalità che potranno essere implementate in futuro. | |
• Limitazioni dell'implementazione: | Alcune limitazioni dell’implementazione. | |
• Progetto delle estensioni: | Note di progetto sull’estensione API. | |
• Meccanismo delle vecchie estensioni: | Alcune compatibilità per le vecchie estensioni. | |
• Sommario delle note: | Sommario delle note di implementazione. |
Vedi POSIX/GNU,
per un compendio delle estensioni GNU per il linguaggio e il programma
awk
. Tutte queste funzionalità possono essere inibite invocando
gawk
con l’opzione --traditional o con l’opzione
--posix.
Se gawk
è stato compilato per il debug con ‘-DDEBUG’, è
possibile specificare un’ulteriore opzione sulla riga di comando:
-Y
--parsedebug
Stampa l’informazione contenuta nella pila di analisi, durante la fase di analisi iniziale del programma.
Quest’opzione è utile solo a chi sviluppa gawk
e non all’utente
occasionale. È probabile che non sia neppure disponibile nella versione di
gawk
che si sta usando, perché rallenta l’esecuzione del programma.
Successivo: Future estensioni, Precedente: Modalità di compatibilità, Su: Note [Contenuti][Indice]
gawk
Se si desidera migliorare gawk
in maniera significativa, c’è la
massima libertà di farlo. È questo lo scopo del software libero; il codice
sorgente è disponibile, ed è possibile modificarlo a piacere
(vedi Copia).
Questa
sezione tratta di come è possibile modificare gawk
,
ed espone alcune considerazioni che si dovrebbero tenere presenti.
• Accedere ai sorgenti: | Accedere al deposito dei sorgenti Git. | |
• Aggiungere codice: | Aggiungere codice al programma
principale gawk .
| |
• Nuovi sistemi: | Portare gawk su un nuovo sistema
operativo.
| |
• File derivati: | Perché i file derivati sono tenuti
nel deposito git .
|
Successivo: Aggiungere codice, Su: Aggiunte [Contenuti][Indice]
gawk
Poiché gawk
è Software Libero, il codice sorgente è sempre
disponibile.
Distribuzione di Gawk descrive come scaricare e installare
le versioni ufficiali rilasciate di gawk
.
Peraltro, se si intende modificare gawk
e mettere a disposizione le
modifiche, è preferibile lavorare sulla versione in via di sviluppo. Per far
ciò è necessario accedere al deposito del codice sorgente di gawk
.
Il codice è mantenuto usando il sistema distribuito
di controllo delle versioni Git. Sarà necessario installarlo se non è già
presente nel sistema. Quando git
è disponibile, va usato il comando:
git clone git://git.savannah.gnu.org/gawk.git
Questo comando scarica in locale una copia esatta del deposito dei
sorgenti di gawk
. Se si sta usando un firewall
che non consente l’uso del protocollo nativo di Git, è possibile accedere
ugualmente al deposito usando il comando:
git clone http://git.savannah.gnu.org/r/gawk.git
Una volta modificato il sorgente, è posibile usare ‘git diff’ per
produrre una patch, e spedirla al manutentore di gawk
; si veda
Bug, per come farlo.
In passato era disponibile un’interfaccia Git–CVS utilizzabile da persone che non avevano a disposizione Git. Purtroppo, quest’interfaccia non funziona più, ma si potrebbe avere maggior fortuna usando un sistema di controllo versioni più moderno, come Bazaar, che è dotato di un’estensione Git per lavorare con depositi di sorgenti Git.
Successivo: Nuovi sistemi, Precedente: Accedere ai sorgenti, Su: Aggiunte [Contenuti][Indice]
Ognuno è libero di aggiungere tutte le nuove funzionalità che vuole a
gawk
. Comunque, se si desidera che tali modifiche siano incorporate
nella distribuzione di gawk
, ci sono parecchi passi da fare per
rendere possibile la loro inclusione:
gawk
,
prendere in considerazione la possibilità di scriverla sotto forma di
estensione (vedi Estensioni dinamiche).
Se ciò non è possibile, continuare con i passi rimanenti descritti in questa
lista.
gawk
o, meglio ancora, sull’ultimo codice
sorgente presente nel deposito Git. Se la versione di gawk
è molto
vecchia, potrei non essere affatto in grado di integrare le modifiche.
(Vedi Scaricare,
per informazioni su come ottenere l’ultima versione di gawk
.)
gawk
.
(Gli Standard di codifica GNU sono disponibili nel sito web del
Progetto GNU.
Sono disponibili anche versioni in formato Texinfo, Info, e DVI.)
gawk
.
Il codice sorgente in C di gawk
segue le istruzioni dello
Standard di codifica GNU, con qualche piccola eccezione. Il codice è
formattato usando lo stile tradizionale “K&R”, in particolare per ciò che
riguarda il posizionamento delle parentesi graffe e l’uso del carattere TAB.
In breve, le regole di codifica per gawk
sono le seguenti:
int
,
sulla riga immediatamente sopra quella che contiene il nome e gli argomenti
della funzione.
if
, while
, for
, do
, switch
e return
).
for
, e nel corpo delle macro.
NULL
e '\0'
per le condizioni
contenute nelle istruzioni if
, while
e for
, e anche
nelle varie clausole case
delle istruzioni switch
, invece
del semplice puntatore o il semplice valore del carattere.
true
e false
per le variabili booleane
,
la costante simbolica NULL
per i valori dei puntatori,
e la costante carattere '\0'
quando è il caso, invece dei valori 1
e 0
.
alloca()
per allocare memoria dalla stack.
Il farlo genera dei problemi di portabilità che non giustificano il vantaggio
secondario di non doversi preoccupare di liberare la memoria. Usare invece
malloc()
e free()
.
strcmp()
non è una funzione
booleana!” Usare invece ‘strcmp(a, b) == 0’.
0x001
, 0x002
, 0x004
, etc.) invece che
spostare di un bit a sinistra in incrementi successivi
(‘(1<<0)’, ‘(1<<1)’, etc.).
NOTA: Qualora fossi costretto a riformattare completamente il codice per farlo aderire allo stile di codifica usato in
gawk
, potrei anche decidere di ignorare del tutto le modifiche proposte.
man
.
Si dovrà anche firmare un documento liberatorio relativo alle modifiche apportate alla documentazione.
gawk
con la versione proposta.
Si raccomanda di usare la versione GNU di diff
o, ancora meglio,
‘git diff’ o ‘git format-patch’.
Per inviare le modifiche proposte spedire l’output prodotto da diff
.
(Vedi Bug, per l’indirizzo di posta elettronica.)
L’uso di questo formato rende semplice per me l’applicazione delle modifiche
alla versione principale del sorgente di gawk
(usando il programma di
utilità patch
). Se invece tocca a me applicare le modifiche a mano,
con un editor di testo, potrei decidere di non farlo, specialmente
se le modifiche sono molte.
Sebbene questa possa sembrare una richiesta molto esigente, si tenga presente che, anche se il nuovo codice non è opera mia, tocca poi a me manutenerlo e correggere eventuali errori. Se non è possibile per me farlo senza perderci troppo tempo, potrei anche lasciar perdere la modifica.
Successivo: File derivati, Precedente: Aggiungere codice, Su: Aggiunte [Contenuti][Indice]
gawk
su un nuovo Sistema OperativoSe si vuol portare gawk
su di un nuovo sistema operativo, sono
necessari parecchi passi:
gawk
e con le
versioni per altri sistemi operativi.
Evitare modifiche non necessarie alla parte di codice che è indipendente
dal sistema operativo. Se possibile, evitare di disseminare ‘#ifdef’,
utili solo per il proprio port, all’interno del codice sorgente.
Se le modifiche necessarie per un particolare sistema coinvolgono una parte troppo rilevante del codice, è probabile che io non le accetti. In questo caso si possono, naturalmente, distribuire le modifiche per proprio conto, basta che si rispettino i vincoli della GPL (vedi Copia).
gawk
sono mantenuti da terze persone e non dagli sviluppatori di gawk
.
Quindi, non si dovrebbero cambiare, se non per ragioni molto
valide; vale a dire, modifiche a questi file non sono impossibili, ma
le modifiche a questi file saranno controllate con estrema attenzione.
I file sono
dfa.c,
dfa.h,
getopt.c,
getopt.h,
getopt1.c,
getopt_int.h,
gettext.h,
regcomp.c,
regex.c,
regex.h,
regex_internal.c,
regex_internal.h
e
regexec.c.
gettext
).
Neppure questi file dovrebbero essere modificati, se non per ragioni molto
valide. I file sono
ABOUT-NLS,
config.guess,
config.rpath,
config.sub,
depcomp,
INSTALL,
install-sh,
missing,
mkinstalldirs,
xalloc.h
e
ylwrap.
gawk
nei loro sistemi. Se nessuno è disponibile a tener aggiornato un port,
questo diventa non più supportato, e può essere necessario rimuoverlo dalla
distribuzione.
Ogni file gawkmisc.??? del port ha un suffisso esplicativo del tipo di macchina o del sistema operativo in questione—per esempio, pc/gawkmisc.pc e vms/gawkmisc.vms. L’uso di suffissi distinti invece di un semplice gawkmisc.c, rende possibile spostare file da una sottodirectory propria del port nella sottodirectory principale, senza cancellare incidentalmente il file gawkmisc.c vero e proprio. (Al momento, questo rappresenta un problema solo per i port ai sistemi operativi dei PC.)
gawk
per il sistema desiderato.
Seguire queste indicazioni facilita molto l’integrazione delle
modifiche in gawk
e la loro felice coesistenza con il codice di
altri sistemi operativi già presenti.
Nel codice che viene fornito e tenuto aggiornato, si possono tranquillamente usare uno stile di codifica e una disposizione delle parentesi graffe di proprio gradimento.
Precedente: Nuovi sistemi, Su: Aggiunte [Contenuti][Indice]
Se si esaminano i sorgenti di gawk
nel deposito Git
si noterà che sono inclusi file generati automaticamente dagli strumenti
dell’infrastruttura GNU, come Makefile.in generato da Automake e
anche configure proveniente da Autoconf.
Questo comportamento è differente da quello di molti progetti di Libero Software che non memorizzano i file derivati, per mantenere più sgombro il deposito Git, rendendo così più facile comprendere quali sono le modifiche sostanziali confrontando differenti versioni, nel tentativo di capire cosa è cambiato tra una modifica e la precedente.
Tuttavia, ci sono parecchie ragioni per le quali il manutentore di
gawk
preferisce mantenere ogni cosa nel deposito Git.
Innanzitutto, perché in questo modo è facile generare completamente ogni data versione, senza doversi preoccupare di avere a disposizione altri strumenti (più vecchi, probabilmente obsoleti, e in qualche caso perfino impossibili da trovare).
Come esempio estremo, se solo si pensa di tentare di compilare, diciamo, la
versione Unix V7 di awk
, ci si accorge che non solo è necessario
scaricare e ricompilare la versione V7 del comando yacc
per farlo, ma
anche che serve la versione V7 del comando lex
. E quest’ultima è
praticamente impossibile farla funzionare in un sistema GNU/Linux dei giorni
nostri.120
(Oppure, diciamo che la versione 1.2 di gawk
richiede il comando
bison
come funzionava nel 1989, e non è presente il file
awkgram.c [generato tramite bison
] nel deposito Git. Che cosa
ci garantisce di riuscire a trovare quella versione di bison
? O che
quella riesca a generarlo?)
Se il deposito Git comprende tutti i file derivati, è facile, dopo averli scaricati, ricostruire il programma. (Oppure è più facile, a seconda di quanto si vuole risalire indietro nel tempo).
E qui arriviamo alla seconda (e più valida) ragione per cui tutti i file
devono essere proprio nel deposito Git. Domandiamoci a chi ci si rivolge:
agli sviluppatori di gawk
, oppure a un utilizzatore che intende
solo scaricare una data versione e provarla?
Il manutentore di gawk
desidera che per tutti gli utenti
awk
interessati sia possibile limitarsi a clonare il deposito sul
proprio computer, selezionare la variante che lo interessa e costruirla. Senza
doversi preoccupare di avere a disposizione le versioni corrette degli Autotool
GNU.121.
A questo serve il file bootstrap.sh. Va a "toccare"
[tramite il comando touch
] vari altri file nell’ordine richiesto
in modo che
# La formula canonica per compilare il software GNU: ./bootstrap.sh && ./configure && make
tutto funzioni senza problemi.
Questo è estremamente importante per i rami
master
e gawk-X.Y-stable
.
Inoltre, il manutentore di gawk
potrebbe sostenere che
ciò è importante anche per gli sviluppatori di gawk
. Tentando di
scaricare il ramo xgawk
122 per
compilarlo, non ci riuscì. (Mancava il file ltmain.sh, ed egli non
aveva idea di come crearlo, e c’erano anche ulteriori problemi.)
La cosa lo lasciò in uno stato di frustrazione estrema. Riguardo a quel
ramo, il manutentore è in una posizione non differente da quella di un utente
generico che voglia tentare di compilare gawk-4.1-stable
o master
dal deposito Git.
Quindi, il manutentore ritiene che sia non solo importante, ma cruciale, che per ogni dato ramo la formula canonica evocata prima funzioni senza problemi.
Una terza ragione per avere tutti i file è che senza di essi, usare ‘git
bisect’ per tentare di trovare quale modifica ha introdotto un errore diventa
estremamente difficile. Il manutentore ha tentato di farlo su un altro
progetto che richiede di eseguire script di inizializzazione allo scopo
di creare lo script configure
e così via; è stata un’esperienza
veramente dolorosa. Se invece il deposito Git contiene tutto il necessario,
usare git bisect
al suo interno è molto facile.
Quali sono, quindi, alcune delle conseguenze e/o delle cose da fare?
bison
e
GNU gettext
.
Installare a partire dal sorgente è abbastanza facile. È il modo con cui il
manutentore ha lavorato per anni (e ancora lavora).
Egli aveva /usr/local/bin all’inizio del suo PATH
e dava i
seguenti comandi:
wget http://ftp.gnu.org/gnu/package/package-x.y.z.tar.gz tar -xpzvf package-x.y.z.tar.gz cd package-x.y.z ./configure && make && make check make install # come utente root
La maggior parte del testo precedente fa parte di messaggi scritti
originalmente dal manutentore agli altri sviluppatori gawk
.
Da uno degli sviluppatori è stata avanzata l’obiezione
“… che chi scarica il sorgente da Git
non è un utente finale”.
Tuttavia, questo non è esatto. Ci sono “utenti avanzati di awk
”
che possono installare gawk
(usando la formula canonica vista sopra)
ma che non conoscono il linguaggio C. Quindi, i rami più rilevanti
dovrebbero essere sempre compilabili.
È stato proposto poi di lanciare ogni notte uno script tramite il
programma di utilità cron
per creare archivi in formato tar
contenenti tutto “il codice sorgente.” Il problema in questo caso è che
ci sono differenti alberi di sorgenti, che corrispondono ai vari rami!
Quindi gli archivi notturni in questione non sono una risposta valida, anche
per il fatto che il deposito Git può rimanere senza alcuna modifica
significativa per settimane intere.
Fortunatamente, il server Git può rispondere a questa esigenza. Per ogni dato ramo chiamato nome-ramo, basta usare:
wget http://git.savannah.gnu.org/cgit/gawk.git/snapshot/gawk-nome-ramo.tar.gz
per ottenere una copia utilizzabile del ramo in questione.
Successivo: Limitazioni dell'implementazione, Precedente: Aggiunte, Su: Note [Contenuti][Indice]
AWK è un linguaggio simile a PERL, solo che è notevolmente più elegante.
Hey!
Il file TODO nel ramo master
del deposito Git di gawk
contiene un elenco di possibili futuri miglioramenti. Alcuni di questi
riguardano il codice sorgente, e altri possibili nuove funzionalità.
Consultare quel file per esaminare la lista.
Vedi Aggiunte,
se si è interessati a intraprendere qualcuno dei progetti colà elencati.
Successivo: Progetto delle estensioni, Precedente: Future estensioni, Su: Note [Contenuti][Indice]
La tabella seguente specifica i limiti di gawk
in un sistema di
tipo Unix (sebbene anche tra questi ci potrebbero essere variazioni). Altri
sistemi operativi possono avere limiti differenti.
Caratteristica | Limiti |
---|---|
Caratteri in una classe di caratteri | 2^(numero di bit per byte) |
Lunghezza di un record in input | MAX_INT |
Lunghezza di un record in output | Illimitata |
Lunghezza di una riga di codice sorgente | Illimitata |
Numero di campi in un record | MAX_LONG |
Numero di ridirezioni di file | Illimitata |
Numero di record in input in un singolo file | MAX_LONG |
Numero totale di record in input | MAX_LONG |
Numero di ridirezioni via pipe | min(numero processi per utente, numero di file aperti) |
Valori numerici | Numeri a virgola mobile in doppia precisione (se non si usa la funzionalità MPFR) |
Dimensione di un campo | MAX_INT |
Dimensione di una stringa letterale | MAX_INT |
Dimensione di una stringa di printf | MAX_INT |
Successivo: Meccanismo delle vecchie estensioni, Precedente: Limitazioni dell'implementazione, Su: Note [Contenuti][Indice]
Questa sezione documenta l’architettura dell’estensione API, inclusa una trattazione sommaria della progettazione e dei problemi che andavano risolti.
La prima versione delle estensioni per gawk
è stata sviluppata
a metà degli anni ’90, e distribuita con la versione 3.1 di gawk
,
verso la fine degli anni ’90.
Il meccanismo e l’architettura sono rimasti gli stessi per quasi 15 anni,
fino al 2012.
Il vecchio meccanismo delle estensioni usava tipi di dati e funzioni dello
stesso gawk
, con un “abile trucco” per installare le funzioni
di estensione.
La distribuzione gawk
conteneva alcuni esempi di estensioni, solo
poche delle quali erano realmente utili. Tuttavia era chiaro fin da
principio che il meccanismo di estensione era un’aggiunta improvvisata, e
non era realmente ben concepito.
• Problemi con le vecchie estensioni: | Problemi col vecchio meccanismo. | |
• Obiettivi delle estensioni: | Obiettivi del nuovo meccanismo. | |
• Altre scelte progettuali per le estensioni: | Qualche altra scelta progettuale. | |
• Futuri sviluppi delle estensioni: | Possibilità di crescita futura. |
Successivo: Obiettivi delle estensioni, Su: Progetto delle estensioni [Contenuti][Indice]
Il vecchio meccanismo delle estensioni presentava parecchi problemi:
gawk
. Ogni volta
che la struttura NODE
123 veniva modificata, ogni estensione doveva
essere ricompilata. Inoltre, la scrittura di estensioni richiedeva una
certa familiarità con le funzioni interne di gawk
. Esisteva
un po’ di documentazione in questo Documento, ma era ridotta al minimo.
gawk
da un’estensione era necessario
disporre di funzionalità del linker
normalmente disponibili in ambiente di tipo Unix, ma non implementate
nei sistemi MS-Windows; chi voleva utilizzare estensioni in
MS-Windows doveva aggiungerle al modulo eseguibile di gawk
,
anche se MS-Windows supporta il caricamento dinamico di oggetti condivisi.
gawk
; nessuna compatibilità tra le versioni è stata mai prevista o
resa disponibile.
Nonostante questi inconvenienti, gli sviluppatori del progetto xgawk
si basarono su gawk
per sviluppare parecchie estensioni
significative. Inoltre, migliorarono le capacità, in gawk
, di
includere file e di accedere a oggetti condivisi.
Una nuova API è rimasta un desiderio per lungo tempo, ma solo nel 2012
il manutentore di gawk
e gli sviluppatori di xgawk
iniziarono finalmente a lavorare insieme. Ulteriori informazioni riguardanti
il progetto xgawk
sono forniti nella gawkextlib.
Successivo: Altre scelte progettuali per le estensioni, Precedente: Problemi con le vecchie estensioni, Su: Progetto delle estensioni [Contenuti][Indice]
Alcuni obiettivi per la nuova API sono:
gawk
.
Le modifiche alla struttura interna di gawk
dovrebbero essere
irrilevanti per chi scrive una funzione di estensione.
gawk
, se la stessa API rimane invariata.
awk
. Questo vuol dire che le
estensioni dovrebbero avere:
gawk
).
Alcuni ulteriori obiettivi rilevanti sono:
gawk
è un
programma scritto in C. Al momento in cui queste note sono scritte, la cosa
non è stata verificata).
gawk
124 da parte del
linker statico, usato in fase di compilazione, o di quello dinamico,
in modo da rendere possibile la creazione di estensioni che funzionino anche
in ambiente MS-Windows.
In fase di sviluppo, è apparso evidente che dovevano essere disponibili alle estensioni anche altre funzionalità, che sono state successivamente implementate:
gawk
. In particolare, gli sviluppatori di
xgawk
hanno programmato un cosiddetto “gancio aperto” (open
hook) per gestire autonomamente la lettura dei record. In fase di sviluppo,
questo meccanismo è stato generalizzato, per consentire alle estensioni di
agganciarsi sia all’elaborazione dell’input, che a quella dell’output, nonché
all’I/O bidirezionale.
gawk
.
gawk
possa informare anche sulle versioni delle estensioni.
Il vincolo di evitare di accedere ai simboli di gawk
può parere a
prima vista piuttosto difficile da rispettare.
Un tipo di architettura, apparentemente usato da Perl e Ruby e forse da altri
programmi, potrebbe consistere nel mettere il codice principale di
gawk
in una libreria, limitando il programma di utilità
gawk
a una piccola funzione main()
in C che richiamerebbe
dinamicamente la libreria.
Questo inverte i ruoli del programma principale e della sua estensione, e
complica sia la compilazione che l’installazione, trasformando la semplice
copia del programma eseguibile gawk
da un sistema all’altro (o da una
posizione all’altra all’interno dello stesso sistema) in un’operazione ad alto
rischio.
Pat Rankin ha suggerito la soluzione che è stata adottata. Vedi Panoramica sul meccanismo delle estensioni, per maggiori dettagli.
Successivo: Futuri sviluppi delle estensioni, Precedente: Obiettivi delle estensioni, Su: Progetto delle estensioni [Contenuti][Indice]
Per una scelta progettuale arbitraria, le estensioni possono accedere ai valori
delle variabili e dei vettori predefiniti (come ARGV
e FS
), ma
non possono modificarli, con la sola eccezione di PROCINFO
.
Il motivo di questa scelta è di impedire a una funzione di estensione di
alterare il flusso di un programma awk
togliendogli il controllo.
Mentre una vera funzione di awk
può fare quel che vuole, a
discrezione del programmatore, una funzione di estensione dovrebbe fornire un
servizio, o rendere disponibile un’API C da utilizzare all’interno di
awk
, senza interferire con le variabili FS
o ARGC
e
ARGV
.
Inoltre, diverrebbe facile avviarsi su un sentiero scivoloso. Quante
funzionalità di gawk
dovrebbero essere disponibili alle estensioni?
Devono poter usare getline
? Oppure richiamare gsub()
o compilare
espressioni regolari? Oppure richiamare funzioni interne di awk
?
(Questo potrebbe creare molta confusione.)
Per evitare questi problemi, gli sviluppatori di gawk
hanno scelto di
iniziare con le funzionalità più semplici e di base, che sono comunque
veramente utili.
Sebbene gawk
consenta cose interessanti come l’MPFR,
e vettori indicizzati internamente con numeri interi,
un’altra decisione è stata quella di non rendere disponibili all’API queste
funzionalità, per amor di semplicità e per restare fedeli alla tradizionale
semantica di awk
. (In effetti, i vettori indicizzati internamente
con numeri interi sono talmente trasparenti che non sono neppure documentati!)
In più, tutte le funzioni nell’API controllano che i puntatori ai parametri
passati loro in input non siano NULL
. Se lo sono, viene emesso un
messaggio di errore. (È bene che il codice di estensione verifichi
che i puntatori ricevuti da gawk
non siano NULL
. Ciò non
dovrebbe succedere, ma gli sviluppatori di gawk
sono solo degli
esseri umani, e capita anche a loro di commettere degli errori, di tanto in
tanto.)
Col tempo, l’API si svilupperà certamente; gli sviluppatori di gawk
si aspettano che questo avvenga in base alle necessità degli utenti. Per ora,
l’API disponbile sembra fornire un insieme di funzionalità minimo, eppure
potente, per creare estensioni.
Precedente: Altre scelte progettuali per le estensioni, Su: Progetto delle estensioni [Contenuti][Indice]
L’API può ancora essere ampliata, in due modi:
gawk
passa un “identificativo di estensione” all’estensione in fase
di caricamente dell’estensione. L’estensione a sua volta restituisce questo
identificativo a gawk
a ogni chiamata di funzione. Questo meccanismo
consente a gawk
di identificare l’estensione che lo chiama, se la
cosa dovesse risultare necessaria.
gawk
in fase di registrazione di ogni funzione estesa. Questo è fatto in vista di
un possibile futuro meccanismo per raggruppare funzioni di estensione, e per
evitare in questo modo possibili conflitti nei nomi di funzione.
Naturalmente, al momento in cui queste righe sono state scritte, nessuna decisione è stata presa riguardo ai punti sopra descritti.
Successivo: Sommario delle note, Precedente: Progetto delle estensioni, Su: Note [Contenuti][Indice]
Estensioni dinamiche, descrive le API supportate e i meccanismi
per scrivere estensioni per gawk
. Quest’API è stata introdotta
nella versione 4.1. Peraltro, già da molti anni gawk
metteva a disposizione un meccanismo di estensione che richiedeva una
familiarità con la struttura interna di gawk
e che non era stato
progettato altrettanto bene.
Per garantire un periodo di transizione, gawk
versione 4.1
continua a supportare il meccanismo di estensione originale.
Questo rimarrà disponibile per la durata di una sola versione principale.
Il supporto cesserà, e sarà rimosso dal codice sorgente, al rilascio
della prossima versione principale.
In breve, le estensioni in stile originale dovrebbero essere compilate includendo il file di intestazione awk.h nel codice sorgente dell’estensione. Inoltre, va definito l’identificativo ‘GAWK’ durante la preparazione (si usi ‘-DGAWK’ con compilatori in stile Unix). Se non lo si fa, le definizioni in gawkapi.h risulteranno in conflitto con quelle in awk.h e l’estensione non sarà compilabile.
Come nelle versioni precedenti, un’estensione vecchio stile sarà caricata
usando la funzione predefinita extension()
(che non viene ulteriormente
documentata). Questa funzione, a sua volta, trova e carica il file oggetto
condiviso che contiene l’estensione e chiama la sua routine C dl_load()
.
Poiché le estensioni in stile originale e quelle nello stile nuovo usano
differenti routine di inizializzazione(dl_load()
e dlload()
,
rispettivamente), esse possono tranquillamente essere installate nella stessa
directory (il cui nome deve essere contenuto nella variabile AWKLIBPATH
)
senza problemi di conflitti.
Il team di sviluppo di gawk
raccomanda caldamente di convertire
ogni estensione del vecchio tipo ancora in uso, in modo da utilizzare la nuova
API descritta
in
Estensioni dinamiche.
Precedente: Meccanismo delle vecchie estensioni, Su: Note [Contenuti][Indice]
gawk
possono essere disabilitata sia con
l’opzione --traditional che con l’opzione --posix.
L’opzione --parsedebug è disponibile se gawk
è stato
compilato con ‘-DDEBUG’.
gawk
è conservato in un deposito Git
pubblicamente accessibile. Tutti possono scaricarlo e visualizzare il
codice sorgente.
gawk
sono benvenuti. Seguire le istruzioni
delineate in questo capitolo renderà più agevole integrare
i contributi degli utenti nel codice principale.
Questo vale sia per il contributo di nuove funzionalità che per il
porting a ulteriori sistemi operativi.
gawk
ha alcuni limiti: generalmente quelli imposti
dall’architettura hardware della macchina.
xgawk
, e a fornire una
compatibilità binaria in futuro.
gawk
, ma sarà rimosso nella prossima versione principale.
Quest’appendice si propone di definire alcuni dei concetti
e termini fondamentali usati nel resto di questo Documento.
Poiché questo Documento è dedicato ad awk
,
e non riguarda la programmazione al computer in generale, la trattazione
è necessariamente piuttosto generica e semplificata.
(Se serve qualcosa di più approfondito, ci sono molti
altri testi introduttivi che si possono consultare.)
• Fondamenti ad alto livello: | Una visione dall’alto. | |
• Fondamenti sui tipi di dati: | Una velocissima introduzione ai tipi di dati. |
Successivo: Fondamenti sui tipi di dati, Su: Concetti fondamentali [Contenuti][Indice]
Al livello più fondamentale, il compito di un programma è di elaborare alcuni dati in input e produrre dei risultati. Si veda la Figura D.1.
Il “programma” nella figura può essere sia un programma
compilato125
(come ls
),
sia un programma interpretato. In quest’ultimo caso, un programma
direttamente eseguibile dal computer, come awk
, legge il
programma, e quindi usa le istruzioni in esso contenute per elaborare i dati.
Quando si scrive un programma, esso è composto normalmente dai seguenti insiemi di istruzioni di base, come si vede nella Figura D.2:
Queste sono le cose da fare prima di iniziare la reale elaborazione dei
dati, per esempio controllare gli argomenti con cui è stato invocato il
programma, inizializzare dei dati di cui si potrà aver bisogno per la
successiva elaborazione, e così via.
Questa fase corrisponde alla regola BEGIN
di awk
(vedi BEGIN/END).
Nella preparazione di una torta, questa fase corrisponde a quella di tirar fuori tutti i contenitori in cui mischiare gli ingredienti, e la teglia in cui cuocerla, e nell’assicurarsi di avere a disposizione tutti gli ingredienti necessari.
Questa fase è quella in cui si svolge il lavoro vero e proprio. Il programma legge i dati, raggruppati in “pezzi logici”, e li elabora secondo necessità.
In quasi tutti i linguaggi di programmazione, la lettura dei dati va gestita
manualmente, controllando dopo ogni lettura se è
rimasto ancora qualcosa d’altro da leggere. Il paradigma criterio di
ricerca–azione di awk
(vedi Per iniziare)
gestisce automaticamente la parte di lettura dati.
Nella preparazione di una torta, l’elaborazione corrisponde all’attività vera e propria: rompere le uova, mescolare la farina, l’acqua e gli altri ingredienti, e quindi mettere la torta a cuocere nel forno.
Una volta elaborati tutti i dati, ci sono attività da svolgere prima di aver
finito. Questa fase corrisponde alla regola END
di awk
.
(vedi BEGIN/END).
Dopo che la torta è stata tirata fuori dal forno, va fatta raffreddare e avvolta in una pellicola trasparente per evitare che qualcuno la assaggi, e inoltre vanno lavati i contenitori e le posate.
Un algoritmo è la descrizione dettagliata della procedura necessaria per svolgere un compito o per elaborare dati. Lo si può paragonare alla ricetta per preparare una torta. I programmi sono il modo con cui un algoritmo viene eseguito da un computer. Spesso è compito del programmatore sia sviluppare un algoritmo, sia programmarlo.
I “pezzi logici” nominati precedentemente sono chiamati record (registrazioni), in analogia con le registrazioni del personale di una ditta, degli studenti di una scuola, o dei pazienti di un dottore. Ogni record è composto di molte parti, per esempio nome, cognome, data di nascita, indirizzo, e così via. Le parti di cui è composto un record sono chiamate campi del record.
L’atto di leggere i dati è noto come input, e quello di generare risultati è, come facilmente prevedibile, chiamato output. Spesso i due sono riuniti sotto il nome di “input/output” e, ancor più spesso, con l’abbreviazione “I/O”. (In inglese “input” e “output” sono spesso usati come verbi, nel gergo informatico, al posto di leggere e scrivere.)
awk
gestisce la lettura dei dati, come anche la divisione in
record e campi. Lo scopo del programma dell’utente è di dire ad awk
cosa fare con i dati. Questo vien fatto descrivendo modelli da
ricercare nei dati e azioni da eseguire qualora si siano trovati questi
modelli. Questa caratteristica dei programmi awk
, di essere
guidati-dai-dati, di solito li rende più facili sia da scrivere che da
leggere.
Precedente: Fondamenti ad alto livello, Su: Concetti fondamentali [Contenuti][Indice]
In un programma si tiene traccia di informazioni e valori in contenitori
chiamati variabili. Una variabile è solo un nome per designare un certo
valore, come nome
, cognome
, indirizzo
, e così via.
awk
ha molte variabili predefinite, e ha dei nomi speciali per
designare il record in input corrente e i campi che compongono il record
stesso. Si possono inoltre raggruppare molti valori associati tra di loro
sotto un unico nome, utilizzando un vettore.
I dati, in particolare in awk
, possono avere valori numerici, come 42
o 3.1415927, o avere come valore delle stringhe. Un valore di tipo stringa è
essenzialmente qualsiasi cosa che non sia un numero, per esempio un nome. Le
stringhe sono talora chiamate dati di tipo carattere, poiché memorizzano
i singoli caratteri che le formano. Le singole variabili, come pure le
variabili numeriche e di tipo stringa, sono definite come valori
scalari. Raggruppamenti di valori, come i vettori, non sono scalari.
Aritmetica del computer, ha fornito un’introduzione di base ai tipi numerici (interi e a virgola mobile) e a come questi sono usati in un computer. Si consiglia di rileggere quelle informazioni, comprese le numerose avvertente là esposte.
Mentre è probabile che ci si sia abituati all’idea di un numero senza un valore
(cioè, allo zero), richiede un po’ più di riflessione abituarsi all’idea di
dati di tipo carattere a lunghezza zero. Nonostante ciò, questo tipo di dato
esiste. È chiamato stringa nulla. La stringa nulla è un dato di tipo
carattere che non ha un valore. In altre parole, è vuoto. Si scrive così nei
programmi awk
: ""
.
Gli esseri umani sono abituati a usare il sistema decimale, cioè a base 10. In base 10, i numeri vanno da 0 a 9, e poi “vengono riportati” nella colonna successiva. (Chi si ricorda la scuola elementare? 42 = 4 x 10 + 2.)
Ma esistono anche altre basi per i numeri. I computer normalmente usano la base 2 o binaria, la base 8 o ottale, e la base 16 o esadecimale. Nella numerazione binaria, ogni colonna rappresenta il doppio del valore della colonna alla sua destra. Ogni colonna può contenere solo uno 0 o un 1. Quindi, il numero binario 1010 rappresenta (1 x 8) + (0 x 4) + (1 x 2) + (0 x 1), ossia il numero decimale 10. Le numerazioni ottale ed esadecimale sono trattate più ampiamente in Numeri non-decimali.
Al livello più basso possibile, i computer memorizzano i valori come gruppi di
cifre binarie, o bit. I computer moderni raggruppano i bit in gruppi di
otto, detti byte. Applicazioni avanzate talora hanno necessità di
manipolare i bit direttamente, e gawk
è dotato di apposite funzioni.
I programmi sono scritti nei linguaggi di programmazione. Esistono centinaia,
se non migliaia, di linguaggi di programmazione. Uno dei più diffusi è il
linguaggio di programmazione C. Il linguaggio C ha esercitato un’influsso
molto forte nella progettazione del linguaggio awk
.
Ci sono state parecchie versioni di C. La prima è spesso designata come
“K&R” C, dalle iniziali di Brian Kernighan e Dennis Ritchie,
gli autori del primo libro sul C. (Dennis Ritchie ha creato il linguaggio,
e Brian Kernighan è stato uno dei creatori di awk
.)
A metà degli anni ’80 è iniziato uno sforzo rivolto a produrre uno
standard internazionale per il C. Questo lavoro ha raggiunto un punto di
arrivo nel 1989 con la produzione dello standard ANSI per il C.
Questo standard è diventato uno standard ISO nel 1990.
Nel 1999, uno standard ISO C revisionato è stato approvato e pubblicato.
Dove è opportuno, POSIX awk
è compatible con lo standard
ISO C del 1999.
Successivo: Copia, Precedente: Concetti fondamentali, Su: Top [Contenuti][Indice]
La situazione in cui due processi che comunicano tra loro sono entrambi bloccati, in attesa che l’altro processo faccia qualcosa.
Un linguaggio di programmazione originalmente definito dal Department of Defense U.S.A. per la programmazione integrata. È stato progettato per favorire dei buoni metodi da seguire nell’ingegneria del software.
Si veda “Variabili d’ambiente”.
I metacaratteri regexp ‘^’ e ‘$’, che richiedono che la corrispondenza che si sta cercando si trovi all’inizio o alla fine di una stringa, rispettivamente.
Un’area del linguaggio le cui specifiche spesso non erano (o ancora non sono) chiare, col risultato di ottenere un comportamente inatteso o non desiderabile. Tali aree sono segnalate in questo Documento con “(a.b.)” nel testo e sono riportate nell’indice analitico sotto la voce “angolo buio”.
L’American National Standards Institute. Questo ente produce parecchi standard, e tra questi gli standard per i linguaggi di programmazione C e C++. Questi standard spesso diventano anche internazionali. Si veda anche “ISO”.
Un argomento può essere due cose differenti. Può essere un’opzione o un
nome-file passato a un comando mentre lo si invoca dalla riga dei comandi,
oppure può essere qualcosa passato a una funzione all’interno di un
programma, per esempio all’interno di awk
.
In quest’ultimo caso, un argomento può essere passato a una funzione in
due modi. Nel primo modo è passato come valore alla funzione chiamata,
ossia una copia del valore della variabile è reso disponibile alla funzione
chiamata, ma la variabile originale non può essere modificata dalla
funzione stessa. Nel secondo modo l’argomento è passato per riferimento,
ossia un puntatore alla variabile in questione è passato alla funzione, che
può quindi modificarla direttamente. In awk
le variabili scalari
sono passate per valore, e i vettori sono passati per riferimento.
Si veda “Passaggio per valore/riferimento”.
Arrotondare il risultato di un’operazione aritmetica può essere difficile.
C’è più di un modo di arrotondare, e in gawk
è possibile scegliere
quale metodo dovrebbe essere usato all’interno di un programma.
Vedi Impostare modi di arrotondare.
Un’espressione awk
che cambia il valore di qualche variabile o
dato oggetto di awk
. Un oggetto a cui si può assegnare un valore
è detto un lvalue. I valori
assegnati sono chiamati rvalue.
Vedi Operatori di assegnamento.
awk
Henry Spencer dell’Università di Toronto ha scritto un assembler adatto a
molti diversi hardware, usando solo script sed
e
awk
. È lungo migliaia di righe, e include
la descrizione dell’hardware di
numerosi micro-computer a 8 bit. È un
buon esempio di programma per cui sarebbe stato
meglio utilizzare un altro linguaggio.
Si può scaricare da http://awk.info/?awk100/aaa.
Un’istruzione in un programma che afferma che una condizione è verificata in un dato punto di un programma. Utile per ragionare su come si suppone funzioni un programma.
Una serie di istruzioni awk
associate a una regola. Se
l’espressione di ricerca della regola individua un record in input,
awk
esegue su quel record l’azione relativa. Le azioni sono
sempre racchiuse tra parentesi graffe.
(Vedi Panoramica sulle azioni).
La versione GNU della shell standard (il Bourne-Again SHell). Si veda anche “Bourne Shell”.
Notazione a base due, che usa le cifre 0
–1
. Poiché
i circuiti elettronici funzionano “naturalmente” in base 2
(basta pensare a Off/On), ogni cosa all’interno di un computer è
calcolata usando la base 2. Ciascuna cifra rappresenta la presenza
(o l’assenza) di una potenza di 2 ed è chiamata un bit.
Così, per esempio, il numero in base due 10101
rappresenta il
numero in base decimale 21, ((1 x 16) + (1 x 4) + (1 x 1)).
Poiché i numeri in base due diventano rapidamente molto lunghi sia da leggere che da scrivere, normalmente li si unisce a gruppi di tre (ossia, sono visti come numeri ottali) o a gruppi di quattro (ossia, sono visti come numeri esadecimali). Non c’è un modo diretto per inserire numeri a base due in un programma C. Se necessario, tali numeri vengono solitamente inseriti come numeri ottali o esadecimali. Il numero di cifre in base due contenuto nei registri usati per rappresentare i numeri interi all’interno dei computer è un’indicazione approssimativa della potenza di calcolo del computer stesso. La maggior parte dei computer oggi usa 64 bit per rappresentare i numeri interi nei registri di calcolo, ma registri a 32 bit, 16 bit e 8 bit sono stati largamente in uso in passato. Vedi Numeri non-decimali.
Una particolare perla di saggezza, segno, detto o ricordo prodotto da (o presentato a) un programma. (Con vivi ringraziamenti al Prof. Doug McIlroy).
Abbreviazione di “Binary Digit” [cifra binaria].
Tutti i valori nella memoria di un computer sono rappresentati nella forma di
cifre binarie: valori che sono zero o uno.
Gruppi di bit possono essere interpretati differentemente—come numeri
interi, numeri a virgola mobile, dati di tipo carattere, indirizzi di altri
oggetti contenuti in memoria, o altri dati ancora.
awk
permette di lavorare con numeri a virgola mobile e stringhe.
gawk
permette di manipolare bit con le funzioni predefinite
descritte
in
Funzioni a livello di bit.
I computer sono spesso definiti dal numero di bit che usano per rappresentare valori interi. Molti sistemi sono a 32-bit, ma i sistemi a 64-bit sono sempre più numerosi, mentre i sistemi a 16-bit [e quelli a 8-bit] sono praticamente scomparsi.
La shell standard (/bin/sh) in Unix e nei sistemi derivati da Unix,
Originariamente scritto da Steven R. Bourne dei Bell Laboratories.
Molte shell (Bash, ksh
, pdksh
, zsh
) sono
generalmente compatibili con la Bourne shell, anche quando offrono ulteriori
funzionalità.
Il linguaggio di programmazione di sistema con cui è scritta la maggior parte
del software GNU. Il linguaggio di programmazione awk
ha una
sintassi simile a quella del C, e
questo Documento puntualizza, quando serve, le somiglianze esistenti
fra awk
e C.
In generale, gawk
tenta di essere ragionevolmente simile alla
versione 1990 del C ISO.
La C Shell (csh
o la sua versione migliorata tcsh
) è una
shell Unix creata da Bill Joy verso la fine degli anni ’70. La C shell si
differenzia dalla altre shell per le sue funzionalità interattive, e per lo
stile complessivo, che è abbastanza simile a quello del linguaggio C.
La C shell non è compatibile all’indietro con la Bourne Shell, e per questo
motivo un’attenzione speciale è necessaria se si convertono alla C shell
degli script scritti per altre shell Unix, in particolare per ciò che
concerne la gestione delle variaili di shell.
Si veda anche “Bourne Shell”.
Un linguaggio di programmazione molto diffuso, orientato agli oggetti, derivato dal C.
Quando awk
legge un record in input, suddivide il record in parti
separate da spazi vuoti (o da una regexp che individua il separatore,
modificabile reimpostando la variabile predefinita FS
). Tali parti
sono dette campi. Se le parti sono di lunghezza fissa, si può usare la
variabile predefinita FIELDWIDTHS
per descriverne le lunghezze.
Se si desidera specificare i contenuti dei campi, piuttosto che il separatore
fra i campi, si può usare la variabile predefinita FPAT
per farlo.
(Vedi Separatori di campo,
Dimensione costante,
e
Separazione in base al contenuto).
L’insieme di codici numerici usati da un computer per rappresentare i caratteri (lettere, numeri, segni d’interpunzione, etc.) di un particolare paese o località. L’insieme di caratteri più comunemente in uso oggi è l’ASCII (American Standard Code for Information Interchange). Molti paesi europei usano un’estensione dell’ASCII nota come ISO-8859-1 (ISO Latin-1). L’insieme di caratteri Unicode sta guadagnando popolarità e affermandosi come standard, e il suo uso è particolarmente esteso nei sistemi GNU/Linux.
Un preprocessore per pic
che legge descrizioni di molecole
e produce l’input a pic
che serve a disegnarle.
È stato scritto in awk
da Brian Kernighan e Jon Bentley, ed è disponibile in
http://netlib.org/typesetting/chem.
Si veda “Espressione tra parentesi quadre”.
Un programma che traduce codici sorgente scritti in qualche linguaggio in codici eseguibili su un particolare computer. Il codice oggetto risultante può quindi essere eseguito direttamente dal computer. Si veda anche “Interprete”.
Concatenare due stringhe significa unirle, producendo una nuova stringa. Per esempio, la stringa ‘pippo’ concatenata con la stringa ‘pluto’ produce la stringa ‘pippopluto’. (Vedi Concatenazione).
Un meccanismo interno di gawk
per minimizzare la quantità di
memoria necessaria per contenere il valore delle variabili di tipo
stringa. Se il valore assunto da una variabile è usato in più di un
posto nel programma, solo una copia del valore stesso è tenuta in
memoria, e il contatore di riferimenti ad esso associato è aumentato di
uno quando lo stesso valore è usato da un’ulteriore variabile, e diminuito
di uno quando la variabile relativa non è più utilizzata. Quando il
contatore di riferimenti va a zero, la parte di memoria utilizzata per
contenere il valore della variuabile è liberato.
Un programma subordinato con il quale è possibile una comunicazione bidirezionale dal programma principale.
Sono costituiti da numeri e stringhe di caratteri. I numeri sono convertiti in stringhe e viceversa, a seconda delle necessità. (Vedi Conversione).
Un programma che serve agli sviluppatori per rimuovere “bug” (de-bug) dai loro programmi.
Un nome unico che identifica un’applicazione. Usato per raggruppare messaggi che sono tradotti in fase di esecuzione nel linguaggio locale.
Una rappresentazione di numeri all’interno del computer che ha una parte
espressa sotto forma di frazione. I numeri a doppia precisione hanno più
cifre decimali che quelli a singola precisione, ma le operazioni che la
usano consumano più risorse di quelle
eseguite in singola precisione. La doppia precisione è il formato con cui
awk
memorizza i valori numerici. Nel linguaggio C è il tipo di
dati detto double
.
Un programma che legge record da un flusso in input e li elabora uno o più alla volta. Questo è diverso da quel che farebbe un programma batch il quale potrebbe leggere completamente i file in input, prima di iniziare a fare alcunché, ed è diverso anche da un programma interattivo, che richiede input dall’utente [tipicamente, una riga alla volta].
Un effetto collaterale ha luogo quando un’espressione ha un effetto ulteriore, invece di produrre solo un valore. Espressioni di assegnamento, incremento e decremento, e invocazioni di funzioni hanno effetti collaterali. (Vedi Operatori di assegnamento).
la data usata come “inizio del tempo” per i campi che contengono date. I valori del tempo nella maggior parte dei dei sistemi sono rappresentati in numero di secondi trascorsi dall’Epoca, con funzioni di libreria che consentono di convertire tali valori nei formati normali di data e ora.
L’Epoca nei sistemi Unix e POSIX parte dal primo gennaio 1970 alle ore 00:00:00 UTC. Si veda anche “GMT” e “UTC”.
Notazione per l’aritmetica in base 16, che usa le cifre 0
–9
e
le lettere A
–F
, con ‘A’
che rappresenta 10, ‘B’ che rappresenta 11, e così via, fino a
‘F’ per 15.
I numeri esadecimali sono scritti in C prefissandoli con ‘0x’,
per indicarne la base. Quindi, 0x12
è 18 ((1 x 16) + 2).
Vedi Numeri non-decimali.
Così detta dal nome del matematico inglese George Boole. Si veda anche “Espressione logica”.
Un’espressione che usa l’operatore ternario ‘?:’, come p.es. ‘expr1 ? expr2 : expr3’. Dell’espressione expr1 viene calcolato il valore; se risulta verificata, il valore dell’intera espressione diviene quello di expr2; altrimenti il valore è quello di expr3. In ogni caso, solo una delle due espressioni expr2 e expr3 viene calcolata. (Vedi Espressioni condizionali).
Una relazione che è vera o falsa, del tipo di ‘a < b’.
Espressioni di confronto sono usate nelle istruzioni
if
, while
, do
, for
e nelle espressioni di ricerca per scegliere quale record in input elaborare.
(Vedi Tipi di variabile e confronti).
Una parte di un’espressione regolare che permette di specificare
corrispondenze multiple di qualche parte della regexp. Le espressioni di
intervallo non erano originariamente ammesse nei programmi awk
.
Le espressioni di ricerca individuano per awk
a quali record in
input sono applicabili determinate
regole.
Un’espressione di ricerca [pattern] è un’espressione condizionale specifica che viene confrontata con ogni record in input. Se la corrispondenza esiste, si dice che il modello individua il record in input. Una tipica espressione di ricerca potrebbe confrontare il record in input con un’espressione regolare. (Vedi Panoramica sui criteri di ricerca).
Un’espressione che usa gli operatori logici AND, OR e NOT,
scritti come ‘&&’, ‘||’, e ‘!’ in awk
.
Spesso chiamate espressioni booleane, dal nome del matematico che per primo
ha sistematizzato questo tipo di logica matematica.
un’espressione regolare (abbreviabile come “regexp”) è un modello che
descrive un assieme di stringhe, potenzialmente illimitato. Per esempio
l’espressione regolare
‘R.*xp’ corrisponde a qualsiasi stringa che inizia con la lettera
‘R’ e termina con le lettere ‘xp’. In awk
, le espressioni
regolari sono usate nei modelli [pattern] e nelle espressioni condizionali.
Le espressioni regolari possono contenere sequenze di protezione.
(Vedi Espressioni regolari).
Si veda “Espressioni regolari dinamiche”.
Un’espressione regolare costante è un’espressione regolare scritta tra barre,
come /pippo/
. A una tale espressione viene assegnato un valore quando
si scrive un programma awk
e non può essere modificata in fase di
esecuzione del programma. (Vedi Uso di regexp.)
Un’espressione regolare dinamica è un’espressione regolare scritta come
un’espressione normale. Potrebbe essere una costante stringa, come
"pippo"
, ma potrebbe anche essere un’espressione il cui valore è variabile
(Vedi Espressioni regolari calcolate).
All’interno di una espressione regolare, un’espressione racchiusa fra parentesi quadre sta a indicare che un singolo carattere appartiene a una specifica classe di caratteri. Un’espressione tra parentesi quadre può contenere una lista di uno o più caratteri, come ‘[abc]’, un intervallo di caratteri, come ‘[A-Z]’, o un nome, delimitato da ‘:’, che designa un insieme di caratteri conosciuto, come ‘[:digit:]’. La forma di espressione tra parentesi quadre racchiusa tra ‘:’ è indipendente dalla rappresentazione binaria dei caratteri stessi, che potrebbe utilizzare le codifiche ASCII, EBCDIC, o Unicode, a seconda dell’architettura del computer, e della localizzazione. Si veda anche “Espressioni regolari”.
La negazione di una espressione tra parentesi quadre. Tutto ciò che non è descritto da una data espressione tra parentesi quadre. Il simbolo ‘^’ precede l’espressione tra parentesi quadre che viene negata. Per esempio: ‘[[^:digit:]’ designa qualsiasi carattere che non sia una cifra. ‘[^bad]’ designa qualsiasi carattere che non sia una delle lettere ‘b’, ‘a’, o ‘d’. Si veda “Espressione tra parentesi quadre”.
Una funzionalità aggiunta o una modifica a un linguaggio di programmazione
o a un programma di utilità, non definita dallo standard di quel linguaggio
o di quel programma di utilità.
gawk
ha molte estensioni rispetto al POSIX awk
(fin
troppe).
Free Documentation License. Si veda “Licenza Documentazione Libera”.
Un nome-file interpretato internamente da gawk
, invece che
gestito direttamente dal sistema operativo in cui viene eseguito
gawk
—per esempio, /dev/stderr.
(Vedi File speciali).
Una variabile [di tipo booleano] che, se verificata, indica la presenza o l’assenza di qualche condizione.
Le stringhe di formato controllano il modo in cui le funzioni
strftime()
, sprintf()
e l’istruzione printf
visualizzano
l’output che producono. Inoltre, le conversioni da numeri a stringhe sono
controllate dalle stringhe di formato contenute nelle variabili predefinite
CONVFMT
e OFMT
. (Vedi Lettere di controllo).
awf
)Henry Spencer all’Università di Toronto ha scritto un formattatore che
accetta un ampio sottoassieme dei comandi di formattazione ‘nroff -ms’
e ‘nroff -man’ usando
awk
e sh
.
Si può scaricare da http://awk.info/?tools/awf.
Abbreviazione di FORmula TRANslator (traduttore di formule), è uno dei primi linguaggi di programmazione, pensato per il calcolo scientifico. È stato ideato da John Backus ed è disponibile a partire dal 1957. È ancora in uso ai giorni nostri.
Un’organizzazione senza fini di lucro dedicata alla produzione e distribuzione di software liberamente distribuibile. È stata fondata da Richard M. Stallman, l’autore dell’originale editor Emacs. GNU Emacs è la versione di Emacs maggiormente usata oggigiorno.
Si veda “Free Software Foundation”.
Una parte di un programma awk
che si può chiamare da qualsiasi
punto del programma, per eseguire un compito. awk
ha parecchie
funzioni predefinite.
Gli utenti possono definire essi stessi delle funzioni in qualsiasi parte
del programma. Le funzioni possono essere ricorsive, ossia possono
chiamare se stesse.
Vedi Funzioni.
In gawk
è anche possibile avere funzioni condivise tra diversi
programmi, incluse secondo necessità usando la direttiva
@include
(vedi Includere file).
In gawk
il nome della funzione da chiamare può essere generato
in fase di esecuzione, ossia in maniera dinamica.
L’estensione API di gawk
fornisce funzioni di costruzione
(vedi Funzioni di costruzione).
Il linguaggio awk
fornisce funzioni predefinite, che compiono
calcoli vari, di tipo numerico, di input/output e di tipo carattere. Esempi
sono sqrt()
([square root], la radice quadrata di un numero) e
substr()
(che estrae una sottostringa da una stringa).
gawk
fornisce funzioni per la gestione di data e ora,
le operazioni a livello di bit, l’ordinamento di
vettori, il controllo di tipo [di variabile] e la traduzione di stringhe
in fase di esecuzione di progranna.
(Vedi Funzioni predefinite).
gawk
L’implementazione GNU di awk
.
Un documento che descrive le condizioni alle quali gawk
e i suoi
file sorgenti possono essere distribuiti. (Vedi Copia).
“Greenwich Mean Time”. Il termine tradizionalmente usato per UTC. È la datazione usata internamente dai sistemi Unix e POSIX. Si veda anche “Epoca” e “UTC”.
“GNU’s not Unix” (GNU non è Unix). Un progetto della Free Software Foundation, ancora in corso, che mira a creare un ambiente di calcolo completo, liberamente distribuibile, aderente allo standard POSIX.
Una variante del sistema GNU che usa il kernel Linux, invece del kernel proprio della Free Software Foundation, noto come Hurd. Il kernel Linux è un clone di Unix stabile, efficiente, completo di tutte le funzionalità, ed è stato portato su varie architetture hardware. È molto diffuso su sistemi del tipo dei Personal Computer, ma funziona bene anche in parecchi altri computer. Il codice sorgente del kernel Linux è disponibile nei termini della GNU General Public License, la qual cosa è forse il suo aspetto più rilevante.
Si veda “General Public License”.
I caratteri ‘{’ e ‘}’. Le parentesi graffe sono usate in
awk
per delimitare azioni, istruzioni composte, e il codice che
costituisce le funzioni.
Una descrizione dei programmi awk
, nei quali si specifica quali sono
i dati che si vogliono elaborare, e cosa fare quando si trovano tali dati.
Abbreviazione per “Input/Output,” ovvero il trasferimento di dati da e verso un programma in esecuzione.
L’azione che consiste nel confrontare una stringa con un’espressione regolare. Se la regexp descrive qualcosa che è contenuto nella stringa, si dice che la individua.
La procedura con cui si scrive o si modifica un programma in modo che possa inviare messaggi in lingue differenti, senza richiedere ulteriori modifiche al codice sorgente.
Un numero intero, cioè un numero che non ha una parte frazionaria.
Un programma che accetta come input del codice sorgente, e usa le
istruzione contenute nello stesso per elaborare dati e fornire risultati.
awk
è tipicamente (ma non sempre) implementato come un interprete.
Si veda anche “Compilatore”.
Una sequenza di righe consecutive nel/nei file in input. Un’espressione di
ricerca può specificare intervalli di righe di input da far elaborare ad
awk
oppure può specificare singole righe.
(Vedi Panoramica sui criteri di ricerca).
Acronimo di International Organization for Standardization. Questo ente elabora degli standard internazionali in vari settori, inclusi i linguaggi di programmazione, come il C e il C++. In ambito informatico, standard importanti come quelli per il C, C++, e POSIX sono allo stesso tempo standard nazionali americani e standard internazionali ISO. In questo Documento lo Standard C è chiamato “ISO C”. Si veda il sito web ISO per ulteriori informazioni sul nome dell’ente e sul suo acronimo di tre lettere, che rimane lo stesso in tutte le lingue.
Un’espressione all’interno di un programma awk
nella parte
"azione" di una regola criterio di ricerca–azione, o all’interno
di una funzione awk
. Un’espressione può essere un assegnamento
di variabile, un’operazione su un vettore, un ciclo, etc.
Una serie di istruzioni awk
, racchiuse tra parentesi graffe.
Le istruzioni composte possono essere nidificate [possono esserci più livelli
di parentesi graffe].
(Vedi Istruzioni).
Un’istruzione di controllo è un’istruzione per eseguire una data operazione
o un insieme di operazioni all’interno di un programma awk
,
se una determinata condizione è verificata.
Istruzioni di controllo sono: if
, for
, while
, e do
(vedi Istruzioni).
Un moderno linguaggio di programmazione originalmente sviluppato da Sun Microsystems (ora Oracle) che prevede la programmazione orientata agli oggetti. Sebbene normalmente sia implementato compilando le istruzioni per una macchina virtuale standard (la JVM—Java Virtual Machine) il linguaggio può essere compilato per essere eseguito in maniera nativa.
La Korn Shell (ksh
) è una shell Unix sviluppata da David Korn,
presso i Bell Laboratories, nei primi anni ’80. La Korn shell è
compatibile all’indietro con la Bourne shell e comprende molte funzionalità
presenti nella C Shell.
Si veda anche “Bourne Shell”.
Si veda “Licenza Documentazione Libera”.
Questo documento descrive i termini nei quali possono essere distribuiti degli archivi contenenti librerie in formato eseguibile o oggetti condivisi, e il relativo codice sorgente.
Si veda “Lesser General Public License”.
Questo documento descrive i termini in base ai quali questo Documento è pubblicato e può essere copiato. (Vedi Licenza per Documentazione Libera GNU (FDL)).
awk
Il linguaggio in cui i programmi awk
sono scritti.
Si veda “GNU/Linux”.
Si veda “Espressione tra parentesi quadre”.
La funzionalità che fornisce i dati necessari perché un programma internazionalizzato interagisca con l’utente in un particolare linguaggio.
[left-value, ossia valore a sinistra] Un’espressione che può stare alla
sinistra di un operatore di assegnamento.
Nella maggior parte dei linguaggi, gli lvalue possono essere variabili o
elementi di un vettore. In awk
, un designatore di campo può anche
essere usato come un lvalue.
Un valore nel formato “secondi a partire dall’epoch” usato dai sistemi Unix
e POSIX. Usato per le funzioni gawk
mktime()
, strftime()
, e systime()
.
Si veda anche “Epoca,” “GMT,” e “UTC”.
Caratteri usati all’interno di una regexp e che non rappresentano se stessi. Servono invece per rappresentare operazioni con espressioni regolari, come per esempio delle ripetizioni, dei raggruppamenti, o delle alternanze.
Una nidificazione si riscontra dove l’informazione è organizzata a strati,
o dove degli oggetti contengono altri oggetti simili.
In gawk
la direttiva @include
può essere nidificata. La nidificazione “naturale” delle operazioni
aritmetiche e logiche può essere modificato attraverso l’uso di parentesi.
(vedi Precedenza).
Un’operazione che non fa nulla.
Un dato oggetto il cui valore è numerico. Le implementazioni di awk
usano numeri a virgola mobile in doppia precisione per rappresentare i numeri.
Le primissime implementazioni di awk
usavano numeri a virgola mobile
in singola precisione.
Spesso descritto, in termini matematici, come un numero “razionale” o reale, è soltanto un numero che può avere una parte frazionaria. Si veda anche “Doppia precisione” e “Singola precisione”.
Si veda “Metacaratteri”.
Notazione avente come base 8, nella quale le cifre sono 0
–7
.
I numeri ottali in C sono scritti premettendo uno ‘0’,
per indicare la base. Quindi, 013
è 11 ((1 x 8) + 3).
Vedi Numeri non-decimali.
Si veda “Graffe”.
nel linguaggio awk
, una parola chiave (keyword) è una parola
che ha un significato speciale. Queste parole sono riservate e non possono
essere usate come nomi di variabili.
Le parole chiave di gawk
sono:
BEGIN
,
BEGINFILE
,
END
,
ENDFILE
,
break
,
case
,
continue
,
default
delete
,
do…while
,
else
,
exit
,
for…in
,
for
,
function
,
func
,
if
,
next
,
nextfile
,
switch
,
e
while
.
Un acronimo inglese che descrive qual è probabilmente la causa più frequente di problemi nell’uso di un computer. (Problem Exists Between Keyboard and Chair [il problema si trova tra la tastiera e la sedia].)
In gawk
, una lista di directory in cui cercare file contenenti del
codice sorgente per awk
.
Nella shell, una lista di directory in cui ricercare un programma eseguibile.
Si veda “Estensione”.
Il nome di una serie di standard che specificano l’interfaccia di un Sistema
Operativo Portabile (Portable Operating System). La “IX” specifica
che questi standard sono stati originati dallo Unix.
Lo standard più rilevante per gli utenti awk
è lo
IEEE Standard for Information Technology, Standard 1003.1-2008.
Lo standard POSIX 2008 può essere trovato in rete all’indirizzo:
http://www.opengroup.org/onlinepubs/9699919799/.
L’ordine in cui le operazioni sono eseguite quando si usano degli operatori se non si stabiliscono precedenze per mezzo di parentesi.
Variabili e/o funzioni che sono riservate all’uso esclusivo di funzioni di
libreria, e non per il programma principale awk
. Un’attenzione
particolare va prestata quando si desigano tali variabili e funzioni.
(Vedi Nomi di variabili di libreria).
awk
Un programma awk
consiste in una serie di espressioni di
ricerca e azioni, che formano delle regole. Per ogni record in
input a un progranna, le regole del programma sono elaborate nell’ordine in
cui sono scritte. I programmi
awk
possono anche contenere definizioni di funzioni.
Si veda “Record in input” e “Record in output”.
Una singola parte di dati letta da awk
. Solitamente, un
record in input di awk
consiste in una linea di testo.
(Vedi Record).
Un singolo pezzo di dati scritto da awk
. Solitamente, un
record in output di awk
consiste di una o più righe di testo.
Vedi Record.
Quando una funzione chiama se stessa, direttamente o indirettamente. Se questo è chiaro, si può passare a leggere la definizione successiva. Altrimenti, si veda la voce “Ricorsione”.
Si veda “Espressione regolare”.
Un segmento di un programma awk
che specifica come trattare singoli
record in input. Una regola consiste in una espressione di ricerca e in
una azione.
awk
legge un record in input; poi, per ogni regola, se il record in
input soddisfa l’espressione di ricerca della regola, awk
esegue
l’azione specificata dalla regola.
Altrimenti, la regola non ha alcun effetto su quel record in input.
Ridirezione significa ricevere input da quaclosa che non sia il flusso dello standard input, o dirigere output a qualcosa di diverso dal flusso dello standard output.
Si può ridirigere input all’istruzione getline
usando gli operatori
‘<’, ‘|’, e ‘|&’.
Si può ridirigere l’output delle istruzioni print
e printf
verso
un file o un comando di sistema, usando gli operatori ‘>’, ‘>>’,
‘|’, e ‘|&’.
(Vedi Getline,
e Ridirezione).
[right-value, ossia valore a destra] Un valore che può apparire alla destra
di un operatore di assegnazione.
In awk
, essenzialmente ogni espressione ha un valore.
Ognuno di questi valori è un rvalue.
Un valore singolo, sia numerico che di tipo stringa. Le variabili normali sono scalari; i vettori e le funzioni non lo sono.
La natura degli operatori logici awk
‘&&’ e ‘||’.
Se il valore dell’intera espressione in cui sono contenuti è determinabile
valutando solo una parte iniziale dell’espressione, la parte seguente non è
presa in considerazione.
(Vedi Operatori booleani).
awk
Un altro nome per designare un programma awk
.
sed
Si veda “Editore di flusso”.
Il valore iniziale, o il punto di partenza, di una sequenza di numeri casuali.
Una speciale sequenza di caratteri usata per descrivere caratteri non stampabili, come ‘\n’ (ritorno a capo) o ‘\033’ per il carattere ASCII ESC (Escape). (Vedi Sequenze di protezione).
Il programma che interpreta i comandi nei sistemi Unix e in quelli che rispettano lo standard POSIX. La shell funziona sia interattivamente che come un linguaggio di programmazione, che elabora file sequenziali, detti script di shell.
Una rappresentazione di numeri all’interno del computer che ha una parte
espressa sotto forma di frazione. I numeri a singola precisione hanno meno
cifre significative di quelli a doppia precisione, ma le operazioni relative
richiedono talora meno risorse elaborative da parte del computer.
Questo tipo di numero è quello usato da alcune tra le prime versioni di
awk
per memorizzare valori numerici. Nel linguaggio C, sono numeri
di tipo float
.
Il carattere generato premendo la barra spaziatrice sulla tastiera.
Una sequenza di spazi, TAB, o caratteri di ritorno a capo presenti in un record in input o in una stringa.
Un dato che consiste in una sequenza di caratteri, come ‘Io sono una
stringa’. Le costanti stringa sono scritte tra doppi apici nel linguaggio
awk
e possono contenere sequenze di protezione
(Vedi Sequenze di protezione).
Una stringa che non contiene alcun carattere. È rappresentabile
esplicitamente nei programmi awk
mettendo due caratteri di
doppio apice uno dietro all’altro (""
). La si può inserire nei dati
in input mettendo due separatori di campo uno dietro all’altro.
Si veda “Stringa nulla”.
Il carattere generato premendo il tasto TAB sulla tastiera. Normalmente può generare sino a otto spazi in output.
Un sistema operativo per computer originalmente sviluppato nei primi anni ’70 presso gli AT&T Bell Laboratories. Inizialmente si diffuse nelle università di tutto il mondo e in seguito si estese agli ambienti del mondo del lavoro come un sistema per lo sviluppo del software e come server di rete. Ci sono parecchie versioni di Unix a pagamento, come pure parecchi sistemi operativi modellati su Unix e il cui codice sorgente è liberamente disponibile. (come GNU/Linux, NetBSD, FreeBSD, e OpenBSD).
L’abbreviazione comune per “Universal Coordinated Time” (tempo coordinato universale). Questa è l’ora standard di Greenwich, (UK), usata come tempo di riferimento per i calcoli relativi a marcature temporali. Si veda anche “Epoca” e “GMT”.
Un nome per designare un valore. In awk
, le variabili possono
essere degli scalari o dei vettori.
Una collezione di stringhe, in formato ‘nome=valore’, che
ogni programma ha a disposizione. Gli utenti in generale assegnano valori
alle variabili d’ambiente per fornire informazioni a vari programmi.
Esempi tipici sono le variabili d’ambiente HOME
e PATH
.
ARGC
,
ARGV
,
CONVFMT
,
ENVIRON
,
FILENAME
,
FNR
,
FS
,
NF
,
NR
,
OFMT
,
OFS
,
ORS
,
RLENGTH
,
RSTART
,
RS
,
e
SUBSEP
sono le variabili con un significato speciale in awk
.
In più,
ARGIND
,
BINMODE
,
ERRNO
,
FIELDWIDTHS
,
FPAT
,
IGNORECASE
,
LINT
,
PROCINFO
,
RT
,
e
TEXTDOMAIN
sono le variabili con un significato speciale in gawk
.
Se i loro valori sono modificati, il contesto di esecuzione di awk
cambia.
(Vedi Variabili predefinite).
Un raggruppamento di molti valori con uno stesso nome.
La maggior parte dei linguaggi fornisce solo vettori sequenziali.
awk
fornisce vettori associativi.
Un vettore i cui indici possono essere numeri o stringhe, e non solamente interi sequenziali compresi in un intervallo prestabilito.
Successivo: Licenza per Documentazione Libera GNU (FDL), Precedente: Glossario, Su: Top [Contenuti][Indice]
Copyright © 2007 Free Software Foundation, Inc. http://fsf.org/ This is an unofficial translation of the GNU General Public License into Italian. It was not published by the Free Software Foundation, and does not legally state the distribution terms for software that uses the GNU GPL—only the original English text of the GNU GPL does that. However, we hope that this translation will help Italian speakers understand the GNU GPL better. Questa è una traduzione non ufficiale in italiano della GNU General Public License. Questa traduzione non è stata pubblicata dalla Free Software Foundation, e non stabilisce i termini legali di distribuzione del software che usa la GNU GPL. Soltanto la versione originale in inglese della GNU GPL fa ciò. Ciononostante, speriamo che questa traduzione possa aiutare gli utenti di lingua italiana a comprendere un po’ meglio la GNU GPL. A chiunque è permesso copiare e ridistribuire copie esatte di questo documento di licenza, ma non è in alcun modo consentito apportarvi modifiche.
La GNU General Public License è una licenza libera e basata su copyleft per software e altri tipi di opere.
Le licenze della maggior parte del software e di altre opere materiali sono pensate per togliere la libertà di condividere e modificare tali opere. Al contrario, la GNU General Public License ha l’obiettivo di garantire la libertà di condividere e modificare tutte le versioni di un programma e di fare in modo che esso rimanga software libero per tutti gli utenti. Noi, Free Software Foundation, usiamo la GNU General Public License per la maggior parte del nostro software; essa viene applicata anche a qualunque altro software rilasciato dall’autore sotto questa licenza. Chiunque può utilizzare questa licenza per i suoi programmi.
Quando parliamo di software libero (free software), ci riferiamo al concetto di libertà, non al prezzo. Le nostre General Public License sono progettate per garantire che chiunque abbia la libertà di distribuire copie di software libero (anche dietro pagamento di un prezzo, se lo desidera), che chiunque riceva o possa ricevere il codice sorgente se lo vuole, che chiunque possa apportare modifiche al software o utilizzarne delle porzioni in altri software liberi, e che chiunque sappia che ha il diritto di fare tutte queste cose col software libero.
Per proteggere i vostri diritti, abbiamo la necessità di impedire che altri vi neghino questi diritti o vi obblighino a rinunciarvi. Pertanto, chiunque distribuisce o modifica software rilasciato con questa licenza assume dei precisi doveri: il dovere di rispettare la libertà degli altri.
Per esempio, chi distribuisce copie di un programma rilasciato sotto questa licenza, sia gratis che dietro pagamento di un prezzo, e’ obbligato a riconoscere a chi riceve il software esattamente gli stessi diritti che ha ricevuto. Deve garantire che chi riceva il software abbia o possa avere accesso al codice sorgente. E deve chiaramente far conoscere ai destinatari del software queste condizioni, così che essi conoscano quali sono i loro diritti.
Gli sviluppatori che usano la GNU GPL proteggono i vostri diritti in due modi: (1) Rivendicando il copyright sul software, e (2) offrendovi questa licenza che vi garantisce il diritto legale di copiarlo e/o di modificarlo.
Al fine di proteggere gli sviluppatori e gli autori, la GPL spiega chiaramente che non c’è nessuna garanzia per questo software libero. Nell’interesse degli utenti e degli autori, la GPL impone che le versioni modificate del software vengano esplicitamente marcate come “modificate”, in maniera tale che eventuali problemi non vengano erroneamente attribuiti agli autori delle versioni precedenti.
Alcuni dispositivi sono progettati per negare agli utenti l’installazione o l’esecuzione di versioni modificate del software che gira sugli stessi, anche se il costruttore si riserva la possibilità di farlo. Ciò è fondamentalmente incompatibile con l’obiettivo di garantire la libertà degli utenti di modificare il software. Una ripetizione sistematica di tali abusi avviene nel campo dei dispositivi per usi individuali, e ciò rende questi abusi ancora più inaccettabili. Pertanto, abbiamo realizzato questa versione della GPL al fine di proibire queste pratiche. Se problemi simili dovessero sorgere in altri ambiti, saremo pronti ad estendere queste misure a questi nuovi ambiti in versioni future della GPL, nella maniera che si renderà necessaria per difendere la libertà degli utenti.
In conclusione, tutti i programmi sono costantemente minacciati dai brevetti sul software. Gli Stati non dovrebbero permettere ai brevetti sul software di limitare lo sviluppo e l’utilizzo di software per computer, ma nei Paesi in cui ciò avviene noi vogliamo evitare in particolare il pericolo che i brevetti sul software applicati ad un programma libero possano renderlo, a tutti gli effetti, proprietario. Per impedire ciò, la GPL assicura che non è possibile utilizzare i brevetti sul software per rendere un programma non libero.
I termini e le condizioni esatte per la copia, la distribuzione e la modifica del software sono riportate di seguito.
“Questa Licenza” si riferisce alla versione 3 della GNU General Public License.
“Copyright” indica anche leggi simili al copyright che riguardano altri tipi di opere, come le maschere per la produzione di semiconduttori.
“Il Programma” indica qualunque opera che sia soggetta a copyright e che sia rilasciata sotto questa Licenza. I detentori della licenza sono indicati come “tu” o “voi”. Licenziatari e destinatari possono essere individui o organizzazioni.
“Modificare” un’opera significa copiare o adattare tutta o parte dell’opera in una maniera che richieda un permesso di copyright, e non indica la semplice azione di fare una esatta copia dell’opera. L’opera risultante viene chiamata “versione modificata” dell’opera precedente, oppure viene detta opera “basata sulla” opera precedente.
Una “opera coperta da questa licenza” indica il Programma originale non modificato oppure un’opera basata sul Programma.
“Propagare” un’opera significa fare qualunque cosa con essa che, in mancanza di un esplicito permesso, ti renda direttamente o indirettamente perseguibile per violazione secondo le vigenti normative sul copyright, ad eccezione della semplice esecuzione del Programma su un computer o della modifica di una copia privata. La Propagazione include la copia, la distribuzione (con o senza modifiche), la messa a disposizione al pubblico e, in alcuni stati, altre attività simili e connesse.
“Distribuire” un’opera indica qualunque forma di propagazione che permetta a terze parti di effettuare o ricevere delle copie. La mera interazione con un utente attraverso una rete di computer, senza che ci sia alcun trasferimento di una copia, non è considerata “Distribuzione”.
Una interfaccia utente interattiva fornisce delle “Adeguate Informazioni Legali” soltanto nel caso in cui include una apposita funzionalità, resa adeguatamente visibile, che (1) visualizzi un’adeguata informazione di copyright, e (2) informi l’utente che non c’è alcuna garanzia sull’opera (eccetto nel caso in cui delle garanzie sono espressamente fornite), dica che il licenziatario può distribuire l’opera utilizzando questa Licenza, indichi come è possibile prendere visione di una copia di questa Licenza. Se l’interfaccia presenta una lista di comandi o di opzioni, come per esempio un menù, una delle opzioni fornite nella lista deve rispettare questa condizione.
Il “codice sorgente” di un’opera indica la forma più indicata dell’opera per effettuare modifiche su di essa. Il “codice oggetto” indica qualunque forma dell’opera che non sia codice sorgente.
Una “Interfaccia Standard” è una interfaccia che risponde ad uno standard ufficiale definito da un ente di standardizzazione riconosciuto o, nel caso di interfacce specifiche per un particolare linguaggio di programmazione, una interfaccia che è largamente utilizzata dagli sviluppatori per sviluppare in tale linguaggio.
Le “Librerie di Sistema” di un eseguibile includono qualsiasi cosa, eccetto l’opera nel suo insieme, che (a) sia inclusa nella normale forma di pacchettizzazione di un “Componente Principale”, ma che non è parte di quel Componente Principale, e (b) che serva solo a consentire l’uso dell’opera con quel Componente Principale, o per implementare una Interfaccia Standard per la quale esista una implementazione disponibile al pubblico in forma sorgente. Un “Componente Principale”, in questo contesto, è un componente essenziale (kernel, gestore di finestre eccetera) dello specifico sistema operativo (ammesso che ce ne sia uno) sul quale l’eseguibile esegue, o un compilatore utilizzato per produrre il programma, o un interprete di codice oggetto utilizzato per eseguire il programma.
Il “Sorgente Corrispondente” per un’opera in forma di codice oggetto è il codice sorgente necessario per generare, installare e (per un programma eseguibile) eseguire il codice oggetto e per modificare l’opera, inclusi gli script per controllare le suddette attività di generazione, installazione ed esecuzione. Non sono incluse le Librerie di Sistema usate dal programma, o gli strumenti di utilità generica o i programmi liberamente accessibili che sono utilizzati, senza modifiche, per portare a termine le suddette attività ma che non fanno parte dell’opera. Per esempio, il sorgente corrispondente include i file con le definizioni delle interfacce associati ai file sorgente dell’opera, e il codice sorgente delle librerie condivise e sottoprogrammi collegati dinamicamente specificatamente necessari per il programma, ad esempio a causa di stretta comunicazione dati o di controllo di flusso tra questi sottoprogrammi e altre parti del programma.
Il Sorgente Corrispondente non include qualunque cosa che l’utente possa rigenerare automaticamente da altre parti del Sorgente Corrispondente stesso.
Il Sorgente Corrispondente di un’opera in forma di codice sorgente è l’opera stessa.
Tutti i diritti garantiti da questa Licenza sono garantiti per la durata del copyright sul Programma, e sono irrevocabili ammesso che le suddette condizioni siano rispettate. Questa Licenza afferma esplicitamente il tuo permesso illimitato di eseguire il Programma non modificato. Il risultato dell’esecuzione di un programma coperto da questa Licenza è a sua volta coperto da questa Licenza solo se il risultato stesso, a causa del suo contenuto, è un’opera coperta da questa Licenza. Questa Licenza riconosce il tuo diritto all’uso legittimo o altri diritti equivalenti, come stabilito dalla legislazione sul copyright.
Puoi creare, eseguire e propagare programmi coperti da questa Licenza che tu non distribuisci, senza alcuna condizione fino a quando la tua Licenza rimane valida. Puoi distribuire opere coperte da questa Licenza ad altri al solo scopo di ottenere che essi facciano delle modifiche al programma esclusivamente per te, o che ti forniscano dei servizi per l’esecuzione di queste opere, ammesso che tu rispetti i termini di questa Licenza nel distribuire tutto il materiale per il quale non detieni il copyright. Coloro i quali creano o eseguono per conto tuo un programma coperto da questa Licenza lo fanno esclusivamente in tua vece, sotto la tua direzione e il tuo controllo, in maniera tale che sia proibito a costoro effettuare copie di materiale di cui detieni il copyright al di fuori della relazione che intrattengono nei tuoi confronti.
Distribuire opere coperte da licenza in qualunque altra circostanza è consentito soltanto alle condizioni espresse in seguito. Non è consentito sottolicenziare le opere: la sezione 10 lo rende non necessario.
Nessun programma protetto da questa Licenza può essere considerato parte di una misura tecnologica di restrizione che sottosta ad alcuna delle leggi che soddisfano l’articolo 11 del “WIPO copyright treaty” adottato il 20 Dicembre 1996, o a simili leggi che proibiscono o limitano l’elusione di tali misure tecnologiche di restrizione.
Quando distribuisci un programma coperto da questa Licenza, rifiuti tutti i poteri legali atti a proibire l’elusione di misure tecnologiche di restrizione ammesso che tale elusione sia effettuata nell’esercizio dei diritti garantiti da questa Licenza riguardo al programma coperto da questa Licenza, e rinunci all’intenzione di limitare l’operatività o la modifica del programma per far valere, contro i diritti degli utenti del programma, diritti legali tuoi o di terze parti che impediscano l’elusione di misure tecnologiche di restrizione.
Ti è permesso distribuire copie esatte del codice sorgente del Programma come lo hai ricevuto, con qualunque mezzo, ammesso che tu aggiunga in maniera appropriata su ciascuna copia una appropriata nota di copyright; che tu lasci intatti tutti gli avvisi che affermano che questa Licenza e tutte le clausole non-permissive aggiunte in accordo con la sezione 7 sono valide per il codice che distribuisci; che tu lasci intatti tutti gli avvisi circa l’assenza di garanzia; che tu fornisca a tutti i destinatari una copia di questa Licenza assieme al Programma.
Puoi richiedere il pagamento di un prezzo o di nessun prezzo per ciascuna copia che distribuisci, e puoi offrire supporto o garanzia a pagamento.
Puoi distribuire un’opera basata sul Programma, o le modifiche per produrla a partire dal Programma, nella forma di codice sorgente secondo i termini della sezione 4, ammesso che tu rispetti anche tutte le seguenti condizioni:
La giustapposizione di un’opera coperta da questa Licenza assieme ad altre opere separate e indipendenti, che non sono per loro natura estensioni del Programma, e che non sono combinate con esso a formare un altro programma più grande, dentro o in uno stesso supporto di memorizzazione a lungo termine o di distribuzione, è semplicemente detto “aggregato” se la raccolta e il suo copyright non sono utilizzati per limitare l’accesso o i diritti legali degli utenti della raccolta stessa oltre ciò che ciascun singolo programma consente. L’inclusione di un programma coperto da questa Licenza in un aggregato non comporta l’applicazione di questa Licenza alle altre parti dell’aggregato.
Puoi distribuire un programma coperto da questa Licenza in formato di codice oggetto secondo i termini delle sezioni 4 e 5, ammesso che tu fornisca anche il Sorgente Corrispondente in formato comprensibile da un computer sotto i termini di questa stessa Licenza, in uno dei seguenti modi:
Una porzione separabile del codice oggetto, il cui sorgente è escluso dal Sorgente Corrispondente e trattato come Libreria di Sistema, non deve essere obbligatoriamente inclusa nella distribuzione del codice oggetto del programma.
Un “Prodotto Utente” è un (1) “prodotto consumer”, cioè qualunque proprietà personale tangibile che è normalmente utilizzata per scopi personali, familiari o domestici, oppure (2) qualunque cosa progettata o venduta per essere utilizzata in ambiente domestico. Nella classificazione di un prodotto come “prodotto consumer”, i casi dubbi andranno risolti in favore dell’ambito di applicazione. Per un dato prodotto ricevuto da un dato utente, “normalmente utilizzato” si riferisce ad un uso tipico o comune di quella classe di prodotti, indipendentemente dallo stato dell’utente specifico o dal modo in cui l’utente specifico utilizza, o si aspetta o ci si aspetta che utilizzi, il prodotto. Un prodotto è un “prodotto consumer” indipendentemente dal fatto che abbia usi commerciali, industriali o diversi da quelli “consumer”, a meno che questi usi non rappresentino il solo modo utile di utilizzare il prodotto in questione.
Le “Informazioni di Installazione” per un Prodotto Utente sono i metodi, le procedure, le chiavi di autorizzazioni o altre informazioni necessarie per installare ed eseguire versioni modificate di un programma coperto da questa Licenza all’interno di un Prodotto Utente, a partire da versioni modificate dei suoi Sorgenti Corrispondenti. Tali informazioni devono essere sufficienti ad assicurare che il funzionamento del codice oggetto modificato non sia in nessun caso proibito o ostacolato per il solo fatto che sono state apportate delle modifiche.
Se distribuisci un codice oggetto secondo le condizioni di questa sezione in, o assieme, o specificatamente per l’uso in o con un Prodotto Utente, e la distribuzione avviene come parte di una transazione nella quale il diritto di possesso e di uso del Prodotto Utente viene trasferito al destinatario per sempre o per un periodo prefissato (indipendentemente da come la transazione sia caratterizzata), il Sorgente Corrispondente distribuito secondo le condizioni di questa sezione deve essere accompagnato dalle Informazioni di Installazione. Questa condizione non è richiesta se nè tu nè una terza parte ha la possibilità di installare versioni modificate del codice oggetto sul Prodotto Utente (per esempio, se il programma è installato su una ROM)
La condizione che richiede di fornire delle Informazioni di Installazione non implica che venga fornito supporto, garanzia o aggiornamenti per un programma che è stato modificato o installato dal destinatario, o per il Prodotto Utente in cui esso è stato modificato o installato. L’accesso ad una rete può essere negato se le modifiche apportate impattano materialmente sull’operatività della rete o se violano le regole e i protocolli di comunicazione attraverso la rete.
Il Sorgente Corrispondente distribuito, e le Informazioni di Installazione fornite, in accordo con questa sezione, devono essere in un formato che sia pubblicamente documentato (e con una implementazione pubblicamente disponibile in formato di codice sorgente), e non devono richiedere speciali password o chiavi per essere spacchettate, lette o copiate.
Le “Condizioni Aggiuntive” sono condizioni che completano le condizioni di questa Licenza permettendo delle eccezioni a una o più delle condizioni sopra elencate. Le condizioni aggiuntive che sono applicabili all’intero Programma devono essere considerate come se fossero incluse in questa Licenza, a patto che esse siano valide secondo le normative vigenti. Se alcune condizioni aggiuntive fanno riferimento soltanto ad alcune parti del Programma, quelle parti possono essere utilizzate separatamente sotto le stesse condizioni, ma l’intero Programma rimane sottoposto a questa Licenza senza riferimento ad alcuna condizione aggiuntiva.
Quando distribuisci una copia di un programma coperto da questa Licenza, puoi, a tua discrezione, eliminare qualunque condizione aggiuntiva dalla copia, o da parte di essa. (Le Condizioni Aggiuntive possono essere scritte in maniera tale da richiedere la loro rimozione in certi casi di modifica del Programma). Puoi aggiungere Condizioni Aggiuntive su materiale, aggiunto da te ad un’opera coperta da questa Licenza, per il quale hai o puoi garantire un’adeguata licenza di copyright.
Indipendentemente da qualunque altra condizione di questa Licenza, e per il materiale che aggiungi ad un’opera coperta da questa Licenza, puoi (se autorizzato dai legittimi detentori del copyright per il suddetto materiale) aggiungere alle condizioni di questa Licenza delle condizioni che:
Tutte le altre condizioni addizionali non-permissive sono considerate “ulteriori restrizioni”, secondo il significato specificato alla sezione 10. Se il Programma o parti di esso contengono, all’atto della ricezione dello stesso, informative che specificano che esso è soggetto a questa Licenza assieme ad una condizione che è una “ulteriore restrizione”, puoi rimuovere quest’ultima condizione. Se un documento di licenza contiene ulteriori restrizioni ma permette di rilicenziare o distribuire il Programma con questa Licenza, puoi aggiungere al Programma del materiale coperto dalle condizioni di quel documento di licenza, a patto che le ulteriori restrizioni non compaiano nelle versioni rilicenziate o ridistribuite.
Se aggiungi ad un Programma coperto da questa Licenza delle condizioni aggiuntive in accordo con questa sezione, devi aggiungere anche, nei file sorgenti corrispondenti, un avviso che riassuma le condizioni aggiuntive applicate a quei file, ovvero un avviso che specifichi dove è possibile trovare copia delle condizioni aggiunte.
Tutte le Condizioni aggiuntive, permissive o non-permissive, devono essere espresse nella forma di una licenza scritta e separata, o espresse esplicitamente come eccezioni; in entrambi i casi valgono le condizioni succitate.
Non puoi propagare o modificare un programma coperto da questa Licenza in maniera diversa da quanto espressamente consentito da questa Licenza. Qualunque tentativo di propagare o modificare altrimenti il Programma è nullo, e provoca l’immediata cessazione dei diritti garantiti da questa Licenza (compresi tutte le eventuali licenze di brevetto garantite ai sensi del terzo paragrafo della sezione 11).
In ogni caso, se cessano tutte le violazioni di questa Licenza, allora la tua licenza da parte di un dato detentore del copyright viene ripristinata (a) in via cautelativa, a meno che e fino a quando il detentore del copyright non cessa esplicitamente e definitivamente la tua licenza, e (b) in via permanente se il detentore del copyright non ti notifica in alcun modo la violazione entro 60 giorni dalla cessazione della licenza.
Inoltre, la tua licenza da parte di un dato detentore del copyright viene ripristinata in maniera permanente se il detentore del copyright ti notifica la violazione in maniera adeguata, se questa è la prima volta che ricevi una notifica di violazione di questa Licenza (per qualunque Programma) dallo stesso detentore di copyright, e se rimedi alla violazione entro 30 giorni dalla data di ricezione della notifica di violazione.
La cessazione dei tuoi diritti come specificato in questa sezione non provoca la cessazione delle licenze di terze parti che abbiano ricevuto copie o diritti da te secondo questa Licenza. Se i tuoi diritti cessano e non sono ristabiliti in via permanente, non hai diritto di ricevere nuove licenze per lo stesso materiale, secondo quanto stabilito nella sezione 10.
Non sei obbligato ad accettare i termini di questa Licenza al solo fine di ottenere o eseguire una copia del Programma. Similmente, propagazioni collaterali di un Programma coperto da questa Licenza che occorrono come semplice conseguenza dell’utilizzo di trasmissioni peer-to-peer per la ricezione di una copia non richiedono l’accettazione della Licenza. In ogni caso, solo e soltanto questa Licenza ti garantiscono il permesso di propagare e modificare qualunque programma coperto da questa Licenza. Queste azioni violano le leggi sul copyright nel caso in cui tu non accetti questa Licenza. Pertanto, modificando o propagando un programma coperto da questa Licenza, indichi implicitamente la tua accettazione della Licenza.
Ogni qual volta distribuisci un programma coperto da questa Licenza, il destinatario riceve automaticamente una licenza, dal detentore originario del copyright, di eseguire, modificare e propagare il programma, nel rispetto di questa Licenza. Non sei ritenuto responsabile del rispetto di questa Licenza da parte di terze parti.
Una “transazione d’entità” è una transazione che trasferisce il controllo di una organizzazione, o sostanzialmente di tutti i suoi beni, che suddivide una organizzazione o che fonde più organizzazioni. Se la propagazione di un programma coperto da questa Licenza è conseguente ad una transazione di entità, ciascuna parte che ha ruolo nella transazione e che riceve una copia del programma riceve allo stesso tempo qualsiasi licenza sul programma che i predecessori della parte possedevano o potevano rilasciare nel rispetto del paragrafo precedente, e in più il diritto di possesso del Sorgente Corrispondente del programma dal predecessore in interesse, se il predecessore lo possiede o se può ottenerlo senza troppe difficoltà.
Non puoi imporre nessuna ulteriore restrizione sull’esercizio dei diritti garantiti o affermati da questa Licenza. Per esempio, non puoi imporre un prezzo di licenza, una royalty, o altri costi per l’esercizio dei diritti garantiti da questa Licenza, a non puoi dar corso ad una controversia (ivi incluse le controversie incrociate o la difesa in cause legali) affermando che siano stati violati dei brevetti a causa della produzione, dell’uso, della vendita, della messa in vendita o dell’importazione del Programma o di sue parti.
Un “contribuente” è un detentore di copyright che autorizza l’uso secondo questa Licenza di un Programma o di un’opera basata sul Programma. L’opera così licenziata viene chiamata “versione del contribuente”.
I “diritti essenziali di brevetto” da parte di un contribuente sono tutti i diritti di brevetto che appartengono o che sono controllati dal contribuente, che siano già acquisiti o che saranno acquisiti in futuro, che possano essere violati in qualche maniera, consentita da questa Licenza, generando, modificando o vendendo la versione del contribuente, ma non includono i diritti che possano essere violati soltanto come conseguenza di ulteriori modifiche alla versione del contribuente. In relazione a questa definizione, il termine “controllo” include il diritto di garantire sottolicenze di brevetto in maniera consistente con le condizioni di questa Licenza.
Ciascun contribuente ti garantisce la licenza di brevetto sui diritti essenziali di brevetto del contribuente stesso non-esclusiva, valida in tutto il mondo, esente da royalty, di creare, usare, vendere, offrire in vendita, importare e altrimenti eseguire, modificare e propagare i contenuti della versione del contribuente.
Nei tre paragrafi successivi, con “licenza di brevetto” si intende qualunque accordo o contratto, comunque denominato, di non rivendicazione di un brevetto (come per esempio un permesso esplicito di utilizzare un brevetto o un accordo di rinuncia alla persecuzione per violazione di brevetto). “Garantire” una tale licenza di brevetto ad una parte significa portare a termine un tale accordo o contratto di non rivendicazione di brevetto contro la parte.
Se distribuisci un programma coperto da questa Licenza, confidando consapevolmente su una licenza di brevetto, e il Sorgente Corrispondente per il programma non è reso disponibile per la copia, senza alcun onere aggiuntivo e comunque nel rispetto delle condizioni di questa Licenza, attraverso un server di rete pubblicamente accessibile o tramite altri mezzi facilmente accessibili, allora devi (1) fare in modo che il Sorgente Corrispondente sia reso disponibile come sopra, oppure (2) fare in modo di rinunciare ai benefici della licenza di brevetto per quel particolare programma, oppure (3) adoperarti, in maniera consistente con le condizioni di questa Licenza, per estendere la licenza di brevetto a tutti i destinatari successivi. “Confidare consapevolmente” significa che tu sei attualmente cosciente che, eccettuata la licenza di brevetto, la distribuzione da parte tua di un programma protetto da questa Licenza in un paese, o l’utilizzo in un paese del programma coperto da questa Licenza da parte di un destinatario, può violare uno o più brevetti in quel paese che tu hai ragione di ritenere validi.
Se, come conseguenza o in connessione con una singola transazione o con un dato accordo, distribuisci, o fai in modo di distribuire, un programma coperto da questa Licenza, e garantisci una licenza di brevetto per alcune delle parti che ricevono il Programma autorizzandole ad utilizzare, propagare, modificare o distribuire una specifica copia del Programma, allora la licenza di brevetto che fornisci è automaticamente estesa a tutti i destinatari del Programma coperto da questa Licenza e delle opere basate sul Programma.
Una licenza di brevetto è “discriminatoria” se non include nell’ambito della sua copertura, proibisce l’esercizio, o è vincolata al non-esercizio di uno o più dei diritti che sono specificatamente garantiti da questa Licenza. Non puoi distribuire un Programma coperto da questa Licenza se sei parte di un accordo con una terza parte la cui attività comprende la distribuzione di software, secondo il quale tu sei costretto ad un pagamento alla parte terza in funzione della tua attività di distribuzione del Programma, e in conseguenza del quale la parte terza garantisce, a qualunque delle parti che riceveranno il Programma da te, una licenza di brevetto discriminatoria (a) assieme a copie del Programma coperto da questa Licenza distribuite da te (o ad altre copie fatte da codeste copie), oppure (b) principalmente per e in connessione con specifici prodotti o raccolte di prodotti che contengono il Programma, a meno che l’accordo non sia stato stipulato, o le licenze di brevetto non siano state rilasciate, prima del 28 Marzo 2007.
Nessuna parte di questa Licenza può essere interpretata come atta ad escludere o limitare gli effetti di qualunque altra licenza o altri meccanismi di difesa dalla violazione che possano altrimenti essere resi disponibili dalla normativa vigente in materia di brevetti.
Se ti vengono imposte delle condizioni (da un ordine giudiziario, da un accordo o da qualunque altra eventualità) che contraddicono le condizioni di questa Licenza, non sei in nessun modo esonerato dal rispetto delle condizioni di questa Licenza. Se non puoi distribuire un Programma coperto da questa Licenza per sottostare simultaneamente agli obblighi derivanti da questa Licenza e ad altri obblighi pertinenti, allora non puoi distribuire il Programma per nessun motivo. Per esempio, se accetti delle condizioni che ti obbligano a richiedere il pagamento di una royalty per le distribuzioni successivamente effettuate da coloro ai quali hai distribuito il Programma, l’unico modo per soddisfare sia queste condizioni che questa Licenza è evitare del tutto la distribuzione del Programma.
Indipendentemente da qualunque altra condizione espressa da questa Licenza, hai il permesso di collegare o combinare qualunque Programma coperto da questa Licenza con un’opera rilasciata sotto la versione 3 della licenza GNU Affero General Public License, ottenendo un singolo Programma derivato, e di distribuire il Programma risultante. Le condizioni di questa Licenza continuano a valere per le parti riguardanti il Programma che sono coperte da questa Licenza, mentre le condizioni speciali della GNU Affero General Public License, sezione 13, riguardanti l’interazione mediante rete, saranno applicate al Programma così risultante.
La Free Software Foundation può pubblicare delle versioni rivedute e/o delle nuove versioni della GNU General Public License di tanto in tanto. Tali versioni saranno simili, nello spirito, alla presente versione, ma potranno differire nei dettagli al fine di affrontare nuovi problemi e nuove situazioni.
A ciascuna versione viene assegnato un numero identificativo di versione. Se il Programma specifica che si applica a sè stesso una certa versione della GNU General Public License, “o qualunque altra versione successiva”, hai la possibilità di sottostare alle condizioni di quella specifica versione o di qualunque altra versione successiva pubblicata dalla Free Software Foundation. Se il Programma non specifica un numero di versione della GNU General Public License, puoi scegliere qualunque versione della GNU General Public License pubblicata dalla Free Software Foundation.
Se il Programma specifica che un sostituto o un procuratore può decidere quali versioni future della GNU General Public License posso essere utilizzate, allora tale scelta di accettazione di una data versione ti autorizza, in maniera permanente, ad utilizzare quella versione della Licenza per il Programma.
Versioni successive della Licenza possono garantire diritti aggiuntivi o leggermente differenti. Ad ogni modo, nessun obbligo aggiuntivo viene imposto agli autori o ai detentori di copyright come conseguenza della tua scelta di adottare una versione successiva della Licenza.
NON C’È NESSUNA GARANZIA PER IL PROGRAMMA, PER QUANTO CONSENTITO DALLE VIGENTI NORMATIVE. ECCETTO QUANDO ALTRIMENTI STABILITO PER ISCRITTO, I DETENTORI DEL COPYRIGHT E/O LE ALTRE PARTI FORNISCONO IL PROGRAMMA “COSÌ COME È” SENZA GARANZIA DI ALCUN TIPO, NÉ ESPRESSA NÉ IMPLICITA, INCLUSE, MA NON LIMITATE A, LE GARANZIE DI COMMERCIABILITÀ O DI UTILIZZABILITÀ PER UN PARTICOLARE SCOPO. L’INTERO RISCHIO CONCERNENTE LA QUALITÀ E LE PRESTAZIONI DEL PROGRAMMA È DEL LICENZIATARIO. SE IL PROGRAMMA DOVESSE RISULTARE DIFETTOSO, IL LICENZIATARIO SI ASSUME I COSTI DI MANUTENZIONE, RIPARAZIONE O CORREZIONE.
IN NESSUN CASO, A MENO CHE NON SIA RICHIESTO DALLA NORMATIVA VIGENTE O CONCORDATO PER ISCRITTO, I DETENTORI DEL COPYRIGHT, O QUALUNQUE ALTRA PARTE CHE MODIICA E/O DISTRIBUISCE IL PROGRAMMA SECONDO LE CONDIZIONI PRECEDENTI, POSSONO ESSERE RITENUTI RESPONSABILI NEI CONFRONTI DEL LICENZIATARIO PER DANNI, INCLUSO QUALUNQUE DANNEGGIAMENTO GENERICO, SPECIALE, INCIDENTALE O CONSEQUENZIALE DOVUTO ALL’USO O ALL’IMPOSSIBILITÀ D’USO DEL PROGRAMMA (INCLUSI, MA NON LIMITATI A, LE PERDITE DI DATI, LA CORRUZIONE DI DATI, LE PERDITE SOSTENUTE DAL LICENZIATARIO O DA TERZE PARTI O L’IMPOSSIBILITÀ DEL PROGRAMMA A FUNZIONARE ASSIEME AD ALTRI PROGRAMMI), ANCHE NEL CASO IN CUI IL DETENTORE O LE ALTRE PARTI SIANO STATI AVVISATI CIRCA LA POSSIBILITÀ DI TALI DANNEGGIAMENTI.
Se la dichiarazione di garanzia e la limitazione di responsabilità fornite precedentemente non hanno effetto legale in un paese a causa delle loro condizioni, le corti di giustizia devono applicare la norma locale che più si avvicini al rifiuto assoluto di qualsivoglia responsabilità civile relativa al Programma, a meno che una garanzia o una assunzione di responsabilità scritta non accompagni una copia del programma ottenuta dietro pagamento.
Se sviluppi un nuovo programma, e vuoi che esso sia della massima utilità, il modo migliore è renderlo software libero in modo che chiunque possa ridistribuirlo e modificarlo secondo i termini di questa Licenza.
Per fare ciò, allega le seguenti note informative al programma. Il modo migliore è inserirle all’inizio di ciascun file sorgente, al fine di rimarcare adeguatamente la mancanza di garanzia; ciascun file dovrebbe inoltre contenere la dichiarazione di copyright e un riferimento al posto in cui è possibile ottenere la versione completa delle note informative.
<una riga con nome del programma e breve descrizione di ciò che fa.> Copyright (C) <anno> <nome dell'autore> Questo software è libero; lo puoi distribuire e/o modificare alle condizioni stabilite nella 'GNU General Public License' pubblicata dalla Free Software Foundation; fai riferimento alla versione 3 della Licenza, o (a tua scelta) a una qualsiasi versione successiva. Questo programma è distribuito con la speranza che sia utile, ma SENZA ALCUNA GARANZIA; senza neppure la garanzia implicita di COMMERCIABILITÀ o IDONEITÀ AD UN PARTICOLARE SCOPO. Si veda la 'GNU General Public License' per ulteriori dettagli. Dovresti aver ricevuto una copia della GNU General Public License assieme a questo programma; se non è così, si veda http://www.gnu.org/licenses/.
Inoltre, aggiungi le informazioni necessarie a contattarti via posta ordinaria o via posta elettronica.
Se il programma interagisce mediante terminale, fai in modo che visualizzi, quando viene avviato in modalità interattiva, un breve messaggio come quello che segue:
<programma> Copyright (C) <anno> <nome dell'autore> Questo programma non ha ALCUNA GARANZIA; per dettagli usare il comando ‘show w’. Questo è software libero, e ognuno è libero di ridistribuirlo sotto certe condizioni; usare il comando ‘show c’ per i dettagli.
Gli ipotetici comandi ‘show w’ e ‘show c’ devono visualizzare le parti corrispondenti della GNU General Public License. Naturalmente i comandi del tuo programma potrebbero essere differenti; per una interfaccia di tipo GUI, dovresti usare un bottone “About” o “Info”.
Devi inoltre fare in modo che il tuo datore di lavoro (se lavori come programmatore presso terzi) o la tua scuola, eventualmente, firmino una “rinuncia al copyright” sul programma, se necessario. Per maggiori informazioni su questo punto, e su come applicare e rispettare la GNU GPL, consultare la pagina http://www.gnu.org/licenses/.
La GNU General Public License non consente di incorporare il programma all’interno di software proprietario. Se il tuo programma è una libreria di funzioni, potresti ritenere più opportuno consentire il collegamento tra software proprietario e la tua libreria. Se è questo ciò che vuoi, allora utilizza la GNU Lesser General Public License anziché questa Licenza, ma prima leggi http://www.gnu.org/philosophy/why-not-lgpl.html.
Successivo: Indice analitico, Precedente: Copia, Su: Top [Contenuti][Indice]
Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. http://fsf.org This is an unofficial translation of the GNU Free Documentation License into Italian. It was not published by the Free Software Foundation, and does not legally state the distribution terms for software that uses the GNU FDL—only the original English text of the GNU FDL does that. However, we hope that this translation will help Italian speakers understand the GNU FDL better. Questa è una traduzione non ufficiale della GNU Free Documentation License in italiano. Non è una pubblicazione della Free Software Foundation, e non ha validità legale per i termini di distribuzione della documentazione che usa la GNU FDL; solo il testo originale inglese della GNU FDL ha tale validità. Comunque, speriamo che questa traduzione aiuti chi parla italiano a comprendere meglio la GNU FDL. A chiunque è permesso copiare e ridistribuire copie esatte di questo documento di licenza, ma non è in alcun modo consentito apportarvi modifiche.
Lo scopo di questa licenza è di rendere liberi un manuale, un testo o altri documenti funzionali e utili, nel senso di assicurare a tutti la libertà effettiva di copiarli e ridistribuirli, con o senza modifiche, con o senza fini di lucro. In secondo luogo questa licenza prevede per autori ed editori il modo per ottenere il giusto riconoscimento del proprio lavoro, preservandoli dall’essere considerati responsabili per modifiche apportate da altri.
Questa licenza garantisce il “copyleft”: questo significa che i lavori che derivano dal documento originale devono essere ugualmente liberi. È il complemento alla Licenza Pubblica Generale GNU, che è una licenza di tipo “copyleft” pensata per il software libero.
Questa licenza è stata progettata appositamente per l’uso con manuali di software libero, perché il software libero ha bisogno di documentazione libera: un programma libero dovrebbe accompagnarsi a manuali che forniscano le stesse libertà del software. Questa licenza non è limitata alla manualistica del software; può essere utilizzata per ogni testo che tratti un qualsiasi argomento e al di là dell’avvenuta pubblicazione cartacea. Si raccomanda l’uso di questa licenza principalmente per opere che abbiano fini didattici o per manuali.
Questa licenza si applica a qualsiasi manuale o altra opera, su ogni tipo di supporto, che contenga la nota, posta dal detentore del copyright, che attesti la possibilità di distribuzione secondo i termini di questa licenza. Tale nota permette universalmente, senza pagamento di diritti e senza limiti di durata di utilizzare il lavoro secondo le condizioni qui specificate. Con “documento”, nel seguito ci si riferisce a qualsiasi manuale o opera. Ogni fruitore è un destinatario della licenza ed è ad esso che si fa riferimento. Si conviene che la licenza viene accettata se si copia, modifica o distribuisce il lavoro in una maniera tale da richiedere il permesso secondo le leggi sul copyright.
Una “versione modificata” del documento è ogni opera contenente il documento stesso o parte di esso, sia riprodotto alla lettera che con modifiche, oppure traduzioni in un’altra lingua.
Una “sezione secondaria” è un’appendice cui si fa riferimento o una premessa del documento e riguarda esclusivamente il rapporto dell’editore o dell’autore del documento con l’argomento generale del documento stesso (o argomenti affini) e non contiene nulla che possa essere compreso nell’argomento principale. (Perciò, se il documento è in parte un manuale di matematica, una sezione secondaria non può contenere spiegazioni di matematica). Il rapporto con l’argomento può essere un tema collegato storicamente con il soggetto principale o con soggetti affini, o essere costituito da argomentazioni legali, commerciali, filosofiche, etiche o politiche pertinenti.
Le “sezioni non modificabili” sono alcune sezioni secondarie i cui titoli sono esplicitamente elencati come titoli delle sezioni non modificabili nella nota che indica che il documento è realizzato sotto questa licenza. Se una sezione non rientra nella precedente definizione di sezione secondaria, allora non è permesso che venga definita come non modificabile. Il documento può anche non contenere sezioni non modificabili. Se nel documento non vengono indicate sezioni non modificabili, allora significa che non ve ne sono.
I “testi di copertina” sono dei brevi brani di testo che sono elencati, nella prima o quarta pagina di copertina, nella nota che indica che il documento è rilasciato sotto questa licenza. Il testo sulla prima di copertina può essere composto al massimo di 5 parole mentre quello sulla quarta di copertina può essere al massimo di 25 parole.
Una copia “trasparente” indica una copia leggibile da un calcolatore, codificata in un formato le cui specifiche sono disponibili pubblicamente, tale che il suo contenuto possa essere modificato in modo semplice con generici editor di testi o (per immagini composte da pixel) con generici editor di immagini o (per i disegni) con qualche editor di disegni ampiamente diffuso; la copia deve essere adatta al trattamento per la formattazione o per la conversione in una varietà di formati atti alla successiva formattazione. Una copia fatta in un formato di file, per il resto trasparente, i cui marcatori o assenza di tali sono stati progettati per intralciare o scoraggiare modifiche future da parte dei lettori non è trasparente. Un formato immagine non è trasparente se viene usato per rappresentare una notevole quantità di testo. Una copia non “trasparente” viene detta “opaca”.
Esempi di formati adatti per copie trasparenti sono l’ASCII puro senza marcatori, il formato di ingresso per Texinfo, il formato di ingresso per LaTeX, SGML o XML accoppiati ad una DTD pubblica e disponibile, e i formati conformi agli standard HTML semplice, Postscript e PDF progettati per essere modificati manualmente. Esempio di formati immagine trasparenti includono il PNG, XCF e JPG. I formati opachi includono i formati proprietari che possono essere letti e modificati solo con word processor proprietari, SGML o XML per cui non è in genere disponibile la DTD o gli strumenti per il trattamento, e i formati HTML, Postscript e PDF generati automaticamente da qualche word processor esclusivamente come output.
La “pagina del titolo” di un libro stampato indica la pagina del titolo stessa, più qualche pagina seguente per quanto necessario a contenere in modo leggibile, il materiale che la licenza prevede che compaia nella pagina del titolo. Per opere in formati in cui non sia contemplata esplicitamente la pagina del titolo, con “pagina del titolo” si intende il testo prossimo al titolo dell’opera, precedente l’inizio del corpo del testo.
Il termine “editore” indica qualunque persona o entità che distribuisce al pubblico copie del documento.
Una sezione “Intitolata XYZ” significa una sottosezione con nome del documento il cui titolo sia precisamente XYZ o che contenga XYZ in parentesi dopo il testo che traduce XYZ in un’altra lingua (in questo caso XYZ sta per uno specifico nome di sezione menzionato sotto, come per i “Riconoscimenti”, “Dediche”, “Approvazioni”, o “Storia”). Secondo questa definizione, “preservare il titolo” di tale sezione quando si modifica il documento, significa che essa rimane una sezione “Intitolata XYZ”.
Il Documento può includere dei limiti alla garanzia accanto alla nota affermante l’applicazione di questa licenza al documento. Questi limiti alla garanzia sono da considerare da includere come riferimento a questa licenza, ma solo per quanto riguarda le limitazioni alla garanzia: ogni altra implicazione che questi limiti alla garanzia possono avere è da considerarsi nulla e non ha effetto sul significato di questa licenza.
Si può copiare e distribuire il documento con qualsiasi mezzo, con o senza fini di lucro, purché tutte le copie contengano questa licenza, le note di copyright e l’avviso che questa licenza si applica al documento, e che non si aggiungano altre condizioni al di fuori di quelle della licenza stessa. Non si possono usare misure tecniche per impedire o controllare la lettura o la produzione di copie successive alle copie che si producono o distribuiscono. Si possono comunque accettare compensi per la copiatura. Se si distribuiscono un numero sufficiente di copie si devono seguire anche le condizioni della sezione 3.
Alle stesse condizioni sopra menzionate si possono prestare copie e mostrarle pubblicamente.
Se si pubblicano a mezzo stampa (o in formati che tipicamente posseggono copertine) più di 100 copie del documento, e la nota della licenza richiede uno o più testi di copertina, si devono includere nelle copie, in modo chiaro e leggibile, tutti i testi di copertina indicati: il testo della prima di copertina in prima di copertina e il testo di quarta di copertina in quarta di copertina. Ambedue devono identificare l’editore che pubblica il documento. La prima di copertina deve presentare il titolo completo con tutte le parole che lo compongono egualmente visibili ed evidenti. Si può aggiungere altro materiale alle copertine. Il copiare con modifiche limitate alle sole copertine, purché si preservino il titolo e le altre condizioni viste in precedenza, è considerato alla stregua di copiare alla lettera.
Se il testo richiesto per le copertine è troppo voluminoso per essere riprodotto in modo leggibile, se ne può mettere una prima parte (per quanto ragionevolmente può stare) in copertina, e continuare il resto nelle pagine immediatamente seguenti.
Se si pubblicano o distribuiscono copie opache del documento in numero superiore a 100, si deve anche includere una copia trasparente leggibile da un calcolatore in ogni copia oppure menzionare in ogni copia opaca un indirizzo di rete di calcolatori pubblicamente accessibile che utilizzi un protocollo di rete standard pubblico, da cui si possa scaricare liberamente una copia trasparente completa del documento, senza materiale aggiuntivo. Se si adotta quest’ultima opzione, si deve prestare la giusta attenzione, nel momento in cui si inizia la distribuzione in quantità elevata di copie opache, ad assicurarsi che la copia trasparente rimanga accessibile all’indirizzo stabilito fino ad almeno un anno dopo l’ultima distribuzione (direttamente o attraverso distributori o rivenditori) di quell’edizione al pubblico.
È caldamente consigliato, benché non obbligatorio, contattare l’autore del documento prima di distribuirne un numero considerevole di copie, per metterlo in grado di fornire una versione aggiornata del documento.
Si possono copiare e distribuire versioni modificate del documento rispettando le condizioni delle precedenti sezioni 2 e 3, purché la versione modificata sia realizzata seguendo questa stessa licenza, con la versione modificata che svolga il ruolo del “documento”, così da estendere la licenza sulla distribuzione e la modifica a chiunque ne possieda una copia. Inoltre nelle versioni modificate si deve:
Se la versione modificata comprende nuove sezioni di primaria importanza o appendici che ricadono in “sezioni secondarie”, e non contengono materiale copiato dal documento, si ha facoltà di rendere non modificabili quante sezioni si voglia. Per fare ciò si aggiunga il loro titolo alla lista delle sezioni non modificabili nella nota di licenza della versione modificata. Questi titoli devono essere distinti dai titoli di ogni altra sezione.
Si può aggiungere una sezione intitolata “Approvazioni”, a patto che non contenga altro che le approvazioni alla versione modificata prodotte da vari soggetti–per esempio, affermazioni di revisione o che il testo è stato approvato da una organizzazione come la definizione normativa di uno standard.
Si può aggiungere un brano fino a cinque parole come testo di prima di copertina e un brano fino a 25 parole come testo di quarta di copertina, alla fine dell’elenco dei testi di copertina nella versione modificata. Solamente un brano del testo di prima di copertina e uno del testo di quarta di copertina possono essere aggiunti (anche con adattamenti) da ciascuna persona o organizzazione. Se il documento include già un testo di copertina per la stessa copertina, precedentemente aggiunto o adattato da qualunque fruitore o dalla stessa organizzazione nel nome della quale si agisce, non se ne può aggiungere un altro, ma si può rimpiazzare il vecchio ottenendo l’esplicita autorizzazione dall’editore precedente che aveva aggiunto il testo di copertina.
L’autore/i e l’editore/i del documento non danno, tramite questa licenza, il permesso di usare i loro nomi per pubblicizzare o asserire, anche implicitamente, la loro approvazione di ogni versione modificata.
Si può combinare il documento con altri pubblicati con questa licenza, seguendo i termini definiti nella precedente sezione 4 per le versioni modificate, a patto che si includa l’insieme di tutte le sezioni non modificabili di tutti i documenti originali, senza modifiche, e si elenchino tutte come sezioni non modificabili della combinazione di documenti nella licenza della stessa, mantenendo tutti i limiti alla garanzia.
Nella combinazione è necessaria una sola copia di questa licenza, e più sezioni non modificabili possono essere rimpiazzate da una singola copia se identiche. Se ci sono più sezioni non modificabili con lo stesso nome ma contenuti differenti, si renda unico il titolo di ciascuna sezione aggiungendovi, alla fine e tra parentesi, il nome dell’autore o editore della sezione, se noti, o altrimenti un numero distintivo. Si facciano gli stessi aggiustamenti ai titoli delle sezioni nell’elenco delle sezioni non modificabili nella nota di copyright della combinazione.
Nella combinazione si devono unire le varie sezioni intitolate “Storia” nei vari documenti originali di partenza per formare una unica sezione intitolata “Storia”; allo stesso modo si unisca ogni sezione intitolata “Ringraziamenti”, e ogni sezione intitolata “Dediche”. Si devono eliminare tutte le sezioni intitolate “Approvazioni”.
Si può produrre una raccolta che consista del documento e di altri documenti rilasciati sotto questa licenza, e rimpiazzare le singole copie di questa licenza nei vari documenti con una sola inclusa nella raccolta, solamente se si seguono le regole fissate da questa licenza per le copie alla lettera come se si applicassero a ciascun documento.
Si può estrarre un singolo documento da tale raccolta e distribuirlo separatamente sotto questa licenza, solo se si inserisce una copia di questa licenza nel documento estratto e se si seguono tutte le altre regole fissate da questa licenza per le copie alla lettera del documento.
Un’unione del documento o sue derivazioni con altri documenti o lavori separati o indipendenti, all’interno di, o a formare un, archivio o un supporto, per la memorizzazione o la distribuzione, viene chiamato un “aggregato” se il copyright risultante dall’unione non viene usato per limitare i diritti legali degli utilizzatori oltre a ciò che viene permesso dai singoli lavori. Quando il documento viene incluso in un aggregato, questa licenza non si applica ad altri lavori nell’aggregato che non siano essi stessi dei lavori derivati dal documento.
Se le esigenze del testo di copertina della sezione 3 sono applicabili a queste copie del documento allora, se il documento è inferiore alla metà dell’intero aggregato i testi di copertina del documento possono essere piazzati in copertine che delimitano il documento all’interno dell’aggregato, o dell’equivalente elettronico delle copertine se il documento è in un formato elettronico. Altrimenti devono apparire nella copertina dell’intero aggregato.
La traduzione è considerata un tipo di modifica, di conseguenza si possono distribuire traduzioni del documento nei termini della sezione 4. Rimpiazzare sezioni non modificabili con traduzioni richiede un particolare permesso da parte dei detentori del copyright, ma è possibile includere la traduzione di parti o di tutte le sezioni non modificabili in aggiunta alle versioni originali di queste sezioni. È possibile includere una traduzione di questa licenza, di tutte le avvertenze del documento e di tutti i limiti di garanzia, a condizione che si includa anche la versione originale in inglese della licenza completa, comprese le avvertenze e limitazioni di garanzia. In caso di discordanza tra la traduzione e la versione originale inglese di questa licenza o avvertenza o limitazione di garanzia, prevale sempre la versione originale inglese.
Se una sezione del documento viene titolata “Riconoscimenti”, “Dediche”, o “Storia”, il requisito (sezione 4) di preservare il titolo (sezione 1) richiederà tipicamente il cambiamento del titolo.
Non si può sublicenziare il documento, copiarlo, modificarlo, o distribuirlo al di fuori dei termini espressamente previsti da questa licenza. Ogni altro tentativo di applicare una licenza al documento, copiarlo, modificarlo, o distribuirlo è nullo e pone fine automaticamente ai diritti previsti da questa licenza.
In ogni caso, se cessano tutte le violazioni di questa Licenza, allora la specifica licenza di un particolare detentore del copyright viene ripristinata (a) in via provvisoria, a meno che e fino a quando il detentore del copyright non faccia estinguere esplicitamente e definitivamente la licenza, e (b) in via permanente se il detentore del copyright non notifica in alcun modo la violazione entro 60 giorni dalla cessazione della licenza.
Inoltre, la licenza di un dato detentore del copyright viene ripristinata in maniera permanente se quest’ultimo notifica la violazione in maniera adeguata, se si tratta della prima volta che si riceve una notifica di violazione della licenza (per qualsiasi opera) dallo stesso detentore di copyright, e se la violazione viene corretta entro 30 giorni dalla data di ricezione della notifica di violazione.
La cessazione dei diritti come specificato in questa sezione non provoca la cessazione delle licenze di terze parti che abbiano ricevuto copie o diritti secondo questa licenza. Se i diritti sono cessati e non sono stati ristabiliti in via permanente, la ricezione di una copia dello stesso materiale, in tutto o in parte, non dà alcun diritto ad utilizzarlo.
La Free Software Foundation può occasionalmente pubblicare versioni nuove o rivedute della Licenza per Documentazione Libera GNU. Le nuove versioni saranno simili nello spirito alla versione attuale ma potrebbero differirne in qualche dettaglio per affrontare nuovi problemi e concetti. Si veda http://www.gnu.org/copyleft/.
Ad ogni versione della licenza viene dato un numero che la distingue. Se il documento specifica che si riferisce ad una versione particolare della licenza “o ogni versione successiva”, si ha la possibilità di seguire termini e condizioni sia della versione specificata che di ogni versione successiva pubblicata (non come bozza) dalla Free Software Foundation. Se il documento non specifica un numero di versione particolare di questa licenza, si può scegliere ogni versione pubblicata (non come bozza) dalla Free Software Foundation. Se il documento specifica che un delegato può decidere quale futura versione di questa licenza può essere utilizzata, allora la dichiarazione pubblica di accettazione della versione, da parte del delegato, autorizza in maniera permanente a scegliere tale versione per il documento.
Il termine “sito per la collaborazione massiva multiautore” (o “sito MMC”) indica qualsiasi server web che pubblica opere sottoponibili a copyright e fornisce a chiunque appositi strumenti per modificare tali opere. Un wiki pubblico modificabile da chiunque è un esempio di server in questione. Una “collaborazione massiva multiautore” (o “MMC”) contenuta nel sito indica un qualunque insieme di opere sottoponibili a copyright pubblicate sul sito MMC.
Il termine “CC-BY-SA” indica la licenza Creative Commons Attribution-Share Alike 3.0 pubblicata dalla Creative Commons Corporation, un’organizzazione senza fini di lucro con sede principale a San Francisco, California, come anche le future versioni di tale licenza pubblicate dalla stessa organizzazione.
“Incorporare” significa pubblicare o ripubblicare un documento in tutto o in parte, come parte di un altro documento.
Una MMC è “qualificata a cambiare questa licenza” se ha adottato questa licenza e se tutte le opere precedentemente pubblicate con questa licenza altrove rispetto alla MMC e successivamente incorporate del tutto o in parte nella MMC (1) non hanno testo di copertina o sezioni invarianti e (2) sono state incorporate prima del 1° Novembre 2008.
L’operatore di un sito MMC può ripubblicare un MMC contenuto nel sito con una CC-BY-SA nello stesso sito in qualsiasi momento prima del 1° Agosto 2009, da parte di una MMC qualificata a cambiare questa licenza.
Per applicare questa licenza ad un documento che si è scritto, si includa una copia della licenza nel documento e si inserisca la seguente nota di copyright appena dopo la pagina del titolo:
Copyright (C) <anno> <il vostro nome> È permesso copiare, distribuire e/o modificare questo documento seguendo i termini della ``Licenza per documentazione libera GNU'', versione 1.3 o ogni versione successiva pubblicata dalla Free Software Foundation; senza sezioni non modificabili, senza testi di prima di copertina e di quarta di copertina. Una copia della licenza è inclusa nella sezione intitolata ``Licenza per la documentazione libera GNU''.
Se ci sono sezioni non modificabili, testi di prima di copertina e di quarta di copertina, scrivere nella parte “with… di copertina” il testo seguente:
con le seguenti sezioni non modificabili lista dei loro titoli, con i seguenti testi di prima di copertina elenco, e con i seguenti testi di quarta di copertina elenco,
Se esistono delle sezioni non modificabili ma non i testi di copertina, o qualche altra combinazione dei tre elementi sopra riportati, fondere assieme le due alternative in modo da conformarsi alla situazione descritta.
Se il vostro documento contiene esempi non banali di programma in codice sorgente si raccomanda di rilasciare gli esempi contemporaneamente applicandovi anche una licenza di software libero di vostra scelta, come per esempio la Licenza Pubblica Generica GNU, al fine di permetterne l’uso come software libero.
Precedente: Licenza per Documentazione Libera GNU (FDL), Su: Top [Contenuti][Indice]
Salta a: | !
"
#
$
%
&
'
(
*
+
,
-
.
/
;
<
=
>
?
@
[
\
^
_
{
|
~
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z |
---|
Salta a: | !
"
#
$
%
&
'
(
*
+
,
-
.
/
;
<
=
>
?
@
[
\
^
_
{
|
~
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z |
---|
Lo standard POSIX 2008 è accessibile in rete all’indirizzo http://www.opengroup.org/onlinepubs/9699919799/.
Questi programmi di utilità sono disponibili sui sistemi conformi a POSIX, come pure sui sistemi tradizionali basati su Unix. Se si usa qualche altro sistema operativo, si deve comunque avere familiarità con i concetti di ridirezione I/O e di pipe.
Qualche altro sistema operativo obsoleto su cui
gawk
era stato portato non è più mantenuto e il codice specifico
per quei sistemi è stato rimosso.
Solo i sistemi Solaris usano ancora un
vecchio awk
per il programma di utilità predefinito
awk
. Una versione più moderna di awk
si trova
nella directory /usr/xpg6/bin su questi sistemi.
Tutte queste differenze
si trovano nell’indice alla
voce “differenze tra awk
e gawk
.”
GNU sta per “GNU’s Not Unix.”
La terminologia “GNU/Linux” è spiegata nel Glossario.
Il meccanismo ‘#!’ funziona nei sistemi GNU/Linux, in quelli basati su BSD e nei sistemi Unix a pagamento.
Il ‘?’ e i ‘:’ elencati sopra sono
usati nell’espressione condizionale in tre parti descritta in
Espressioni condizionali.
Il cambio di riga dopo ‘?’ e i ‘:’ è un’estensione minore in
gawk
; specificando --posix come opzione
(vedi Opzioni), quest’estensione non è valida.
Altri linguaggi di script popolari comprendono Ruby e Perl.
per maggiori dettagli,
si veda la Sezione 4.4 di RFC 3875. Si veda anche
note esplicative spedite alla mailing list gawk
bug.
Non raccomandato.
Punti e virgola in MS-Windows.
La versione di gawk
che state usando potrebbe usare una directory diversa; ciò dipende da come
gawk
è stato compilato e installato. La directory effettiva è il
valore di $(datadir)
generato quando è stato configurato
gawk
. Non è comunque il caso di preoccuparsi per questo.
In altri testi, un’espressione tra parentesi quadre potrebbe essere definita come insieme di caratteri, classe di caratteri o lista di caratteri.
È meglio usare due barre inverse se si sta usando una costante stringa con un operatore regexp o una funzione.
Programmatori esperti in C e C++ noteranno che questo è possible, usando qualcosa come ‘IGNORECASE = 1 && /foObAr/ { … }’ e ‘IGNORECASE = 0 || /foobar/ { … }’. Comunque, questo è un po’ tortuoso e non è raccomandato.
Se questo sembra oscuro,
non c’è ragione di preoccuparsi; significa solo che gawk
fa
la cosa giusta.
Almeno quelle che ci sono note.
A un operatore binario, come ‘*’ per la
moltiplicazione, servono due operandi. La distinzione è necessaria poiché
awk
ha anche operatori unari (un operando) e ternari (tre
operandi).
Grazie ad Andrew Schorr per questo suggerimento.
Il programma di utilità sed
è un
“editore di flusso”. Anche il suo comportamento è definito dallo standard
POSIX.
Il formato CSV non ha avuto, per molti anni, una definizione standard formale. RFC 4180 standardizza le pratiche più comuni.
Quando FS
è la stringa nulla (""
), o
un’espressione regolare, questa particolare funzionalità di RS
non
viene applicata; si applica al separatore di campo quando è costituito da un
solo spazio:
‘FS = " "’.
Questo non è completamente vero. RT
può essere
cambiato se RS
è un’espressione regolare.
Questo presuppone che lo standard input provenga dalla tastiera.
“tty” in /dev/tty è un’abbreviazione di “TeleTYpe” [telescrivente], un terminale seriale.
La terminologia tecnica è piuttosto macabra. Il processo-figlio terminato è chiamato “zombie,” e la pulizia alla fine dello stesso è chiamata “reaping” [mietitura].
Prima
della versione 4.2, il valore di ritorno dopo la chiusura di
una pipe o di un coprocesso era una cifra di due byte (16-bit),
contenente il valore restituito dalla chiamata di sistema wait()
La rappresentazione interna di tutti i numeri, compresi gli interi, usa numeri in virgola mobile a doppia precisione. Sui sistemi più moderni, questi sono nel formato standard IEEE 754. Vedi Calcolo con precisione arbitraria, per maggiori informazioni.
Per casi eccezionali possono essere richieste fino a 752 cifre (!), non sembra che sia il caso di preoccuparsene qui.
Può capitare che BWK
awk
, gawk
e mawk
lo interpretino nel modo giusto,
ma non ci si dovrebbe fare affidamento.
gawk
chiama queste variabili unassigned (non ancora assegnate), come si
vede dall’esempio che segue.
Quindi, una
stringa numerica POSIX e una variabile tipo strnum di gawk
sono equivalenti.
Tecnicamente, il confronto di stringhe
dovrebbe funzionare come se le stringhe fossero confrontate usando la
funzione strcoll()
di C.
Si consulti il sito web dell’Austin Group.
Questo programma ha un bug; stampa righe che iniziano con ‘END’. Come si può risolvere?
La versione originale di awk
continuava a
leggere e ignorare i record in input fino alla fine del file.
Alcune tra le prime implementazioni di Unix
awk
inizializzavano FILENAME
a "-"
, anche se
vi erano file-dati da elaborare. Un tale comportamento era incorretto e
non ci si dovrebbe poter contare nei programmi.
Per non parlare dei grossi problemi di implementazione.
L’ordine potrà variare nelle diverse implementazioni
di awk
, che tipicamente usa tabelle hash per memorizzare
elementi e valori del vettore.
Quando due elementi
risultano uguali, la funzione C qsort()
non garantisce
che dopo l’ordinamento venga rispettato il loro ordine relativo originale.
Usando il valore di stringa per stabilire un ordinamento univoco quando i
valori numerici sono uguali assicura che il comportamento di gawk
sia coerente in differenti ambienti.
Un ringraziamento a Michael Brennan per la segnalazione.
Per un’introduzione alle tematiche suddette, si può consultare l’articolo "Localizzazione dei programmi" nel sito pluto.it.
La versione C di
rand()
in molti sistemi Unix
produce notoriamente delle sequenze piuttosto mediocri di numeri casuali.
Tuttavia, non è prescritto che un’implementazione di awk
debba usare la funzione rand()
del linguaggio C per implementare
la versione awk
di rand()
.
In effetti, gawk
usa, per generare numeri casuali,
la funzione random()
di BSD, che è
notevolmente migliore di rand()
mawk
usa un seme differente ogni volta.
I numeri casuali generati da un computer non sono veramente casuali. Tecnicamente sono conosciuti come numeri pseudo-casuali. Ciò vuol dire che, anche se i numeri in una sequenza sembrano casuali, è possibile in realtà generare la stessa sequenza di numeri casuali più e più volte.
Tranne nel caso si usi l’opzione --non-decimal-data, il che non è consigliato. Vedi Dati non decimali per ulteriori informazioni.
Si noti che questo significa che il record sarà dapprima
ricostruito, usando il valore di OFS
se qualche campo è stato cambiato,
e che i campi saranno aggiornati dopo la sostituzione, anche se l’operazione in
sé non cambia il record (è una “no-op”) come ‘sub(/^/, "")’.
Questo è differente da C e C++, in cui il primo carattere ha il numero zero.
Questa decisione si è dimostrata piuttosto avventata,
anche se una nota in questa sezione avvertiva che la successiva versione
principale di gawk
avrebbe adottato le regole POSIX.
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.
In uno scambio di messaggi privato il Dr. Kernighan mi ha comunicato che questo modo di procedere è probabilmente errato.
Vedi Glossario, in particolare le voci “Epoca” e “UTC.”
Il comando di utilità GNU
date
può fare anche molte delle cose qui descritte. Può essere
preferibile usarlo per semplici operazioni relative a data e ora in semplici
script della shell.
Occasionalmente ci sono dei minuti in un anno con un secondo intercalare, il che spiega perché i secondi possono arrivare fino a 60.
Sfortunatamente,
non tutte le funzioni strftime()
dei vari sistemi operativi
ammettono tutte le conversioni qui elencate.
Se questo risulta incomprensibile, non è il caso di preoccuparsi; queste notazioni hanno lo scopo di facilitare la “internazionalizzazione” dei programmi. Altre funzionalità di internazionalizzazione sono descritte in Internazionalizzazione.
Questo è perché ISO C lascia
indefinito il comportamento della versione C di strftime()
e
gawk
usa la versione di sistema di strftime()
,
se disponibile.
Tipicamente, lo specificatore di conversione "non previsto" non appare
nella stringa risultante, o appare così come è scritto.
Questo esempio
presuppone che degli zeri riempiano le posizioni a sinistra.
Per gawk
, è sempre
così, ma in alcuni linguaggi è possibile che le posizioni a sinistra
siano riempite con degli uno.
Per essere più chiari,
la conseguenza è che gawk
può memorizzare solo un determinato
intervallo di valori interi; i numeri al di fuori di questo intervallo vengono
ridotti per rientrare all’interno dell’intervallo.
Questo
programma in realtà non verrà eseguito, perché foo()
risulterà
essere una funzione non definita.
Verso la fine del 2012.
Questa
limitazione potrebbe cambiare in una futura versione;
per appurarlo, si controlli la documentazione che accompagna
la versione in uso di gawk
.
Purtroppo, a distanza di oltre 35 anni, molte delle lezioni impartite da questo libro devono ancora essere apprese da un gran numero di programmatori professionisti.
I risultati non sono identici.
L’output del record trasformato sarà tutto in minuscolo, mentre
IGNORECASE
preserva il contenuto originale del record in input.
Sebbene tutte le routine di libreria si sarebbero
potute riscrivere usando questa convenzione, ciò non è stato fatto, per far
vedere come lo stile di programmazione in awk
si è evoluto e
per fornire alcuni spunti per questa spiegazione.
L’opzione da riga di comando di gawk
--dump-variables è utile per verificare questo.
La situazione sta però cambiando: molti sistemi usano Unicode, una serie di caratteri molto ampia che comprende ASCII al suo interno. Nei sistemi che supportano interamente Unicode, un carattere può occupare fino a 32 bit, facendo diventare i semplici test usati qui eccessivamente complessi.
ASCII
è stato esteso in molti paesi per usare i valori da 128 a 255 includendo
i caratteri specifici del paese. Se il sistema in uso si avvale di queste
estensioni, si può semplificare _ord_init()
per eseguire un ciclo da
0 a 255.
Sarebbe bello
se awk
avesse un operatore di assegnamento per la concatenazione.
La mancanza di un esplicito operatore per la concatenazione rende le operazioni
sulle stringhe più difficili di quanto potrebbero essere.
I
file con suffisso flac contengono normalmente dei brani musicali.
metaflac
è un programma che permette di modificare
le informazioni [metadati] contenute all’inizio di un file di tipo
flac.
Il criterio di
ricerca speciale BEGINFILE
(vedi BEGINFILE/ENDFILE) fornisce un
meccanismo alternativo per trattare i file che non sono leggibili.
Tuttavia, il codice qui proposto fornisce una soluzione portabile.
Questa funzione
è stata scritta prima che gawk
acquisisse la capacità di
dividere le stringhe in caratteri singoli usando ""
come separatore.
È stata lasciata così, poiché l’uso di substr()
è più portabile.
Questo è
vero per le versioni più antiche di Unix. In quelle più recenti,
la password di ogni utente è stata trasferita nel file /etc/shadow,
un file non accessibile dall’utente normale. La struttura del file
/etc/passwd è rimasta la stessa, ma al posto del campo password
c’è una x
.
Capita spesso che le informazioni sulla password siano memorizzate in una lista in rete.
I programmi si distinguono tradizionalemente in CPU-bound e I/O-bound. Quelli CPU-bound effettuano elaborazioni che non richiedono molta attività di I/O, come ad esempio la preparazione di una tavola di numeri primi. Quelli I/O bound leggono dei file, ma richiedono poca attività di elaborazione per ogni record letto.
C’è un
piccolo problema col codice appena illustrato. Supponiamo che la prima volta
non ci siano nomi. Questo codice aggiunge i nomi con una virgola iniziale.
Inoltre non controlla che ci sia un $4
.
Inoltre, qui si introduce un errore subdolo; se una corrispondenza viene trovata, viene inviata in output la riga tradotta, non quella originale.
Questo è la sintassi tradizionale.
La versione POSIX del comando ha una sintassi differente, ma per lo scopo di
questo programma awk
la cosa non ha importanza.
Poiché gawk
gestisce le
localizzazioni in cui un carattere può occupare più di un byte, questo codice
conta i caratteri, non i byte.
Su alcuni sistemi
più datati, incluso Solaris, la versione di sistema di tr
può
richiedere che le liste siano scritte come espressioni di intervallo,
racchiuse in parentesi quadre
(‘[a-z]’) e tra apici, per evitare che la shell effettui
espansioni di nome-file. Questo non è un miglioramento.
“Del mondo-reale” è definito come “un programma effettivamente usato per realizzare qualcosa”.
Una spiegazione dettagliata del linguaggio della
sh
non rientra negli intenti di questo libro. Qualche spiegazione
sommaria viene fornita, ma se si desidera una comprensione più dettagliata, si
dovrebbe consultare un buon libro sulla programmazione della shell.
In alcune versioni molto datate di
awk
, il test ‘getline da_buttare < t’ può ripetersi in un ciclo
infinito se il file esiste ma è vuoto.
gawk
è in grado di elaborare istruzioni @include
al suo stesso interno, per
permettere l’uso di programmi awk
come script Web CGI.
Questo è la definizione trovata usando
define: state machine
come chiave di ricerca in Google.
Questo è il motivo per cui gli ordinamenti predefiniti iniziano con il carattere ‘@’, che non può essere usato in un identificatore.
Ciò è vero perché il confronto basato sulla localizzazione
avviene solo quando si è in modalità POSIX-compatibile, e poiché asort()
e asorti()
sono estensioni di gawk
, esse non sono disponibili
in quel caso.
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.
gawk
fa del suo meglio
per mantenere la distinzione tra commenti posti dopo delle istruzioni e
commenti su righe a sé stanti. Per limiti insiti nell’implementazione,
non sempre questo può avvenire in maniera corretta, in particolare nel
caso di istruzioni switch
. I manutentori di gawk
sperano di poter migliorare la situazione in una futura versione.
Per alcuni sistemi operativi,
la relativa versione di gawk
non supporta il comando GNU gettext
.
Per questo motivo, queste funzionalità non sono disponibili nel caso
si stia lavorando con uno di questi sistemi operativi. Siamo spiacenti.
Gli americani usano una virgola ogni tre cifre decimali, e un punto per separare la parte decimale di un numero, mentre molti europei (fra cui gli italiani) fanno esattamente l’opposto: 1,234.56 invece che 1.234,56.
I miei ringraziamenti a Bruno Haible per questo esempio.
Il comando di utilità
xgettext
che fa parte del pacchetto distribuito come
gettext
è in grado di gestire i file di tipo .awk.
Questo esempio è preso in
prestito dal manuale del comando GNU gettext
.
Questo è
un buon materiale per una gara di “Oscurità awk
”.
Forse sarebbe meglio chiamarlo “Hippy.” Meglio non indagare oltre.
Non sappiamo perché se lo aspettino, ma è così.
Grazie a Michael Brennan per questa descrizione, che abbiamo parafrasato, e per gli esempi.
C’è un saggio molto bello sul calcolo in virgola mobile di David Goldberg, “What Every Computer Scientist Should Know About Floating-Point Arithmetic,” ACM Computing Surveys 23, 1 (1991-03): 5-48. Vale la pena di leggerlo, se si è interessati a scendere nei dettagli, però richiede delle conoscenze informatiche.
È possibile che l’output sia completamente diverso, se la
libreria C presente nel sistema in uso non si conforma, per printf
,
alla regola IEEE 754
di arrotondamento al valore pari in caso di equidistanza.
Weisstein, Eric W. Sylvester’s Sequence. From MathWorld—A Wolfram Web Resource (http://mathworld.wolfram.com/SylvestersSequence.html).
L’avete voluto, tenetevelo.
Si veda la voce “cookie” nello Jargon file per una definizione di cookie, e la voce “magic cookie” sempre nello Jargon file per un bell’esempio. Si veda anche la voce “Cookie” nel Glossario. [È disponibile in rete anche una traduzione italiana dello Jargon file]
Questo succede più spesso nei sistemi MS-Windows, ma può capitare anche in sistemi di tipo Unix.
Poiché l’API usa solo funzionalità previste dal compilatore ISO C 90, non è possibile usare le macro di tipo variadico (che accettano un numero variabile di argomenti) disponibili nel compilatore ISO C 99, che nasconderebbero quel parametro. Un vero peccato!
La differenza è misurabile e indubbiamente reale. Fidatevi.
I valori
numerici creano molti meno problemi, in quanto richiedono solo una variabile
C double
(8 byte) per contenerli.
D’accordo, l’unica struttura di dati.
È anche un
“cookie,” ma gli sviluppatori di gawk
hanno preferito non abusare
di questo termine.
La versione qui
presentata è
lievemente modificata per amor di semplicità. Si veda extension/filefuncs.c
nella distribuzione gawk
per la versione completa.
In pratica, si potrebbe decidere di usare
i comandi GNU Autotools (Automake, Autoconf, Libtool, e gettext
)
per configurare e costruire le librerie necessarie. L’esposizione di come
ciò può essere fatto esula dal tema di questo Documento.
Vedi gawkextlib per i puntatori a siti Internet che permettono di accedere
a questi strumenti.
Cioè, nella maniera con cui sarebbero normalmente scritti in un testo, con le cifre più significative del numero contenute nella parte sinistra, e quelle meno significative nella parte destra della rappresentazione binaria del numero.
E la vita era semplice.
Ed è così che è nata la Campagna per l’Interpretazione Razionale degli Intervalli (in inglese, RRI [Rational Range Interpretation]). Un certo numero di strumenti GNU hanno già implementato questa modifica, o lo faranno presto. Grazie a Karl Berry per aver coniato la frase “Rational Range Interpretation”.
Si veda lo standard e le motivazioni.
L’architettura IA64 è anche nota come “Itanium”.
Ci abbiamo provato. È stata un’esperienza dolorosa.
Ecco un programma GNU che (secondo noi) è estremamente difficile da estrarre dal deposito Git e compilare. Per esempio, in un vecchio (ma ancora funzionante) PowerPC Macintosh, con il sistema operativo Mac Os X 10.5, è stato necessario scaricare e compilare una tonnellata di software, incominciando dallo stesso programma Git, per riuscire a lavorare con l’ultima versione del codice. Non è un’esperienza piacevole e, specie sui vecchi sistemi, è una notevole perdita di tempo.
Neppure partire dall’ultimo archivio tar
compresso è stata una
passeggiata: i manutentori avevano eliminato i file compressi in formato
.gz e .bz2 sostituendoli con file di tipo .tar.xz.
Bisognava quindi per prima cosa scaricare e compilare xz
Un ramo (non più presente) creato da uno degli altri sviluppatori, e che non includeva i file generati.
Una struttura di dati fondamentale
all’interno di gawk
.
I simboli sono le variabili e le funzioni
definite all’interno di gawk
. Accedere a questi simboli da parte
di codice esterno a gawk
caricato dinamicamente al momento
dell’esecuzione è problematico in ambiente MS-Windows.
I programmi compilati sono normalmente scritti in linguaggi di programmazione di livello più basso, come C, C++, o Ada, e quindi tradotti, o compilati, in una forma che il computer può eseguire direttamente.