Disclaimer :
La versione originale di questo articolo è stata pubblicata da IBM
developerWorks ed è di proprietà di Westtech Information Services. Questo
documento è una versione aggiornata dell'articolo originale, e contiene
numerosi miglioramenti apportati dal Gentoo Linux Documentation team.
Questo documento non è mantenuto attivamente.
|
Awk per esempi, Parte 1
1.
Un'introduzione ad un grande linguaggio dal nome bizzarro
In difesa di awk
In questa serie di articoli vi trasformerò in abili programmatori awk. Lo devo
ammettere, awk non ha un nome molto carino o particolarmente alla moda, e il
nome della versione GNU di awk, chiamata gawk, suona francamente strano. Coloro
che non conoscono questo linguaggio, possono sentire il nome "awk" e pensare a
codice così ingarbugliato e antiquato da portare anche il più sapiente guru
di UNIX sull'orlo della follia (facendolo continuamente guaire "kill -9" mentre
corre verso la macchina del caffè).
Di sicuro, awk non ha un gran nome. Ma è un grande linguaggio. Awk è
orientato al text processing e alla generazione di report, ma possiede anche
caratteristiche che permettono di programmare seriamente. E, a differenza di
altri linguaggi, la sintassi di awk è familiare, e prende in prestito alcune
delle migliori caratteristiche di linguaggi come il C, python, e bash (anche
se, tecnicamente, awk è stato creato prima di python e bash). Awk è uno di
quei linguaggi che, una volta imparati, diventeranno una delle migliori frecce
al vostro arco.
Il primo codice awk
Codice 1.1: Il primo awk |
$ awk '{ print }' /etc/passwd
|
Dovreste vedervi apparire di fronte ai vostri occhi il contenuto del vostro
file /etc/passwd. Ora, ecco una spiegazione di cosa ha fatto awk.
Quando abbiamo chiamato awk, abbiamo specificato come file di ingresso
/etc/passwd. Quando abbiamo eseguito awk, esso ha valutato il
comando print per ciascuna linea del file /etc/passwd, in ordine.
L'uscita è diretta allo standard output e otteniamo un risultato identico a
eseguire cat /etc/passwd.
Ecco ora la spiegazione del blocco { print }. In awk, le parentesi graffe sono
usate per raggruppare blocchi di codice, come in C. All'interno del nostro
blocco di codice, abbiamo solamente il comando print. In awk, quando un comando
print appare da solo, l'intero contenuto della riga corrente viene stampato.
Codice 1.2: Stampare la linea corrente |
$ awk '{ print $0 }' /etc/passwd
$ awk '{ print }' /etc/passwd
|
In awk, la variabile $0 rappresenta la linea corrente per intero, quindi print
e print $0 fanno esattamente la stessa cosa.
Codice 1.3: Riempire lo schermo con un po' di testo |
$ awk '{ print "hiya" }' /etc/passwd
|
Campi multipli
Codice 1.4: print $1 |
$ awk -F":" '{ print $1 $3 }' /etc/passwd
halt7
operator11
root0
shutdown6
sync5
bin1
|
Codice 1.5: print $1 $3 |
$ awk -F":" '{ print $1 " " $3 }' /etc/passwd
|
Codice 1.6: $1$3 |
$ awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' /etc/passwd
username: halt uid:7
username: operator uid:11
username: root uid:0
username: shutdown uid:6
username: sync uid:5
username: bin uid:1
|
Script esterni
Codice 1.7: Script di esempio |
BEGIN { FS=":" }
{ print $1 }
|
La differenza tra questi due metodi sta nel come settiamo il separatore di
campo. In questo script, il separatore di campo è specificato all'interno del
codice stesso (settando la variabile FS), mentre il nostro esempio precedente
settava FS passando l'opzione -F":" ad awk tramite la linea di comando. È
generalmente meglio settare il separatore di campo all'interno dello script
stesso., semplicemente perché in questo modo avete un comando in meno da
inserire sulla linea di comando. Parleremo della variabile FS più
dettagliatamente nel seguito di questo articolo.
I blocchi BEGIN e END
Normalmente, awk esegue ciascun blocco del codice del vostro script una volta
per ogni riga del vostro file di input. Tuttavia, ci sono molti casi in cui
potreste aver bisogno di eseguire un codice di inizializzazione prima che awk
cominci a elaborare il testo contenuto nel file di ingresso. Nell'esempio
precedente abbiamo usato un blocco BEGIN. Poiché il blocco BEGIN viene
valutato prima che awk cominci a elaborare il file di ingresso, questo è un
ottimo posto per inizializzare la variabile FS (field separator,
separatore di campo), stampare un'intestazione, o inizializzare altre variabili
globali che userete più avanti nel programma.
Awk fornisce anche un altro blocco speciale, chiamato il blocco END. Awk esegue
questo blocco dopo che tutte le righe del file di ingresso sono state
elaborate. Tipicamente, il blocco END è usato per realizzare calcoli finali o
per stampare dei riepiloghi che dovrebbero apparire alla fine del dati in
uscita.
Espressioni regolari e blocchi
Codice 1.8: Espressioni regolari e blocchi |
/foo/ { print }
/[0-9]+\.[0-9]*/ { print }
|
Espressioni e blocchi
Codice 1.9: fredprint |
$1 == "fred" { print $3 }
|
Codice 1.10: root |
$5 ~ /root/ { print $3 }
|
Istruzioni condizionali
Codice 1.11: if |
{
if ( $5 ~ /root/ ) {
print $3
}
}
|
Entrambi gli script funzionano in modo identico. Nel primo esempio,
l'espressione booleana è situata al di fuori del blocco, mentre nel secondo
esempio, il blocco è eseguito per ogni riga di input, e realizziamo
selettivamente il comando di print usando un'istruzione if. Entrambi i metodi
sono disponibili e potete scegliere quello che risulta più compatibile con le
altre parti del vostro script.
Codice 1.12: if if |
{
if ( $1 == "foo" ) {
if ( $2 == "foo" ) {
print "uno"
} else {
print "one"
}
} else if ($1 == "bar" ) {
print "two"
} else {
print "three"
}
}
|
Codice 1.13: if |
! /matchme/ { print $1 $3 $4 }
|
Codice 1.14: if |
{
if ( $0 !~ /matchme/ ) {
print $1 $3 $4
}
}
|
Entrambi gli script mandano in uscita solo le righe che non contengono la
sequenza di caratteri matchme. Ancora una volta, potete scegliere il metodo che
funziona meglio col vostro codice. I due metodi fanno la stessa cosa.
Codice 1.15: Stampare i campi uguali a foo e bar |
( $1 == "foo" ) && ( $2 == "bar" ) { print }
|
Questo esempio stampa solo quelle righe in cui il primo campo è uguale a foo e
il secondo campo è uguale a bar.
Variabili numeriche!
Nel blocco BEGIN, inizializziamo la nostra variabile intera x a zero. Poi, ogni
volta che awk incontra una linea vuota, eseguirà l'istruzione x=x+1,
incrementando x. Quando tutte le righe sono state state elaborate, il blocco
END stamperà un riepilogo, specificando il numero di linee vuote incontrate.
Variabili stringa
Codice 1.16: Esempio di campo |
2.01
|
Codice 1.17: 1.01x$( )1.01 |
{ print ($1^2)+1 }
|
Facendo un po' di esperimenti, vedrete che se una particolare variabile non
contiene un numero valido, awk tratterà tale variabile come uno zero numerico
ogni volta che dovrà valutarla come espressione matematica.
Un sacco di operatori
Un'altra cosa carina di awk è la sua dotazione di operatori
matematici. Oltre agli operatori standard per addizione, sottrazione,
moltiplicazione e divisione, awk ci permette di usare l'operatore di
esponenziazione "^" precedente mostrato, l'operatore di modulo (resto della
divisione intera) "%" e un sacco di altri utili operatori di assegnamento
mutuati dal C.
Fra questi possiamo citare il pre- ed il post-incremento/decremento (i++,
--foo), gli operatori standard che combinano le principali operazioni
aritmetiche con l'assegnamento (a+=3, b*=2, c/=2.2, d-=6.2). Ma questo non è
tutto -- abbiamo anche i comodi operatori di modulo/esponente sempre combinati
con l'assegnamento( a^=2, b%=4 ).
Separatori di campo
Awk ha un suo proprio bagaglio di variabili speciali. Alcune di esse vi
permettono di mettere a punto il funzionamento di awk, mentre altre possono
essere lette per ricavare importanti informazioni sui dati in ingresso. Abbiamo
già menzionato una di queste variabili speciali, FS. Come ricordato
precedentemente questa variabile permette di definire la sequenza di caratteri
che awk si aspetta di trovare fra i campi. Quando usavamo
/etc/passwd come file di input, FS era settata a ":". Anche se in
questo caso un solo carattere (":") è bastato al nostro scopo, FS ci permette
ancora più flessibilità.
Codice 1.18: Un altro separatore di campo |
FS="\t+"
|
Sopra abbiamo usato il carattere speciale delle espressioni regolari "+", che
significa "uno o più occorrenze del carattere precedente".
Codice 1.19: Settare FS al carattere di spaziatura |
FS="[[:space:]+]"
|
Sebbene questo assegnamento funzioni, esso non è necessario. Perché? Perché,
di default, FS è settato ad un
singolo carattere di spaziatura, che awk
interpreta come "uno o più spazi o tabulazioni." In
questo esempio particolare, il valore
predefinito di FS era esattamente identico al valore che si voleva assegnare.
Codice 1.20: Esempio di separatore di campo |
FS="foo[0-9][0-9][0-9]"
|
Numero di campi
Codice 1.21: Numero di campi |
{
if ( NF > 2 ) {
print $1 " " $2 ":" $3
}
}
|
Numero di record
Codice 1.22: Numero di record |
{
if ( NR > 10 ) {
print "ok, ora arriva il reale contenuto"
}
}
|
Awk fornisce altre variabili speciali che possono essere usate per vari
scopi. Parleremo di queste variabili negli articoli successivi.
Siamo arrivati alla fine della nostra prima esplorazione di awk. Con il
proseguire degli articoli, vi dimostrerò funzionalità più avanzate di awk e
finiremo con un'applicazione awk presa dal mondo reale. Nel frattempo, se siete
desiderosi di saperne di più, date un'occhiata alle risorse proposte di
seguito.
2.
Risorse
Link utili
|