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]);}