Gawk

AWK o GAWK (versione GNU identica a AWK) sono dei programmi per manipolare il testo, per esempio per cancellare o aggiungere o sostituire parole o frasi. Il funzionamento di AWK e' molto complesso e per maneggiarlo bene e' necessario sapere molto bene come funziona.

Quando si lancia awk bisogna specificare prima del file (o i files) da manipolare, il tipo di ricerca da fare (chiamata generalmente PATTERN) e l'azione da compiere (chiamata generalmente ACTION). Se manca l'action verra' tutto mandato sullo schermo (standard output); se manca il pattern, verra' eseguita l'action su tutte le righe; se mancano tutti e due allora dara' errore.

Si puo' anche eseguire awk senza dare un file di input, in questo caso sara' analizzato tutto quello che digiterete sulla tastiera finche' non premerete CTRL+d.

Se il pattern e l'action sono molto lunghi, potete usare un file dove scrivere tutti i comandi e poi dire ad awk di leggere da li' i comandi, usando l'opzione -f:

 $ awk -f istruzione file1 file2 file3

Dare istruzioni

Immaginiamo di volere cercare la parola Gio in un file, allora bisogna scrivere:

 $ awk '/Gio/ {print $0}' file
 Giorgio Ibano
 Giovanni Catalano
 Giorgia Cardia
 Giovanna Irni

Come si vede tra gli apici c'e' sia il pattern, che e' un'espressione regolare, che l'action (stampare nello standard output). E' possibile dare piu' comandi:

 $ awk '/Gio/ {print $0} /r/ {print $0}' file
 Giorgio Ibano
 Giovanni Catalano
 Giorgia Cardia
 Giovanna Irni
 Giorgio Ibano
 Giorgia Cardia
 Giovanna Irni 

Come si vede dall'esempio alcuni nomi sono stati ripetuti perche' ripettavano le condizioni di entrambi i pattern.

Se si usano le variabili, esse sono inizializzate sempre a zero:

 $ awk '/Gio/ {print $0} /Gio/ {somma = somma +1} END {print somma}' file
 Giorgio Ibano
 Giovanni Catalano
 Giorgia Cardia
 Giovanna Irni
 4

La parola END significa che l'azione va eseguita una volta raggiunta la fine del file.

ATTENZIONE! Le variabili sono case sensitive ("somma" e "SOMMA" sono 2 variabili diverse).

Records e fields

Awk suddivide l'input in RECORDS e FIELDS (campi), ogni record e' suddiviso dall'altro da un carattere che di default e' nuova linea, quindi ogni linea e' un record. Per cambiare questa impostazione basta impostare la variabile RS con il carattere che vogliamo rappresenti la divisione fra i vari records. GAWK (quello dentro linux) permette di utilizzare un'intera espressione regolare come separatore dei records, e non solo un carattere. Esempio:

 $ awk 'BEGIN { RS = "/" } { print $0 }' listadellaspesa

Abbiamo usato il comando BEGIN, che fa fare un comando solo all'inizio della lettura del file, e non ad ogni records, perche' una volta impostata una variabile, se non ci sono motivi per cambiarla, essa rimane la stessa per tutto il file.

Il numero di records analizzati viene immesso nella variabile NR (che cambia ad ogni record).

Per sapere il numero di field (campi) in ogni record c'e' la variabile NF. I campi sono divisi tra di loro dagli spazi vuoti, ma si puo' cambiare anche questa impostazione. Ogni campo si indica col simbolo del dollaro ($), per esempio il secondo campo si indica con $2. L'ultimo campo si puo' sempre indicare con la variabile $NF, mentre con la variabile $0 si indica tutta la riga (tutti i campi).

Dopo il simbolo del dollare ci deve essere sempre un numero, quindi awk elabora quello che segue il dollaro fino ad ottenere un numero, per esempio:

 $(2+2) = $4 
 $(2*4) = $8 
 $NF = $8, se ci sono otto campi nel record 

E' possibile imporre anche il valore di NF (per esmpio $NF=3) per considerare solo i primi NF (3) campi.

Separatori in uscita

