Gentoo Logo

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

Indice:

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
....etc.

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
....etc.

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

{
    #salta l'intestazione
    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



Stampa

Aggiornato il 8 gennaio 2008

Oggetto: Awk è un linguaggio molto carino e dal nome molto strano. In questo primo articolo di una serie di tre, Daniel Robbins vi farà velocemente prendere confidenza con Awk. Nella successive puntate verranno affrontati argomenti sempre più complessi, culminando nella presentazione di un'applicazione awk del mondo reale.

Daniel Robbins
Autore

Luca Martini
Traduzione

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.