GUIDA AD AWK, 10 – array

Gli array

 

Fonti:

http://ocs.unipa.it/AL/al239.htm

http://www.thegeekstuff.com/2010/03/awk-arrays-explained-with-5-practical-examples/

Awk supporta gli array. Gli array sono una sorta di estensioni delle variabili, delle variabili che memorizzano più di un valore.

La dichiarazione di un array in awk avviene nel momento in cui vi si fa riferimento. In pratica, con l’istruzione

a[2] = “ciao”

si assegna la stringa “ciao” all’elemento (detto anche indice) “2” dell’array “a”. Se l’array non esisteva, viene creato per l’occasione. Nello stesso modo, se l’elemento “2” non esisteva, viene creato all’interno dell’array.

In pratica, l’array di AWK è un insieme di elementi a cui si fa riferimento con un indice libero. Il fare riferimento a un elemento che non esiste, anche solo per leggerne il contenuto, implica la creazione di tale elemento. Come si può intuire, il riferimento a un elemento che non esiste ancora, crea tale elemento assegnandogli la stringa nulla, restituendo pertanto lo stesso valore.

Gli indici di un array sono trattati da awk come stringhe anche se sono numeri, pertanto non hanno un ordine preciso. L’ordine in cui appaiono gli elementi, dipende da AWK, e in generale dovrebbe dipendere dalla sequenza con qui questi sono stati inseriti.

Creazione di un array:

elenco[“italia”] = “roma”
elenco[“francia”] = “parigi”
elenco[3] = 345
elenco[2345] = “pippo”

Scandire gli elementi di un array:

for(i in elenco){print elenco[i]}

Vediamo in uno script come posso creare un array e leggere il contenuto degli elementi: $ vim array.awk

#!/usr/bin/awk -f

BEGIN {

elenco[“italia”] = “roma” elenco[“francia”] = “parigi” elenco[3] = 345 elenco[2345] = “pippo”

for (i in elenco) { print “elenco [“i”] contiene ” elenco[i] } }

$ ./array.awk elenco [italia] contiene roma elenco [2345] contiene pippo elenco [francia] contiene parigi elenco [3] contiene 345

Se vogliamo verificare il valore di un elemento o semplicemente verificare che esista:

if (234 in prova) {
print “L’elemento prova[234] corrisponde a ” prova[234]
}
Vediamo uno script che crea l’array e poi verifica il contenuto di un elemento:

$ vim array2.awk
#!/usr/bin/awk -f
BEGIN {
elenco[italia] = "roma"
elenco[francia] = "parigi"
elenco[3] = 345
elenco[2345] = "pippo"
if (francia in elenco) {
print "L'elemento elenco[francia] corrisponde a " elenco[francia]
}
}
$ ./array2.awk
 L'elemento elenco[francia] corrisponde a parigi

L’eliminazione di un elemento di un array si ottiene con l’istruzione “delete”:

delete <array>[<indice>]

E’ possibile convertire automaticamente una stringa in un array attraverso la funzione interna “split()”. La sintassi è:

split( <stringa>, <array>[, <separatore>])

Esempio:

split( "uno-due-tre", elenco, "-" )

Crea l’array (o lo ricrea se già esistente, eliminando i precedenti elementi) “elenco” con tre elementi “uno”, “due”, “tre” e con indice (automatico) “1”, “2”, “3”. Quindi crea:

elenco[1] = “uno”
elenco[2] = “due”
elenco[3] = “tre”

Esempio array semplice:

$ cat Iplogs.txt
 180607 093423 123.12.23.122 133
 180607 121234 125.25.45.221 153
 190607 084849 202.178.23.4 44
 190607 084859 164.78.22.64 12
 200607 012312 202.188.3.2 13
 210607 084849 202.178.23.4 34
 210607 121435 202.178.23.4 32
 210607 132423 202.188.3.2 167
$ awk '{
 > Ip[$3]++;
 > }
 > END{
 > for (var in Ip)
 > print var, "access", Ip[var]," times"
 > }
 > ' Iplogs.txt
 125.25.45.221 access 1 times
 123.12.23.122 access 1 times
 164.78.22.64 access 1 times
 202.188.3.2 access 2 times
 202.178.23.4 access 3 times

Lo script lista gli ip unici e conta quante volte ricorre nel file:
$3 è l’indirizzo ip ed è usato come indice dell’array chiamato “Ip”;
per ogni linea incrementa (“++”) il valore dell’indice (ip) corrispondente;
alla fine tutti gli indici saranno ip unici e il valore corrispondente saranno le ricorrenze di quegli ip sul file.

Esempio array doppio, con indici “incrociati”:

$ vim ex2.awk
 BEGIN {
 print "IP Address\tAccess Count\tNumber of sites";
 }
 {
 Ip[$3]++;
 count[$3]+=$NF;
 }
 END{
 for (var in Ip)
 print var,"\t",Ip[var],"\t\t",count[var];
 }
