GUIDA AD AWK, 11 – funzioni

Le funzioni predefinite “LENGTH”, “SPLIT” E “GETLINE”

 

– La funzione “length”:

#!/usr/bin/awk -f
1 {
 if (length($0) > max) {
 max = length($0)
 }
}
END {
 print max
}

La funzione “lenght” restituisce la lunghezza della stringa fornita come parametro. Nell’esempio viene scandito ogni record, e viene memorizzata la sua lunghezza se questa risulta superiore all’ultima misurazione effettuata. Alla fine, viene emesso il contenuto della variabile che è stata usata per annotare questa informazione.

#!/usr/bin/awk -f
length($0) > 80 { print $0 }

Questo esempio emette tutte le righe di un file di testo che superano la lunghezza di 80 caratteri.
– La funzione “split”:

http://wwwcdf.pd.infn.it/AppuntiLinux/awk_funzioni_e_array.htm

http://star-www.rl.ac.uk/docs/sc4.htx/node39.html

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

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

Per 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”

In pratica:

$ cat pippo.txt
uno-due-tre-quattro
$ awk '{split($0,splitarray,"-")}END{for (i in splitarray) print "splitarray["i"]" " = " splitarray[i];}' pippo.txt
splitarray[1] = uno
splitarray[2] = due
splitarray[3] = tre
splitarray[4] = quattro

Un altro esempio che comprende anche l’ordinamento crescente dei valori mediante “sort” della bash

$ 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

– La funzione “getline”:

La funzione predefinita di awk “getline” permette di reindirizzare il contenuto di un file nell’azione corrente. Permette in tal modo di operare con valori provenienti da più file.
Vediamo come lavora “getline” dapprima con un esempio elementare e poi con esempi via via più complessi:

$ cat file-1
A 1
B 2
C 3
$ cat file-2
D 4
E 5
F 6
$ awk '{if (3 == 3){getline < "file-1"; print $0}}' file-2
A 1
B 2
C 3

Questo primo esempio verifica la condizione sempre vera “3 == 3″ e reindirizza $0 di file-2 nell’azione corrente per poi stamparne il contenuto. In questo comando “print $0″ agisce su “file-1″ e non su file-2.

$ awk '{str1=$1; str2=$2; getline < "file-2"; print str1" \t "str2" \t "$2 }' file-1
A 1 4
B 2 5
C 3 6

In questo secondo esempio, invece, l’output contiene le due colonne di file-1 più la seconda colonna di file-2 come terza colonna.
Le prime due variabili “str1″ e “str2″ contengono la prima e la seconda colonna di “file-1″.
La funzione “getline” consente una redirezione di $2 di “file-2″, premettendo quindi di ottenere un output composto da stringhe di file diverse ($1 e $2 di file-1 e $2 di file-2).

L’output può essere a sua volta reindirizzato in un terzo file “file-3″:

$ awk '{str1=$1; str2=$2; getline < "file-2"; print str1" \t "str2" \t "$2 > "file-3"}' file-1
$ cat file-3
A 1 4
B 2 5
C 3 6

E’ facile intuire come la fuzione getline offra molteplici possibilità di interoperabilità tra file.

Ora qualcosa di un po’ più avanzato. Abbiamo due file strutturati, “compare” e “compare1″ e vogliamo che i valori siano inseriti in due array distinti (uno per file). Il nome degli array sarà uguale al nome del file origine dei dati. Il comando deve stampare i valori di entrambe gli array:

$ cat compare
A 10
B 50
C 2
D 100
E 1200
F 1500
$ cat compare1
A 15
B 5
C 3
D 500
E 900
F 1500
$ awk '{var1=$1; var2=$2; compare[var1]=var2;}{getline < "compare1"; compare1[$1]=$2;}END{for(i in compare) print i " " compare[i]; compare1[$1]=$2; for (i in compare1) print i " " compare1[i] }' compare
A 10
B 50
C 2
D 100
E 1200
F 1500
A 15
B 5
C 3
D 500
E 900
F 1500

Ora che gestiamo tutti i valori possiamo iniziare a fare qualcosa di più interessante: fare delle comparazioni. Vogliamo che awk compari i valori del primo array con i corrispondenti valori del secondo array e ci restituisca se il primo è minore, maggiore o uguale al secondo:

$ awk ‘{var1=$1; var2=$2; compare[var1]=var2;}{getline < “compare1″; compare1[$1]=$2;} END{for (i in compare){ if (compare[i] < compare1[i]){ print ” < “}; if (compare[i] > compare1[i]){ print ” > “} if (compare[i] == compare1[i]){ print ” = “} }}’ compare
<
>
<
<
>
=

Ora vogliamo che l’output sia più leggibile. Immaginiamo di avere dei listini molto lunghi che vogliamo confrontare per evidenziare i costi maggiori, minori o uguali di due distributori differenti, calcolando anche la differenza di costi:

$ awk '{var1=$1; var2=$2; compare[var1]=var2; getline < "compare1"; compare1[$1]=$2;} END{for (i in compare){ if (compare[i] < compare1[i]){ print i " \t" compare[i] " \t"" < " " \t" compare1[i] " \t" "differenza:" (compare[i] - compare1[i]) }; if (compare[i] > compare1[i]){ print i " \t" compare[i] " \t" " > " " \t" compare1[i] " \t" "differenza:" (compare[i] - compare1[i])} if (compare[i] == compare1[i]){ print i " \t" compare[i] " \t" " = " " \t" compare1[i] " \t" "differenza:" (compare[i] - compare1[i])} }}' compare
A 10 < 15 differenza:-5
B 50 > 5 differenza:45
C 2 < 3 differenza:-1
D 100 < 500 differenza:-400
E 1200 > 900 differenza:300
F 1500 = 1500 differenza:0

Ovviamente quest’ultimo script risulta MOLTO più leggibile se scritto in un file eseguibile. A tal fine creiamo un file di testo chiamato “comparing.awk” contenente il testo qui di seguito:

#!/usr/bin/awk -f
{
var1=$1;
var2=$2;
}
{
compare[var1]=var2;
getline < "compare1"; compare1[$1]=$2;
}
END{
for (i in compare){
if (compare[i] < compare1[i]){
print i " \t" compare[i] " \t" " < " " \t" compare1[i] \
" \t" "differenza:" (compare[i] - compare1[i]) };
if (compare[i] > compare1[i]){
print i " \t" compare[i] " \t" " > " " \t" compare1[i] \
" \t" "differenza:" (compare[i] - compare1[i])};
if (compare[i] == compare1[i]){
print i " \t" compare[i] " \t" " = " " \t" compare1[i] \
" \t" "differenza:" (compare[i] - compare1[i])};
}
}

Lo rendiamo eseguibile (“chmod a+x comparing.awk”) e poi lo lanciamo dando come argomento lo stesso argomento che abbiamo dato al comando su linea di comando, ovvero il file “compare”:

$ ./comparing.awk compare
A 10 < 15 differenza:-5
B 50 > 5 differenza:45
C 2 < 3 differenza:-1
D 100 < 500 differenza:-400
E 1200 > 900 differenza:300
F 1500 = 1500 differenza:0

Certo sarebbe più comodo e flessibile se i file da confrontare fossero indicati come primo e secondo argomento dello script che lanciamo, anzichè avere il nome di uno dei file scritto all’interno dello script. Per ottenere questo trasformiamo lo script in questo modo lanciandolo da bash anzichè da awk:

$ vim comparing2.sh
#!/bin/bash
pluto=`echo $2`
awk -v secondoarg=$pluto '{
var1=$1;
var2=$2;
}
{
compare[var1]=var2;
getline < secondoarg; compare1[$1]=$2;
}
END{
for (i in compare){
if (compare[i] < compare1[i]){
print i " \t" compare[i] " \t" " < " " \t" compare1[i] \
" \t" "differenza:" (compare[i] - compare1[i]) };
if (compare[i] > compare1[i]){
print i " \t" compare[i] " \t" " > " " \t" compare1[i] \
" \t" "differenza:" (compare[i] - compare1[i])};
if (compare[i] == compare1[i]){
print i " \t" compare[i] " \t" " = " " \t" compare1[i] \
" \t" "differenza:" (compare[i] - compare1[i])};
}
}' $1
echo sono stati elaborati i file: \"$1\" e \"$pluto\"
----------------------------
$ chmod a+x comparing2.sh

$ ./comparing2.sh compare compare1
A 10 < 15 differenza:-5
B 50 > 5 differenza:45
C 2 < 3 differenza:-1
D 100 < 500 differenza:-400
E 1200 > 900 differenza:300
F 1500 = 1500 differenza:0
sono stati elaborati i file: "compare" e "compare1"

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

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>