Il separatore dei fields in uscita e' lo spazio, ma puo' essere cambiato con un altro carattere (o qualunque cosa vogliate) impostando la variabile OFS:

 gawk 'BEGIN {  OFS= ":" }{ print $0 }' listadellaspesa
 mele:pere
 pane:pizza:pasta
 carne

Anche il separatore dei fields e' mofidicabile attraverso la variabile ORS:

 gawk 'BEGIN {  OFS= ":" , ORS=","}{ print $0 }' listadellaspesa
 mele:pere,pane:pizza:pasta,carne

Separatori non identificati da caratteri!

Puo' succedere di dover analizzare dei file dove, per esempio, il separatore dei campi (fiels) e' uno o piu' spazi, quindi tutto procede bene; ma alcuni campi sono macanti e vi sono degli spazi vuoti, per esmpio il seguente file:

 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    26Jun91 15days     46     46  wnewmail

Se guardate con attenzione dei campi sono vuoti, in questo caso alcune operazioni sui campi, che dovrebbero essere sette, vengono sballate perche' alcune righe hanno solamente sei o cinque campi.

Per fortuna si puo' definire la lunghezza del campo, con la variabile FIELDWIDTHS, nel nostro caso basta imporre

 FIELDWIDTHS=9 6 10 6 7 7 35 

e tutti i campi verranno automaticamente riconosciuti bene, perche' il primo campo sara' composto dai primi 9 caratteri della riga, il secondo dai successivi 6 e cosi' via.

Cambiare il contenuto di un file

Se vogliamo cambiare un campo, basta assegnarli un valore come ad una variabile. Pensiamo ad un file che ha messi in colonna dei numeri:

 2  10 25 12
 7  9  32 42
 66 37 24 21
 ...

E pensiamo che dobbiamo sottrarre 10 alla terza colonna:

 $ gawk '{  sottrazione = $3 - 10;  print $3, "sottraendo 10 uguale a", sottrazione }' file
     -| 25 sottraendo 10 uguale a 15
     -| 32 sottraendo 10 uguale a 22
     -| 24 sottraendo 10 uguale a 14
     ...

Ora se volessimo fare un file (file2) identico al precedente (file), ma con i numeri della terza colonna diminuiti di dieci unita', basterebbe scrivere:

 $ gawk '{$3 = $3 -10; print $0}' file > file2

Inoltre e' possibile aggiungere field, guardate questo esempio (sempre riferito al nostro file):

 $ gawk '{$5 = $1 + $2 + $3 + $4; print $0}' file
 2  10 25 12 49
 7  9  32 42 90
 66 37 24 21 148

E' stato aggiunto un quinto campo pari alla somma dei precedenti campi (field)

Stampare su piu' file contemporaneamente

In certi casi diventa importante scrivere un risultato su un file ed un altro risultato su un altro file, per esempio una lista dei nomi ed una lista dei numeri di telefono:

 $ gawk '{ print $2 > "lista-numeriditelefono"; print $1 > "lista-nomi" }' lista

Comparazione

Per comparare si usano i soliti simboli (attenzione a quelli per le espressioni regolari):

  • X<Y : Vera se X e' minore di Y.
  • X>Y :Vera se X e' maggiore di Y.
  • X<=Y :Vera se X e' minore o uguale a Y.
  • X>=Y :Vera se X e' maggiore o uguale a Y.
  • X==Y :Vera se X e' uguale a Y.
  • X!=Y :Vera se X e' diverso da Y.
  • X ~ Y:Vera se X corrisponde all'espressione regolare Y.
  • X !~ Y :Vera se X non corrisponde all'espressione regolare Y.
  • X && Y :Vera se e' vera X e Y.
  • X || Y:Vera se e' vera X o Y.
  • !X :Vera se e' falsa X.

Espressioni condizionali

La piu' semplice e criptica espressione e' costituita dalla seguente forma:

 CONDIZIONE ? azione_se_vera : azione_se_falsa 

Esempio:

 x > y ? print "x maggiore y" : print "y maggiore x"

oppure

 x == y ? print "x uguale y" : print "y diverso x"

If...Then...Else

Il costrutto "if...Then..else" segue la seguente sintassi:

 if ((x % 2) == 0)  print "x e` pari" ; else "x e` dispari"

Il punto e virgola e' importante.