$ awk -f ex2.awk Iplogs.txt
 IP Address Access Count Number of sites
 125.25.45.221 1 153
 123.12.23.122 1 133
 164.78.22.64 1 12
 202.188.3.2 2 180
 202.178.23.4 3 110

Questo script lista tutti gli ip e calcola a quanti siti ha avuto accesso:
la sezione BEGIN stampa l’header del file;
la sezione successiva ha due array aventi entrambe lo stesso indice, ovvero $3 corrispondente all’ip;
l’array “Ip” contiene come indici gli indirizzi ip e come valore le relative ricorrenze;
l’array “count” contiene come indici gli stessi ip e come valore l’ultimo campo del file ($NF) che corrisponde al numero di siti a cui i relativi ip hanno avuto accesso;
nella sezione END, alla fine stampa gli ip unici e il numero di siti a cui hanno avuto accesso.

Esempio array doppio + exp + ciclo for + condizione if

$ cat ex3.awk
 {
 date[$1]++;
 }
 END{
 for (count in date)
 {
 if ( max < date[count] ) {
 max = date[count];
 maxdate = count;
 }
}
 print "Maximum access is on", maxdate;
 }
$ awk -f ex3.awk Iplogs.txt
 Maximum access is on 210607

Lista la data in cui c’è stato il massimo numero di accessi:
l’array “date” ha la data come indice e come valore il numero di ricorrenze;
“max” è la variabile che contiene il numero di ricorrenze ed è usata per trovare la data che ha il numero di ricorrenze più alto;
“maxdate” è la variabile che contiene la data con maggior numero di accessi.

Esempio array che inverte l’ordine degli indici e valori

$ cat test9
 prima riga
 seconda riga
 terza riga
 quarta riga
$ awk '{ a[i++] = $0 } END { for (j=i-1; j>=0;) print a[j--] }' test9
 quarta riga
 terza riga
 seconda riga
 prima riga

Lo script stampa le righe (records) del file in ordine inverso (dall’ultima alla prima):
inizialmente registra tutte le righe nell’array “a”;
una volta terminato il processamento, awk esegue l’END dello script;
l’END legge l’array in ordine inverso perchè il valore di “i” arriva col numero dell’ultima riga processata incrementato di 1 (4 nell’esempio). Prova di questo è:

$ awk '{ a[i++] = $0} END { print i }' test9
 4

il ciclo for setta poi “j” come “j=i-1; j>=0″ ovvero setta “j” a “3” e pone un while fino a che “j>=0″. A questo punto l’array “a” viene letto nella forma “a[j–]” ovvero nella successione degli indici: a[3], a[2], a[1], a[0] stampando i valori dell’ultimo al primo.

Esempio array per eliminare record duplicati

$ cat test10
 prima riga
 quarta riga
 seconda riga
 terza riga
 seconda riga
 quarta riga
$ awk '!($0 in array){ array[$0]; print }' test10
 prima riga
 quarta riga
 seconda riga
 terza riga

Lo script elimina i record duplicati:
tramite l’operatore “in” verifica che il record corrente esista nell’array “a”. Essendoci un “not” logico (“!”) -> se NON esiste lo memorizza e lo stampa.

Come abbiamo già detto, gli elementi di un array in awk non sono ordinati. Ci sono funzioni di “gawk” (“asort” e “asorti”) che svolgono tale funzione di ordinamento, ma noi non ce ne occuperemo.
Qualora fosse necessario ordinare gli elementi o i valori di un array possiamo reindirizzare l’output di awk al comando “sort” della shell.
Ad esempio creiamo un array che prende i valori dall’output di “echo” e stampiamo l’array nella forma <indice>-<valore>:

$ echo "5,3,1,8,20" | awk '{split($0,arr,","); for (i in arr) printf ("%s-%d\n", i, arr[i])}'
 1-5
 2-3
 3-1
 4-8
 5-20

Ora lo vogliamo ordinare sulla base degli indici (ovviamente non cambia nulla perchè gli indici rispettano la successione di creazione, quindi sono già ordinati):

$ echo "5,3,1,8,20" | awk '{split($0,arr,","); for (i in arr) printf ("%s-%d\n", i, arr[i]) | "sort -t- -k1 -n"}'
 1-5
 2-3
 3-1
 4-8
 5-20

E ora invece sulla base dei valori:

$ echo "5,3,1,8,20" | awk '{split($0,arr,","); for (i in arr) printf ("%s-%d\n", i, arr[i]) | "sort -t- -k2 -n"}'
 3-1
 2-3
 1-5
 4-8
 5-20

Se vogliamo fare un programmino che ci ordini del valori che diamo come argomento e che li renda disponibili nella variabile esportata “ARRAYSORTED”:

$ vim ordina.awk
#!/bin/bash

export ARRAYSORTED=`echo $1 |

awk '{split($0,arr,","); for (i in arr) print arr[i] | "sort -k1 -n"
}'`

echo $ARRAYSORTED
--------------------

$ ./ordina.awk 3,2,78,32,8,6,100
2 3 6 8 32 78 100

————————————

INDICE

Lascia una risposta

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>