2.5        AWK

Linguaggio per trattamento di file sequenziali (1977,1985)

Il nome deriva dalle iniziali dei suoi tre autori (Alfred V. Aho, Peter J. Weinberger, e Brian W. Kernighan). La versione originale nasce agli AT&T Bell Laboratories nel 1977; nel 1985 diventa disponibile, sui sistemi UNIX, una versione pi potente con funzioni definibili dall'utente, pi fili in input e trattamento delle espressioni regolari.

AWK indicato per la manipolazione di file sequenziali e per la generazione di stampe; l'interprete gestisce automaticamente l'input ed applica, ad ogni riga di questo, le regole indicate dal programma, per stabilire se questa deve essere scartata, riscritta o modificata. Le regole hanno la forma:

pattern {comando}

dove pattern una stringa di testo, una condizione o un'espressione regolare: se la riga contiene, il testo, o soddisfa la condizione o l'espressione regolare, inviata in output se omesso {comando}, altrimenti eseguito {comando}. Viceversa se manca pattern si esegue {comando}.

La struttura generale di un programma AWK :

   regole iniziali (pattern BEGIN)

   regole applicate ad ogni riga del file di input

   operazioni finali (pattern END)

Il trattamento dei files facilitato da un insieme di variabili mantenute dall'interprete: $0 la riga, $1, $2, sono i campi della riga, NF il numero campi nella riga, NR il numero di righe totali, ed altri ancora.

AWK molto semplice da utilizzare: poich nella riga di comando si pu inserire il "programma", facile scrivere cose del tipo:

awk '/cpu_time/ {print NR " " $0}' flin

per estrarre dal file flin le righe contenenti cpu_time, indicandone la posizione nel file.

L'esempio seguente realizza la fusione di due file ordinati; la posizione della chiave la sua lunghezza sono fornite sulla riga di comando (opzioni v, l'opzione f introduce lo script AWK):

awk -f merge.awk -v inizio=45 -v lungo=12 flon flin

# GNU Awk 3.0.6

# Il programma fonde due file

#

func take_key(riga) { return sprintf("0%s",substr(riga,inizio,lungo))}

 

func read2() { if ((getline Rec2 < file2) > 0)

return take_key(Rec2)

else

return "9"

}

 

func compare() {while (1 == 1) {

if (Key1 == Key2) {

if (Key1 == "9")

break

}

if (Key1 < Key2) break

print Rec2

Key2 = read2()

Ck2++

}

}

 

BEGIN { print "Inizio fusione"

file2 = ARGV[2]

ARGV[2]= "" # il secondo file e' gestito da programma

print "Chiave a colonna " inizio " lunga " lungo "\n"

Key2 = read2()

}

# corpo del programma

{

Key1 = take_key($0)

compare()

print $0

}

 

END { Key1 = "9"

compare()

printf ("\nTotale righe %d (%d + %d)", NR + Ck2, NR, Ck2)

print "\nFine fusione"}

Le chiavi di input del programma sono:

a.out

a.exe

demounit.gpi

gpc.pas

help.bat

pexecute.pas

prova.pas

unit.txt

flin

flon

merge.awk

p.bat

L'output del programma :

Inizio fusione

Chiave a colonna 45 lunga 12

 

A OUT 409,011 04-25-02 10:17p a.out

A EXE 411,059 04-25-02 10:17p a.exe

DEMOUNIT GPI 4,299 04-25-02 10:17p demounit.gpi

FLIN 434 05-15-02 6:11p flin

FLON 0 05-15-02 7:52p flon

GPC PAS 69,959 07-13-00 4:03a gpc.pas

HELP BAT 13 05-13-02 6:34p help.bat

MERGE AWK 956 05-15-02 7:50p merge.awk

P BAT 58 05-15-02 7:39p p.bat

PEXECUTE PAS 3,887 05-13-02 6:45p pexecute.pas

PROVA PAS 201 04-25-02 10:17p prova.pas

UNIT TXT 2,105 04-25-02 10:23p unit.txt

 

Totale righe 12 (4 + 8)

Fine fusione

L'esempio seguente illustra l'utilizzo di AWK per semplificare la scrittura di programmi Brianf (v. par. 2.7) tramite alcune macrofunzioni e l'utilizzo di nomi simbolici per gli indirizzi di memoria.

# GNU Awk 3.0.6

# Macroassembler per Brianf

#

func sost(token) {

if (token !~ id_regexp) return token;

if (token in arr_var) return arr_var[token];

return arr_var[token] = indrz++;

}

func go(to) {

if (to != "") {

j = to - mem;

if (j > 0)

for (i = 1; i <= j; i++) printf (">");

else if (j < 0)

for (i = 0; i > j; i--) printf ("<");

if (j != 0) mem = to;

}

}

func commento() {mtch=1;return sprintf(" \t# %s\n",row);} # commento

func clean(n) {if (n != "") go(n); printf("[-]")}

func incr(n) {go(n);printf("+");}

func decr(n) {go(n);printf("-");}

 

func move(from, to){

go(from);printf("[-");go(to);printf("+");go(from);printf("]");go(to);

}

BEGIN {

id_regexp = "[A-Za-z][A-Za-z0-9]*";

indrz = 1; # assegna indirizzi a nomi simbolici

mem = 0; # posizione di memoria

mtch = 0; # indica se trovata istruzione

}

  {row = $0;$2 = sost($2);$3 = sost($3)} 

/INCR/ {incr($2);printf("%s",commento());}

/DECR/ {decr($2);printf("%s",commento());}

/MOVE/ {clean($3);move($2,$3);printf("%s",commento());}

/INPUT/ {go($2);printf(",%s",commento());}

/OUTPUT/ {go($2);printf(".%s",commento());}

/COPY/ {clean($3);clean("0");go($2);

printf("[-");incr("0");incr($3);go($2);printf("]");

move("0",$2);go($3);printf("%s",commento());}

/CLEAN/ {clean($2);printf("%s",commento());}

/GOTO/ {go($2);printf("%s",commento());}

{if (mtch == 0) print $0; mtch = 0;} # prende tutto il resto

 

END {for (VAR in arr_var) printf ("\nVariabile %s \ta %s",VAR, arr_var[VAR]);}