-
regelbasierter Ansatz: AWK-Programme enthalten Folgen von
Regeln, die aus Mustern und
Aktionen bestehen und der Reihe nach auf die Datensätze
(meist Zeilen) des Eingabe-Datenstromes angewendet werden
-
die Einlese-Schleife muss vom AWK-Anwender also nicht programmiert werden
-
AWK liest automatisch die als Argumente benannten Dateien oder, wenn diese
fehlen, die Standard-Eingabe
-
jeder eingelesene Datensatz wird automatisch in Felder zerlegt, auf die man
bequem zugreifen kann, z.B.
$1 --> Feld 1 des aktuellen Satzes (analog $2, $3, ... für die weiteren Felder)
$NF --> letztes Feld des aktuellen Satzes
NF --> Anzahl der Felder des aktuellen Satzes (number of fields)
$0 --> der komplette Satz
-
Feld- und Datensatz-Trenner sind für Ein- und Ausgabe getrennt über spezielle
Variablen einstellbar:
- FS (field separator)
- OFS (output field separator)
- RS (record separator)
- ORS (output record separator)
-
Manipulationen an Feldern (Zuweisungen, Veränderung der Feld-Anzahl durch
Änderung von NF) führen zu einer Neuberechnung von
$0; dabei wird OFS als
Feld-Trenner benutzt
-
Beispiel:
#include(ofs.sh)
-
neben den Regeln kann ein Programm noch Funktions-Definitionen
enthalten
-
AWK-Programme bestehen also prinzipiell aus einer Folge von Regeln und
Funktions-Definitionen:
pattern { action statements }
function name(parameter list) { statements }
-
das Muster oder die Aktion einer Regel kann fehlen:
- leeres Muster: Regel gilt für alle Datensätze
- leere Aktion: Ausgabe des aktuellen Datensatzes, also print
-
Aktionen sowie Funktionskörper werden in geschweifte Klammern
eingeschlossen und können mehrere Anweisungen enthalten, die durch einen
Zeilenumbruch und/oder ein Semikolon getrennt werden
-
bei einer Regel, die aus Muster und Aktion besteht, muss die öffnende
geschweifte Klammer der Aktion auf derselben Zeile stehen wie das Muster, da
man sonst eine Regel ohne Muster formuliert, die nur aus der Aktion besteht
-
man kann jede Anweisung mit einem Semikolon beenden; ein Semikolon für sich
allein symbolisiert die leere (wirkungslose) Anweisung
-
Kommentare werden mit einem
#
eingeleitet
-
Beispiel:
#include(summe_sp2.awk)
-
Aufruf-Beispiel:
#include(summe_sp2.sh)
-
Ausdrücke sind die einfachsten Anweisungen; man kann sie
sowohl in Mustern als auch in Aktionen einsetzen
-
elementare Ausdrücke umfassen Konstanten, Variablen, Array-Referenzen,
Funktionsaufrufe und und einige eingebaute Objekte wie Feldnamen
-
durch Kombination von Ausdrücken mit verschiedenen Operatoren lassen sich
komplexere Ausdrücke schaffen
-
Liste der Operatoren, fallend nach Präzedenz geordnet:
(...) Gruppierung
$ Feld-Referenz
++ -- Inkrement/Dekrement (+1/-1), Präfix (++a) / Postfix (a--)
++a erhöht den Wert von a vor dem Variablenzugriff, a++ danach
^ Potenzierung (** ist auch zulässig)
+ - ! unäres Plus/Minus und logische Negation
* / % Multiplikation, Division, Modulus (Rest der Division)
+ - Addition, Subtraktion
space String-Verkettung durch Leerzeichen, z.B. "au" "to"
| |& Pipe-Ein-/Ausgabe bei getline, print, printf
< > kleiner, größer
<= >= kleiner gleich, größer gleich
!= == ungleich und gleich
~ !~ Test auf passendes/unpassendes Muster (regulärer Ausdruck);
konstanter Ausdruck /abc/ alleine entspricht $0 ~ /abc/
in Test auf Array-Mitgliedschaft
&& logisches UND
|| logisches ODER
?: ternärer Operator zur Bildung eines bedingten Ausdrucks:
expr1 ? expr2 : expr3
falls expr1 wahr, dann ergibt sich Wert expr2, sonst expr3
= += -=
*= /= %= ^= Zuweisung eines Werten an eine Variable (a op= b entspricht a = a op b)
-
bis auf
?:
und ^
sind alle Operatoren linksassoziativ
-
durch geeignete Klammerung lässt sich die Operatorrangfolge gezielt verändern
-
konstante reguläre Ausdrücke sind überwiegend nur rechts von den Operatoren
~
und !~
sinnvoll, da /xy/ ~ z
eine Kurzform von ($0 ~ /xy/) ~ z
darstellt
-
rechts von
~
und !~
darf ein beliebiger Ausdruck
stehen; dieser wird bei Bedarf in einen String konvertiert und als regulärer
Ausdruck interpretiert, wodurch Suchmuster auch dynamisch im Programm erzeugt
werden können, z.B. $0 ~ "^r" ".*t"
statt
/^r.*t/
-
Aufbau der Muster:
BEGIN
END
/regular expression/
relational expression
pattern && pattern
pattern || pattern
pattern ? pattern : pattern
(pattern)
! pattern
pattern1, pattern2
-
bei BEGIN und END muss eine Aktion folgen; alle BEGIN-Blöcke werden logisch
zusammengefasst und geschlossen vor dem Lesen der ersten Eingabezeile
ausgeführt; analog werden alle END-Blöcke kombiniert und nach dem Lesen der
letzten Eingabezeile oder dem Abbruch des Programms durch
exit
ausgeführt
-
ein in zwei Schrägstriche eingeschlossener konstanter regulärer Ausdruck, der
den Regeln des UNIX-Standard-Suchwerkzeugs
egrep
entspricht, dient
als Suchmuster und selektiert alle Sätze, die dem Muster entsprechen
-
hier eine Auswahl bestimmter Suchmuster-Metazeichen mit Spezialfunktion, die
durch das Voranstellen eines Backslashs aufgehoben werden kann:
. ein beliebiges Zeichen
^ Anfang des Strings
$ Ende des Strings
() Klammerung zur Bildung von Teilmustern
[abc] alle Zeichen der genannten Menge
[^ab] alle Zeichen außerhalb der genannten Menge
* beliebige (0..n-malige) Wiederholung des vorangehenden Elements
+ vorangehendes Element mindestens 1 Mal vorhanden
? vorangehendes Element 0 oder 1 Mal vorhanden
a|b Alternative: Muster a oder Muster b
a{2,6} vorangehendes Element ist 2 bis 6 Mal vorhanden
\... Escape-Sequenzen wie bei Strings (\n, \t, ...)
-
durch ein Komma zwischen zwei Mustern wird ein Bereich (range)
definiert; er selektiert jede Satzfolge, die mit einem Satz beginnt, der dem
ersten Muster entspricht und die bis zum ersten Folgesatz fortgesetzt wird, der
auf das zweite Muster passt
-
Beispiel:
#include(range.awk)
-
Bereiche können mit keinem anderen Muster kombiniert werden
-
Aktionen:
-
neben den o.g. Ausdrücken stehen auch Ein-/Ausgabe- sowie
Steuerflussanweisungen zur Verfügung
-
Ein-/Ausgabe-Anweisungen:
getline [var] # liest Folgesatz der Einleseschleife nach $0 bzw. var
getline [var] <file # liest nächsten Satz der Datei file nach $0 bzw. var
cmd | getline [var] # liest die Ausgabe der Pipe "cmd" nach $0 bzw. var
print # schreibt den aktuellen Satz auf die Standardausgabe
print expr-list # analoge Ausgabe der Ausdrücke der Liste
print expr-list >file # Ausgabe in die Datei "file"
printf fmt, expr-list # formatierte Ausgabe
printf fmt, expr-list >file # dito in eine Datei
system(cmd-line) # externes Kommando ausführen
close(file), close(command) # schließt Datei oder Pipe
-
Steuerflussanweisungen:
if (condition) statement [ else statement ] # vollständige/unvollständige Alternative
while (condition) statement # Abweisschleife
do statement while (condition) # Nichtabweisschleife
for (expr1; expr2; expr3) statement # Zählschleife; entspricht: expr1;
# while (expr2) { statement; expr3 }
for (var in array) statement # Arraydurchlauf
break # Schleifenabbruch
continue # starte neue Schleifeniteration
delete array[index] # Löschung eines Array-Elements
delete array # Löschung eines Arrays
exit [ expression ] # Sprung zum END-Block / Programmende
{ statements } # Anweisungsblock (Gruppierung von Anweisungen)
next # starte neue Iteration der Haupteinleseschleife
nextfile # Übergang zur nächsten Eingabedatei
-
der Funktionsumfang von AWK ist fest definiert und überschaubar
-
Erweiterungen durch Module/externe Bibliotheken sind standardmäßig nicht
vorgesehen
Anmerkungen:
-
die Gawk-spezifische Funktion extension()
zum dynamischen Nachladen von Funktionen ist laut Handbuch ein experimentelles
Feature, dessen Nutzung allgemein nicht empfohlen wird
-
das Shell-Skript igawk realisiert einen rein textuellen
Include-Mechanismus zum Einfügen von AWK-Dateien in andere AWK-Programme:
#include(igawk.awk)
-
auf Grund einer klugen Mittel-Auswahl ist AWK in seinem Einsatzgebiet dennoch
ausgesprochen flexibel
-
reguläre Ausdrücke sind fest in die Sprache integriert, aber weniger mächtig
als bei Perl und Co. (z.B. fehlen Lookahead und Lookbehind)
-
es gibt nur zwei skalare Datentypen: Strings und Gleitpunktzahlen
-
String-Konstanten werden in Doppelapostrophe eingeschlossen und können
verschiedene, jeweils mit einem Backslash eingeleitete Escape-Sequenzen
enthalten, die spezielle, ggf. nicht druckbare Zeichen symbolisieren: z.B.
\n
für eine Zeilenschaltung, \t
für einen Tabulator
und \\
für den Backslash selbst
-
Gleitpunktzahlen sind bei modernen Implementierungen double-precision
floating-points
-
Achtung: die Locale (speziell der Dezimaltrenner der Locale) kann die Arbeit
von AWK beeinflussen:
#include(locale.sh)
-
AWK kennt daneben noch numerische Strings: das sind Strings, die wie (gültige)
Zahlen aussehen
-
dieses Konzept dient auch der Typ-Bestimmung von Variablen:
-
numerische Konstanten sowie Ergebnisse numerischer Operationen haben den Typ
NUMERIC
-
String-Konstanten sowie Ergebnisse von String-Operationen haben den Typ STRING
-
Felder, über getline() gelesene Eingaben, der
Inhalt der Spezial-Variablen FILENAME, die Elemente
der Felder ENVIRON und ARGV
sowie die durch die Funktion split() generierten
Feld-Elemente haben den Typ STRNUM, wenn ihre Werte
numerische Strings sind, andernfalls den Typ STRING
-
uninitialisierte Variablen haben auch den Typ STRNUM
-
Variablen werden bei Benutzung automatisch angelegt und im Falle einer
Zuweisung auch initialisiert
-
uninitialisierte Variablen haben je nach Kontext als impliziten Wert den leeren
String oder die Zahl Null
-
bei Zuweisungen des Inhalts einer Variablen an eine andere Variable
wird auch der Typ der Ausgangs-Variablen mit zugewiesen
-
der Variablen-Typ bestimmt, wie deren Werte verglichen werden:
numerisch oder als Strings
-
Vergleichs-Regeln:
Typ | STRING | NUMERIC | STRNUM |
STRING | String | String | String |
NUMERIC | String | numerisch | numerisch |
STRNUM | String | numerisch | numerisch |
-
Daten werden je nach Kontext automatisch von Strings in Zahlen und umgekehrt
konvertiert
Beispiel:
#include(konvert.awk)
-
im Booleschen Kontext (also bei Vergleichen) werden der leere String sowie die
Zahl 0 als FALSE und alle anderen Werte als TRUE interpretiert
-
zusätzlich zu skalaren Variablen existieren mehrdimensionale assoziative Felder
(Arrays, Hashes, Dictionaries) mit Strings als Schlüssel bzw. Index und Strings
oder Zahlen als Werte; Felder als Feldinhalte sind also unzulässig
-
die verschiedenen Indexe werden de facto zu einem String zusammengefügt, wobei
der Inhalt der Variablen SUBSEP als Trenner dient;
intern handelt es sich also immer um eindimensionale Arrays
-
mit
for
kann man alle Indexe eines Arrays durchlaufen, wobei die
Reihenfolge als undefiniert zu betrachten ist
-
Beispiel:
#include(array.awk)
-
einmal erzeugte skalare Variablen sind nachfolgend nur skalar und
Array-Variablen entsprechend nur als Array-Variablen nutzbar
-
Variablen sind standardmäßig global
-
Argumente benutzerdefinierter Funktionen sind lokal; manchmal nutzt man
gezielt überzählige Argumente als lokale Variablen
Beispiel:
#include(lokale_vars.awk)
Aufruf-Möglichkeit:
#include(lokale_vars.sh)
-
automatisches Speicher-Management beim Anlegen von Variablen und Arrays sowie
beim Löschen von Feld-Inhalten mittels delete
-
Operatoren und Anweisungen sind an die Sprache C angelehnt; zusätzlich
existiert mit ^ bzw. ** der
Potenz-Operator
-
AWK-Programme sind im Allgemeinen recht kompakt, aber dennoch lesbar
-
sinnvolle AWK-Programme sind nicht selten One-liners, also
sehr kurze Code-Stücke, die aus einer Zeile oder nur wenigen Zeilen bestehen
-
Beispiele:
#include(one_liners.sh)