giovedì 15 marzo 2012

Un po' di AWK. Quarta parte.

Salve!

Col post di oggi continuiamo il sequel su AWK!
Partiamo sempre dalla solita tabella. Solo che oggi l'ho modificata un pò per ragioni pratiche.

Nino;22.2;=>;promosso
Luca;17.6;=>;respinto
Sara;25.6;=>;promosso
Gigi;24.2;=>;promosso
Toni;29.4;=>;promosso
Sasà;17.6;=>;respinto
Enzo;29.8;=>;promosso
Bice;25.6;=>;promosso
Rita;24.2;=>;promosso

Qualora non abbiate letto le "puntate precedenti", vi invito a farlo. Ritengo, infatti, che i post precedenti su AWK siano necessari per comprendere i successivi. Li trovate qui:
Un pò di AWK. Prima parte.
Un pò di AWK. Seconda parte.
Un pò di AWK. Terza parte.

Non ho grosse pretese e non sono neanche bravissimo con questo linguaggio. Semplicemente mi affascina, perciò condivido con voi quello che so. Una cosa però la voglio ribadire. Per una semplice deformazione professionale, cerco di capire sempre il perchè delle cose, come funzionano, cosa c'è dentro, qual è la logica che ci sta dietro. E spero tanto che questo mio modo di fare trapeli da 'ste pagine web.  Fine del discorso autoreferenziale. ;)

Oggi parlerò solo di FS, che non significa affatto ferrovia dello stato.

FS, Input Field Separator,  è una delle variabili built-in di awk.

Procediamo come al solito. Salviamo la tabella nella directory Documenti e nominiamola "tabella.txt"

FS è un separatore di campi. In genere non si scrive perchè, nella stragrande maggioranza dei casi, i campi sono separati tra loro da spazi e il  valore predefinito della variabile FS è proprio lo spazio. Può succedere però, come nel nostro caso, che i campi siano separati non da spazi, bensì da punti e virgole ";". In tal caso bisogna cambiare il suo valore predifinito. "Ammazza che fatica". Se non avete capito un tubo, siete giustificati. Cercherò di essere più chiaro negli esempi. :)

Se non dicessi ad awk che FS=";", ecco cosa succederebbe:

Apriamo il terminale e spostiamoci nella directory Documenti

$ cd Documenti

e diamo questa riga di comando:

$ awk '{print $1}' tabella.txt

Come ormai sapete, $1 è il primo campo. Nel nostro caso, l'elenco dei nomi. Stando alla norma, awk dovrebbe stampare solo ciò che gli abbiamo chiesto, cioè l'elenco dei nomi e solo quello. E invece...

Nino;22.2;=>;promosso
Luca;17.6;=>;respinto
Sara;25.6;=>;promosso
Gigi;24.2;=>;promosso
Toni;29.4;=>;promosso
Sasà;17.6;=>;respinto
Enzo;29.8;=>;promosso
Bice;25.6;=>;promosso
Rita;24.2;=>;promosso

...ci stampa tutta la tabella! Perchè? Perchè AWk pensa che i vari campi siano divisi da spazi e, dato che non ne trova, fa coincidere i campi con i record. Che casino! Come se avessimo scritto:

$ awk '{print $0}' tabella.txt

Ricordate qual è il significato di $0, vero?

Per ovviare a questo problema (mi riferisco al problema di awk che confonde campi con record e non al fatto che ricordiate o meno il significato di $0) basta scrivere così:

$ awk -F ";" '{print $1}' tabella.txt

Oppure scrivere così:

$ awk 'BEGIN {FS=";"} {print $1}' tabella.txt

Oppure si può scrivere così:

$ cat tabella.txt | awk -F ";" '{print $1}'

Ma lasciamo stare come si potrebbe scrivere! L'importante è che sia chiaro aver capito il compito della variabile FS e come si cambia il suo valore predefinito.

L'output in tutti i casi è il seguente:

Nino
Luca
Sara
Gigi
Toni
Sasà
Enzo
Bice
Rita

Ma non è finita mica qui!

Può succedere infatti che i separatori di campo, per la stessa tabella, siano tanti e diversi!

Nino,22.2;=>,promosso
Luca,17.6;=>,respinto
Sara,25.6;=>,promosso
Gigi,24.2;=>,promosso
Toni,29.4;=>,promosso
Sasà,17.6;=>,respinto
Enzo,29.8;=>,promosso
Bice,25.6;=>,promosso
Rita,24.2;=>,promosso

Nella nuova tabella di cui sopra ho aggiunto anche la virgola ","

Come farò, questa volta, a dire ad awk di stamparmi l'elenco dei nomi e le rispettive medie?

Ecco fatto!

$ cat tabella.txt | awk -F "[;,]" '{print $1,$2}'

Output:

Nino 22.2
Luca 17.6
Sara 25.6
Gigi 24.2
Toni 29.4
Sasà 17.6
Enzo 29.8
Bice 25.6
Rita 24.2

Come potete notare, questa volta, ho assegnato alla variabile FS sia il punto e virgola che la virgola.

Avete mai dato un'occhiata al file di sistema /etc/passwd?

$ cat /etc/passwd

Chiediamo ad awk di stamparci tutti gli utenti che usano bash come shell predifinita! Da notare che qui il separatore di campo che ci interessa è questo: ":" (i due punti).

$ cat /etc/passwd | awk 'BEGIN { FS=":" } $7 ~ "/bin/bash" { print $1 }'

ma si può scrivere anche così:

$ awk -F ":" '$7~"/bin/bash" {print $1}' /etc/passwd

Nel mio caso l'output è il seguente:

root
bit3lux

Qui ci sarebbe anche un "costrutto nuovo", $7 ~ "/bin/bash", ma ne parleremo la prossima volta. Dovrò riprendere pure il discorso su FS. Ancora non ho finito. :D

Ciao :)

1 commento:

  1. Sei davvero bravo e la tua passione la trasmetti anche nel tuo modo di scrivere, grazie mille!!!

    RispondiElimina