While

Il costrutto WHILE serve a ripetere un'azione finche' una condizione continua a rimanere vera, per esempio:

 $ gawk '{i=1 ; while (i<=3) {print i  ; i = i + 1} }' file
   1
   2
   3

Questo e' quelo che succede se il file e' composto da un'unica riga.

Esiste il comando BREAK per uscire dal ciclo.

Do...While

Diversamente da WHILE, il construtto "DO...WHILE" controlla se la condizione e' vera dopo averla eseguita almeno una volta. Esempio:

 $ gawk '{i=0; do {print "i e` uguale a ", i , " , ma questa scritta appare lo
          stesso!"} while (i > 10)}' file
 i e` uguale a  0 , ma questa scritta appare lo stesso!

Questo e' quelo che succede se il file e' composto da un'unica riga.

Esiste il comando BREAK per uscire dal ciclo.

For

Il ciclo FOR i bravi programmatori non lo usano, perche' sanno che si tratta di un ciclo WHILE scritto in un altro modo, pero' tante volte il ciclo for semnra piu' semplice quando si legge il codice. La sintassi di FOR e' la seguente:

 $ gawk 'begin {print "La tabellina del 2"}
        {for ( i=0 ; 1 <= 20; i = i+2) print i}' file
   La tabellina del 2
   0
   2
   4
   6
   8
   10
   12
   14
   16
   18
   20

Questo e' quelo che succede se il file e' composto da un'unica riga.

Esiste il comando BREAK per uscire dal ciclo.

Continue

Il comando continue serve per passare immediatamente all'iterazione successiva, saltando tutte le righe di codice dopo il comando CONTINUE all'interno del ciclo.

Next

Il comando next serve a processare il recod successivo a quello in analisi.

Nextfile

Similmente a Next, NEXTFILE fa eseguire il file succesivo nella eventuale lista di file passati al comando gawk.

Se non ci sono piu' file il comando NEXTFILE chiude l'esecuzione di gawk.

Exit

Il comando EXIT fa chiudere il programma gawk.

Altre variabili predefinite

La variabile FILENAME contiene sempre come valore il nome del file che si sta processando.

La variabile FNR contiene il numero di record attuale (quindi generalmente la riga che si sta processando).

Matrici (array)

Le matrici in gawk non hanno una dimensione fissa, quindi e' possibile ingrandirle o rimpicciorirle a piacimento. Ogni elemento di una matrice puo' essere identificato da un numero (generalmente partendo da 0) o da una parola, esempio:

 Matrice "animali":
 +----+-----+----+-----+
 |Cane|Gatto|Topo|Pesce|
 +----+-----+----+-----+
    0    1     2    3

 Matrice "piante":
 +---------+---------+-------+
 | Giglio  | Edera   |  Pino |
 +---------+---------+-------+
   "primo"   "verde"   "ciao"

Una matrice puo' essere monodimensionale (animali[0]=Cane), a due dimensioni (casella[2,E]=Acqua) o a piu' dimensioni (calciatori[Lazio,10,riserva]=nongiocamai).

Per dichiarare una matrice si usa la seguente espressione:

 matrice[1]="ciao mondo!"

E' possibile eliminare un elemento della matrice e il suo riferimento utilizzando il comando DELETE:

 delete cani[1]

In questo modo l'elemento cani[1] non esiste piu'. Pero' la variabile cani rimane una matrice e non e' possibile assegnarli un valore con il comando cani = "pastore tedesco" .

Se ci divertiamo ad assegnare una serie di nomi esotici per identificare gli elementi poi potremmo non essere piu' in grado di sapere quanti sono o di ordinarli o esaminarli. Per fortuna esiste il comando la funzione ASORT(matrice), che ci dice il numero di elementi di una matrice e li ordina cominciando a 1. Esempio:

 cani["dalmata"]="bianco e nero"
 cani["levriero"]="marrone"
 asort(cani) 
 print cani[1]
 bianco e marrone

Funzioni

Ecco una lista delle funzioni disponibili in gawk.

Matematiche

  • int(x) :Restituisce il valore intero (trincato) di x.
  • sqrt(x) :Restituisce la radice quadrata di x.
  • exp(x) :Restituisce la potenza di x.
  • log(x) :Restituisce il logaritmo di x.
  • sin(x) :Restituisce il seno di x, pero' x deve essere espresso in radianti.
  • cos(x) :Restituisce il coseno di x, pero' x deve essere espresso in radianti.
  • atan2(x/y) :Restituisce l'arco tngente di x/y, espresso in radianti.
  • rand() :Da' un numero casuale tra zero e uno, ma mai zero.

Stringhe

  • index(frase, parola) :Restituisce il numero del carattere dove comincia la parola nella frase data, esempio:
 $ awk 'BEGIN { print index("peanut", "an") }'
 -| 3
  • length(stringa) :Restituisce la lunghezza della stringa. Se non mettete nulla nelle parentesi vi restistuisce la lunghezza della stringa ($0).
  • split(stringa, matrice,(separatore)) :Divide una stringa in pezzi definiti dal separatore (se non indicato si intende lo spazio), e li mette nella matrice indicata:
  $ gawk '{split("cul-de-sac", a, "-"); print a[1], "\n", a[2], "\n", a[3]}' file
   cul
   de
   sac
  • toupper(stringa) :Converte la stringa in caratteri tutti maiuscoli.
  • tolower(stringa) :Converte la stringa in caratteri tutti minuscoli.

Tempo

  • systime() :Restituisce il tempo timestamp, cioe' in secondi passati dal 1/1/1970 alle 00:00:00 UTC.
  • mktime("data") :Restituisce la data nel formato timestamp formato la data deve essere scritta nel seguente formato: YYYY MM DD HH mm SS.
  • strftime((FORMATO),(TIMESTAMP)) :Restituisce la data attuale nel formato Sun Nov 14 13:44:57 CET 2004 se non sepcificato altrimenti nella variabile formato. Se viene indicata una data timestamp verra' indicata nel formato indicato quella data. Il formato si spefica nel seguente modo:
  • %a :Giorno della settimana abbreviato.
  • %A :Giorno della settimana.
  • %b :Mese dell'anno abbreviato.
  • %B :Mese dell'anno.
  • %c :Data nel formato predefinito.
  • %C :Il secolo.
  • %d :Il giorno del mese in formato numerico (01-31).
  • %D :La data nel formato `%m/%d/%y.
  • %e :Il giorno del mese in formato numerico ma con uno spazio al posto dello zero davanti ( 1-31).
  • %F :La data in formato ISO-8601, cioe' m-%d.
  • %g :Il resto dell'anno diviso cento, e' come dire le ultime due cifre dell'anno, ma si usano gli anni le settimane e i giorni ISO, quindi se per un sabato capita i primi del 2004 e' contato come un giorno della settima 53 del 2003 e quindi e' preso il 2003 come riferimento. State attenti a come lo usate...
  • %G :La settimana dell'anno secondo lo standard ISO.
  • b.
  • %H :L'ora nel formato 00-23.
  • %I :L'ora nel formato 01-12.
  • %j :Il giorno dell'anno (001-366).
  • %m :Il mese in formato numerico.
  • %M :I minuti (00-59).
  • %n :A capo (carattere ASCII LF).
  • %p :La scritta AM/PM riguardante l'ora.
  • %r :L'ora in formato da 12, equivalente a M:p.
  • %R :L'ora equivalente a M.
  • %S :I secondi.
  • %t :Il carattere TAB.
  • %T :L'ora nel formato M:%S.
  • %u :Il giorno della settimana nel formato numerico (1-7) considerando lunedi' 1.
  • %U :La settimana dell'anno considerando la domenica come primo giorno della settimana.(00-53)
  • %V :La settimana dell'anno considerando il lunedi' come primo giorno della settimana.(01-53)
  • %w :Il giorno della settimana in formato numerico, considerando la domenica come primo giorno della settimana.
  • %W :La settimana dell'anno considerando il lunedi' come primo giorno della settimana.(00-53)
  • %y :Le ultime due cifre dell'anno.
  • %Y :L'anno con quatro cifre.
  • %z :Il fuso orario nella forma +HHmm
  • %Z :Il fuso orario con una sigla.
  • % % :Per ottenere il simolo percentuale %.