AWK: un amico per aiutarci con i log files (e non solo)

AWK – Un po’ di storia…

La storia di AWK è lunga, in termini informatici lunghissima. E’ il 1977 quando Alfred Aho, Peter Weinberger e Brian Kerningham (dalle iniziali dei tre il suo nome) presentano questo anomalo linguaggio interpretato, utilissimo per processare files di testo strutturati e generare report.

awk-book
la copertina del libro originale su AWK

AWK è un più di un semplice tool e forse qualcosa meno di un completo linguaggio di programmazione, nel senso moderno del termine (anche se è un completo linguaggio di programmazione). Mostra la sua piacevolezza e potenza come filtro in una pipeline, dove si fa preferire alla criptica sintassi di SED e consente di evitare di scomodare il Perl.

 

 

AWK, nelle sue varianti (la più diffusa è quella a cura della Free Software Foundation, GAWK) è disponibile praticamente per qualsiasi sistema operativo possiate immaginare. Sì, anche il DOS.

Ma cosa rende insieme unico, attraente e insieme così poco conosciuto questo strumento che ha per logo un simpatico pinguino, 14 anni prima di Linux?

La “strana” logica di AWK

Per spiegarlo inizierò dal concetto di base su cui, a mio avviso, si fonda la corretta comprensione del linguaggio. No, non è un esempio di “Hello World”…

La logica di AWK è semplice, ma potente. Come quasi tutti gli strumenti nati in ambito UNIX, AWK ragiona in termini di linee di testo. Processa una linea alla volta applicando delle regole, composte da:

pattern {azione}

sia il pattern che l’azione sono opzionali.
Se manca il pattern, l’azione viene applicata a tutti i record.
Se a mancare è l’azione, il record che corrisponde al pattern è scritto sullo standard output (quindi di default sullo schermo).

Quello che AWK fa, in pratica, è leggere il testo una riga alla volta, separarlo in campi definiti da un delimitatore (modificabile), assegnare i valori a delle variabili predefinite ($1,$2,$3…) e applicare le regole indicate dal programmatore.

AWK può ricevere il testo da processare attraverso una pipe, oppure può presentarsi sotto forma di script, invocabile con awk -f nomescript.awk e processare il file o i files indicato/i.

Un esempio vale mille parole

Complicato? Un esempio mostrerà tutta la semplicità del linguaggio.
Immaginiamo di avere un file di testo chiamato amici.txt:

Aldo, 23
Carlo, 28
Mario, 35
Piero, 17
Stefano, 30
Umberto, 29

Ok, l’esempio è senza senso, ma serve solo per illustrare l’utilizzo di AWK…

se io passo l’output di cat amici.txt tramite una pipe ad AWK in questo modo:

cat amici.txt | awk '/Stefano/ {print $2}'

oppure processo amici.txt direttamente così:

 awk '/Stefano/ { print $2 }' amici.txt

il risultato a schermo sarà… 30

Ricordate? pattern e azione. Se e quando AWK trova “Stefano” stampa il secondo campo, cioè il numero 30. La virgola è il delimitatore di default di AWK. Se avessi avuto come delimitatore il punto e virgola avrei potuto scrivere:

cat amici.txt | awk -F; "/Stefano/ {print $2}"

Giochiamo un po’… se avessi scritto:

cat amici.txt | awk "/Stefano/ {print $2, $1}"

il risultato sarebbe stato:

30, Stefano

iniziate a capire l’utilità di questo strumento, ad esempio per processare al volo files csv?

un po’ di esempi banalissimi:

{print $1} stampa il primo campo di ogni linea di un file

 /pippo/   stampa tutte le linee che contengono pippo

lenght($0) < 40 stampa tutte le linee che contengono meno di 40 caratteri

e via dicendo…

Come dicevo, posso usare AWK in una pipe, ma posso anche scrivere dei veri e propri programmi, di maggiore complessità.

Uno script AWK è fatto di 3 parti (al massimo…)

Un programma AWK consta di tre parti: le assegnazioni e le operazioni per così dire preliminari, tipo l’assegnazione di variabili globali, che troveranno posto qui:

BEGIN {
...
}

(ovviamente questa sezione è del tutto opzionale)

poi seguiranno le istruzioni secondo la logica /pattern/ {azione} che abbiamo già visto.
I pattern possono essere combinati con gli operatori booleani || (OR), && (AND) e ! (NOT).
Le azioni consistono di uno o più comandi, funzioni o assegnazione di variabili, separate da a capo o da punti e virgola e contenute dentro le parentesi graffe {}.
I comandi possono essere assegnazione di variabili (o array), comandi di stampa, funzioni interne, comandi per controllare il flusso dell’operazione.

e per finire, anch’esso opzionale:

END {
...
}

dove andrò, ad esempio, a stampare il risultato finale di una mia elaborazione (potrei aver sommato tutti i valori presenti in $2 di tutte le righe in una variabile e dentro END andrò a stampare, alla fine, il valore totale di quella variabile…).

Se salvo il mio script con un nome, ad esempio pippo.awk, posso eseguirlo con:

awk -f pippo.awk

o più comodamente iniziando il mio script con:

  #!/usr/bin/awk -f

e poi rendendolo eseguibile.

Le variabili possono essere assegnate nel modo più semplice possibile:
PIPPO = “dsajsakdsk”

Gli arrays possono essere creati con la funzione split:

split (stringa,array[,separatore])

…il resto lo lascio alla vostra fantasia!


In libreria, per approfondire

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *