6.3    awk

Nach dieser kurzen Einführung in grep wollen wir uns im Detail mit regulären Ausdrücken auseinandersetzen. Dazu verwenden wir awk.

awk ist eine Skriptsprache zur Verarbeitung von ASCII-Text. Sie wurde nach ihren Entwicklern Aho, Kernighan und Weinberger benannt und entwickelte sich im Laufe der Jahre zu einem populären Werkzeug der Anwender und Administratoren.

Der Grund dafür ist unter anderem der, dass awk sehr einfach zu erlernen ist, da es nur recht wenige Befehle gibt. Darüber hinaus ist die Syntax an die Sprache C angelehnt und daher schon vielen Entwicklerinnen und Entwicklern vertraut.

awk kann über Skripte oder direkt über die Kommandozeile benutzt werden, wobei jeweils das Programm awk bzw. gawk für diese Zwecke verwendet wird. awk ist das eigentliche, auf jedem Unix-artigen System vorhandene Grundprogramm, besser gesagt, der Interpreter. gawk ist die GNU-Version und auf vielen Linux-Systemen verfügbar.

6.3.1    awk starten

awk (wie auch die Implementierungen nawk und gawk) wird ganz einfach über die Kommandozeile gestartet. Die Befehle zur Verarbeitung des Textes werden entweder in Hochkommata ((ª) + (#)) oder in einer Datei abgelegt und als zweiter bzw. dritter Parameter bei Optionen übergeben. Danach folgt optional eine Datei, in der die zu verarbeitenden Daten enthalten sind, die mittels Pipe übergeben werden können.

user$ awk '{print $1}' DateiA DateiB … DateiN
user$ cat Datei | awk '{print $1}'
user$ awk -f skript.awk Datei
user$ cat Datei | awk -f skript.awk

Listing 6.11     So starten Sie awk.

6.3.2    Arbeitsweise von awk

Das Programm besteht aus den folgenden Teilen: dem BEGIN-Teil, dem Hauptteil und dem END-Teil. Nach dem Aufruf wird zunächst einmal der eventuell vorhandene BEGIN-Teil des Programmcodes abgearbeitet, der zur Initialisierung verwendet wird.

Anschließend wird jede einzelne Zeile des zu verarbeitenden Textes separat verarbeitet, was im Hauptteil geschieht. Der Hauptteil enthält demnach den Code für alle Anweisungen, die mit den Zeilen durchgeführt werden sollen, und wird für jede Zeile komplett neu ausgeführt. Bei großen Dateien ist es daher recht sinnvoll, auf zu rechenaufwendige Anweisungen zu verzichten und einen effizienten Code zu schreiben.

Nachdem die Eingabedatei komplett verarbeitet wurde, wird (sofern implementiert) der Code im END-Teil des Skripts ausgeführt.

Hier sehen Sie ein Beispiel für den Aufbau eines awk-Skripts:

BEGIN
{
    print "Monatsabrechnung"
}
{
    print $1 "+" $2 "=" $1+$2
}
END
{
   print "Geschafft."
}

Listing 6.12     Aufbau eines Skripts mit awk

Kommandotrennung

In awk werden einzelne Kommandos, die in derselben Zeile stehen, durch ein Semikolon getrennt: print $1; variable=1.

6.3.3    Reguläre Ausdrücke in awk anwenden

Eine nützliche Fähigkeit von awk besteht darin, als Filter für Muster zu dienen. Damit weist es ähnliche Funktionalitäten wie grep auf, das Sie in Abschnitt 6.2 kennengelernt haben. Und was verwendet man dazu? Richtig! Reguläre Ausdrücke.

user$ cat file
Steffen, Friedrichshafen
Tobias, Ettenbeuren
Johannes, Karlsruhe
user$ awk '/u/' file
Tobias, Ettenbeuren
Johannes, Karlsruhe

Listing 6.13     Aufruf von awk mit einem Muster

6.3.4    Einfache Strings

Einfache Strings werden durch die Angabe des Strings selbst gefiltert. Die gesuchten Strings werden in Hochkommata und zwischen zwei Slashes geschrieben:

user$ awk '/on/' Zweigstellen
Bonn
London
user$ awk '/S/' Zweigstellen
Salzburg
Stockholm

Listing 6.14     Filtern auf Zeichensalat

6.3.5    Der Punkt-Operator

Der Punkt-Operator steht, wie Sie bereits wissen, für ein beliebiges Zeichen an einem einzigen Platz. Man kann ihn mitten in Strings einbauen, um zum Beispiel sowohl große als auch kleine Buchstaben zu erwischen. Eine ebenfalls praktische Anwendung ist die Namensfindung – es könnte vorkommen, dass der Name einer Person entweder mit »c« oder mit »k« geschrieben wird.

user$ awk '/K.ln/' Zweigstellen
Köln

Listing 6.15     Der Punkt-Operator

6.3.6    Der Plus-Operator

Der Plus-Operator + bewirkt hier keine Addition im eigentlichen Sinne; er ist lediglich der langweilige Operator, dessen Bedingung nur erfüllt ist, sofern mindestens einmal das vor ihm geschriebene Zeichen auftritt.

user$ awk '/n+/' Zweigstellen
Bonn
München
London
Bern
Köln

Listing 6.16     Mindestens einmal muss das n vorkommen.

6.3.7    Die Zeichenvorgabe

Es ist möglich, eine bestimmte Zeichenvorgabe zu setzen. Eines dieser von Ihnen vorgegebenen Zeichen muss dann an der entsprechenden Stelle im String vorkommen. Die ausgewählten Zeichen werden dabei in eckige Klammern eingebettet: [abc]. Außerdem können einige Notationen wie a-z oder 0-9 verwendet werden. Einzelne Zeichengruppen werden durch Kommata getrennt.

user$ awk '/M?nch[a-z,0-9][nNzkvps]/' Zweigstellen
München

Listing 6.17     Diese Zeichen dürfen alle vorkommen.

6.3.8    Negierte Zeichenvorgabe

Das Gegenteil der obigen Zeichenvorgabe ist die negierte Zeichenvorgabe. Die dabei angegebene Menge von Zeichen darf an der entsprechenden Stelle nicht vorkommen, damit die Bedingung erfüllt ist.

user$ awk '/M?nch[^e][^bn]/' Zweigstellen
user$ awk '/M?nch[^X][^bY]/' Zweigstellen
München

Listing 6.18     Negierte Zeichenvorgabe

Diese Negierung kann nur auf Mengen und nicht direkt auf Einzelzeichen, d.h. ohne eckige Klammerung, angewandt werden. Später werden Sie sehen, dass man auf diese Art und Weise den Anfang einer Zeile beschreibt.

6.3.9    Zeilenanfang und -ende

Oftmals sortiert man Datensätze nach dem Anfang bzw. dem Ende einer Zeile – ein gutes Beispiel hierfür wäre die Aussortierung der Logdatei-Einträge des Tages x. Die regulären Ausdrücke stellen uns hierfür zwei Zeichen zur Verfügung: ^ und $, wobei der Zeilenanfang durch ^ angegeben wird und als sogenanntes XOR (Exclusive OR) bezeichnet wird, das in der Digitaltechnik Verwendung findet. Das Dollarzeichen sollte jedem bekannt sein und wird in awk (ungeachtet der Bedeutung in regulären Ausdrücken) für die Kennzeichnung eines Variablenzugriffs verwendet. (Dies muss nicht zwangsläufig so sein, macht aber einen guten Programmierstil aus.)

user$ awk '/^B/' Zweigstellen
Bonn
Bern
user$ awk '/n$/' Zweigstellen
Bonn
München
London
Bern
Köln

Listing 6.19     Filtern nach Zeilenanfang und -ende

6.3.10    awk – etwas detaillierter

Nein, wir möchten an dieser Stelle keine 200 Seiten awk-Know-how vermitteln, sondern uns auf wenige Seiten beschränken, um Ihnen in recht kurzer Form die Grundzüge dieser Sprache zu erklären, was wir in den vorangegangenen Abschnitten bereits ansatzweise getan haben.

awk bietet die Möglichkeit, auf einige wichtige Variablen zuzugreifen, die für die korrekte Ausführung des Programmcodes wichtig erscheinen. Sehen wir uns diese Variablen doch einmal an:

6.3.11    Zusätzliche Parameter beim Aufruf

Zu diesem Zeitpunkt kennen Sie nur die grundlegende Form eines Aufrufs von awk und den Parameter zur Trennung der einzelnen Spalten (den oben erwähnten Field Separator). Allerdings sind noch einige weitere wichtige Parameter vorhanden.

Mit -f <Datei> geben Sie den Namen eines Skripts an, das den Programmcode enthält. Vor dem Start können Sie übrigens auch Variablen erzeugen und sie mit neuen Werten beglücken, was mit awk -v Variable=Wert geschieht.

--copyright gibt die Kurzform der Lizenz aus, mit der gawk ausgeliefert wurde. Wer zusätzlich die verwendete awk-Version sehen möchte, sollte --version nutzen. Den Kompatibilitätsmodus aktivieren Sie mit dem »Ur-awk« mit den Argumenten --compat und --traditional, und eine Anwendungshilfe fordern Sie mit --usage und --help an.

6.3.12    awk und Variablen

Auch in awk gibt es Variablen. Und das Schöne daran ist, dass ihre Handhabung einfach ist. Werte werden direkt über den Zuweisungsoperator (das Gleichheitszeichen) an die Variable übergeben. Inkrement- und Dekrement-Operatoren der Sprache C sind hier ebenfalls nutzbar, sodass der Wert via variable++ um 1 erhöht und mit variable-- um denselben Wert gesenkt werden kann.

Schreiben wir einmal ein kleines Testprogramm, das die Zeilen der Eingabedatei ähnlich wie das wc-Programm zählt. Dazu nehmen wir eine Variable Linecount, die die Zeilen zählt, setzen sie am Anfang (also im BEGIN-Bereich) auf den Wert 0 und zählen sie bei jedem Durchlauf des Hauptprogramms eine Zeile weiter:

BEGIN {
  # Im Initialisierungsteil weisen wir der Variablen den Wert 0 zu.
  Linecount=0; # Verwendung: Zeilen zählen.
}
{ # Die Hauptschleife wird für jede Zeile erneut durchlaufen.
  Linecount++;
}
END {
  print "Wert: " Linecount;
}

Listing 6.21     Der Zeilenzähler in awk

Ein Vergleich mit unserem Skript und dem wc-Kommando zeigt uns, dass alles funktioniert: Die Ergebnisse sind äquivalent.

user$ awk -f script.awk file
Wert: 367
user$ wc -l file
    367 file

Listing 6.22     Der Test

Kommentare werden in awk mit einer Raute (#) eingeleitet und gelten jeweils für die aktuelle Zeile ab dem Punkt, an dem sie gesetzt wurden.

6.3.13    Rechenoperationen

Selbstverständlich kann man auch Berechnungen in awk durchführen, und zwar bedeutend komfortabler als in der bloßen Shellskript-Programmierung. Es gibt verschiedene Standardoperatoren, wie den Additions- (+) und den Subtraktionsoperator (-) und die Operatoren für Multiplikation (*) und Division (/). Aber auch Kombinationen mit dem Zuweisungsoperator sind möglich. So können Sie – wie in der Programmiersprache C – aus Variable = Variable + 2; ein kurzes Variable += 2; oder aus Variable = Variable / 5; ein Variable /= 5; machen.

BEGIN {
   Var = 1000;
   Var = 999; print Var;
   Var = Var * 2; print Var;
   Var += 10; print Var;
   Var *= 2; print Var;
}

Listing 6.23     Rechenbeispiel für awk

user$ awk -f script.awk
999
1998
2008
4016
^D

Listing 6.24     Anwendung des Rechenbeispiels

Neben diesen Rechenoperationen können einige weitere (z. B. die Potenzierung) durchgeführt werden: var = 3 ^ 3 weist var den Wert »drei hoch drei«, also 27, zu. Modulo-Operationen sind über den gleichnamigen Operator (%) ebenfalls möglich: var = 5 % 4.

Prä- und Post-Inkrementierung

Die Inkrementierung und Dekrementierung von Variablen kennen Sie bereits; was wir Ihnen jedoch noch verschwiegen haben, ist der Unterschied zwischen der sogenannten Prä- und Post-Inkrementierung bzw. -dekrementierung.

Eine Präverarbeitung hat die Syntax ++variable bzw. --variable, eine Postverarbeitung variable++ bzw. variable--. Der Unterschied zwischen diesen beiden Varianten ist, dass bei der Präversion eine Verarbeitung in einer Anweisung noch vor der eigentlichen Anweisung durchgeführt wird. Bei der Postvariante geschieht dies erst, nachdem solch eine Anweisung beendet wurde. Aber am besten lernt man ja bekanntlich an Beispielen:

user$ cat test.awk
BEGIN {
      test = 1;

      print test++;
      print test;
      print ++test;
}
user$ awk -f test.awk
1
2
3

Listing 6.25     Post- und Prä-Inkrementierung und -Dekrementierung

6.3.14    Bedingte Anweisungen in awk

Es stehen auch in awk einige Möglichkeiten zur Verfügung, bedingte Anweisungen mittels relationaler Ausdrücke zu formulieren. Im Rahmen dieser kleinen awk-Einführung behandeln wir die if-Anweisung und die for-Schleife.

if

Mithilfe dieser Anweisungen werden die Werte von Variablen getestet. Dafür werden sogenannte Bedingungen erstellt, die entweder erfüllt werden oder eben nicht. Wird eine Bedingung also (nicht) erfüllt, wird entweder die nachstehende Anweisung oder eine ganze Gruppe von Anweisungen ausgeführt, wobei in diesem Fall ein Bereich für die Anweisungen innerhalb von geschweiften Klammern geschaffen werden muss.

Zum besseren Verständnis folgt nun ein Beispiel: Der Wert der Variablen var wird mit der if-Anweisung auf verschiedene Bedingungen hin geprüft. Ist die erste Bedingung nicht erfüllt, wird mit else if eine weitere Verzweigung dieser Anweisung eröffnet. Durch das else wird also nur eine Prüfung vorgenommen, wenn die vorherige Bedingung nicht erfüllt ist.

Wenn auch die zweite Bedingung nicht erfüllt ist, var also den Wert 100 hat, tritt die letzte else-Anweisung in Kraft. Eine else-Anweisung ohne zusätzliches if wird immer dann ausgeführt, wenn alle vorherigen Bedingungen unerfüllt sind.

BEGIN {
     var=100; # Wert anpassbar für Ihre Tests
     if(var > 100)
          print "var ist > 100"
     else if(var < 100)
          print "var ist < 100"
     else {
          print "var ist 100"
          print "Hier haben wir eine Gruppierung von zwei Anweisungen"
     }

     if(var <= 1000)
          print "var ist kleiner-gleich 1000"

     if(2 || 3){
          print "Bedingung erfüllt, da sowohl 2 als auch 3 immer WAHR"
          print "sind. Das logische ODER (||) erlaubt zudem, dass nur"
          print "eine der Bedingungen WAHR sein muss."
     }
}

Listing 6.26     So gehts.

Bedingung

Beschreibung

a < b

a muss kleiner als b sein.

a > b

a muss größer als b sein.

a <= b

a muss kleiner als oder gleich b sein.

a => b

a muss größer als oder gleich b sein.

a == b

a muss den gleichen Wert wie b haben.

a != b

a muss ungleich b sein.

a in b

b muss ein Array und a ein Element dieses Arrays sein.

a && b

Sowohl a als auch b müssen erfüllt sein.

a & b

Diese Bedingung ist nur dann erfüllt (also >=1), wenn die binären Werte von a und b, verknüpft mit einem logischen Und, mindestens 1 ergeben.

a || b

Ein logisches Oder. Beachten Sie, dass es in awk kein einfaches Oder-Zeichen (|) für diese Zwecke gibt. Es würde als Pipe-Zeichen behandelt werden.

! a

Diese Bedingung ist erfüllt, wenn a nicht erfüllt ist.

x ? a : b

Wenn die Bedingung x erfüllt ist, wird a als Bedingungswert bezeichnet, andernfalls b.

Tabelle 6.2     Grundlegende und logische Bedingungen

Geschweifte Klammern – nicht immer nötig!

Wie Ihnen vielleicht schon aufgefallen ist, folgen auf if nicht immer geschweifte Klammern. Dies liegt daran, dass die Klammern entfallen können, wenn nur eine Anweisung durch if ausgeführt werden soll. Klammern sind allerdings nie verkehrt und im Zweifelsfall richtig! Dies gilt analog auch für for und while, die wir als Nächstes betrachten.

for und while

Kommen wir nun zu zwei Schleifentypen, die Ihnen in ähnlicher Form bereits von der Shellprogrammierung her bekannt sind und die wir daher nicht nochmals in ihrer Funktionalität, dafür aber in ihrer Syntax erklären.

In der for-Schleife gibt es in der Regel drei Parameter (eine Ausnahme stellt die in-Bedingung für Arrays dar). Das erste Argument legt den Wert einer Variablen fest, wird also zur Initialisierung verwendet. Der mittlere Teil enthält die Bedingung in der gleichen Form wie die if-Anweisung. Der letzte Parameter gibt die Anweisung an, die bei jedem Schleifendurchlauf ausgeführt werden soll. Die einzelnen »Teile« werden dabei durch ein Semikolon voneinander getrennt:

Syntax:      for( Init; Bedingung; Anweisung ) {
                   Anweisung1;
                   Anweisung2;
                   …;
                   AnweisungN;
             }

Beispiel:    for(var=1; var<=5; var++)
                   print var;

Listing 6.27     Die for-Schleife

Die while-Schleife läuft wie in der Shell so lange durch, bis die Bedingung nicht mehr erfüllt wird. Diese Bedingung ist das Einzige, was im Schleifenkopf angegeben werden muss. Die Anweisungen werden in awk jedoch nicht in do und done, sondern, wie wir es hier gewohnt sind, in geschweifte Klammern eingepackt:

Syntax:      while( Bedingung ) {
                   Anweisung1;
                   Anweisung2;
                   …
                   AnweisungN;
             }

Beispiel:    var=1;
             while(var<=5)
                   print var++;

Listing 6.28     Die while-Schleife

6.3.15    Funktionen in awk

Ein wichtiges Feature einer Programmiersprache sind die sogenannten Funktionen. Wir werden Funktionen nicht nur in awk, sondern auch in der Shellskriptprogrammierung verwenden. Eine Funktion enthält keine, eine oder mehrere Anweisungen und führt diese jedes Mal aus, wenn sie aufgerufen wird. Dabei können ihr immer wieder ein oder mehrere Parameter übergeben werden, mit denen sie dann arbeitet. Dabei unterscheidet man in awk zwischen den Funktionen, die man selbst im Skriptcode implementiert, und den sogenannten Builtin-Funktionen. Eine davon kennen Sie sogar bereits: Die print-Funktion gibt Text aus, wobei ihr der Text bzw. die Variablennamen übergeben werden müssen. Diese übergebenen Texte und Variablen sind die im vorherigen Absatz angesprochenen Parameter.

Zur Funktionsbestimmung wird das Schlüsselwort function verwendet. Anschließend folgen der Funktionsname und die in Klammern eingeschlossenen Parameter.

Syntax:      function Name( Parameterliste )
             {
               Anweisung1;
               Anweisung2;
               …
               [return <Wert>] # Optional
             }

Listing 6.29     Funktionen in awk

Hierzu ein Beispiel, in dem wir eine dreizeilige Eingabe mit jeweils zwei Zahlen pro Zeile summieren. Dazu übergeben wir die Eingabe mit einer Pipe an awk:

$ echo '1 1
2 2
3 3' | awk '
function summe(a, b)
{
    sum = a + b;
    return sum;
}
BEGIN {
     ergebnis=0 # Startwert ist 0
     print "Summiere Werte ..."
}
{
     ergebnis+=summe($1, $2);
}
END {
     print "Endwert: " ergebnis;
}'

Listing 6.30     Beispiel für eine Summenfunktion in awk

Das obige Beispiel erzeugt folgende Ausgabe:

Berechne Werte ...
Endwert: 12

Listing 6.31     Ausgabe unseres Beispiels

Rückgabewert

awk unterstützt die sogenannten Rückgabewerte von Funktionen. Dabei werden die zurückgegebenen Funktionswerte in der Funktion selbst berechnet und an eine Variable oder an eine andere Funktion weitergegeben. Für diese Zwecke wird das Schlüsselwort return verwendet. Die obige Funktion könnte mit ihrem Rückgabewert beispielsweise folgende Verwendung finden: print summe(4, 7);. Dabei würde die Builtin-Funktion print den Rückgabewert als eigenen Parameter ansehen und ausgeben.

awk gibt übrigens auch einen Rückgabewert (wie es sich für gute Programme gehört) an die Shell zurück. Bei einem Rückgabewert von 0 war die Ausführung erfolgreich, andernfalls ist der Wert größer als 0.

6.3.16    Builtin-Funktionen

Builtin-Funktionen (also interne Funktionen) stehen nicht nur in der Shell, sondern auch in awk zur Verfügung und stellen eine der wichtigsten Komponenten dieser Sprache dar. Zum Umfang gehören sowohl mathematische Funktionen als auch solche zur String-Verarbeitung. Im Rahmen dieser Einführung werden wir uns auf die wichtigsten Funktionen konzentrieren.

getline()

Die getline-Funktion liest eine Eingabe des Benutzers bzw. der Benutzerin ein und speichert diese Eingabe optional in einer gewünschten Variablen, andernfalls in $0.

print(), sprintf() und printf()

Die Funktion print haben Sie bereits kennengelernt. Die awk-Version funktioniert im Prinzip so wie die der Shellversion. printf ist ebenfalls mit der Shellvariante relativ gleichzusetzen, soll aber in dieser Einleitung nicht weiter betrachtet werden.

sprintf(f, e) macht aus dem regulären Ausdruck e mittels der Formatangabe f einen String und gibt diesen zurück.

system(Kommando)

Diese Funktion ist in einer großen Anzahl von Programmiersprachen vorhanden und bietet die Möglichkeit, Shellbefehle direkt vom Programm aus auszuführen, was aber nicht als guter Programmierstil gilt. Das Kommando wird hierbei als String übergeben und sollte folglich in Anführungszeichen gesetzt werden.

fflush(Datei)

Diese Funktion leert die Puffer aller im Skript verfügbaren bzw. angegebenen Deskriptoren.

cos(x), sin(x)

Diese beiden Funktionen geben den Kosinus bzw. Sinus von x zurück.

exp(x)

exp() ist die Exponentialfunktion von awk.

int(String)

Diese Funktion arbeitet ähnlich wie atoi() in C und gibt die Zahl aus dem String String zurück: print int("70b");

log(x)

Hierbei handelt es sich um die Logarithmusfunktion von awk.

rand() und srand(x)

Wie wir bereits erwähnt haben, ist awk C sehr ähnlich. Man kann zwar nicht auf den gleichen Funktionsumfang zurückgreifen, aber es ist ein Grund mehr, diese Sprache aufgrund der Syntax zu mögen. Auch die Funktionen zur Generierung von Zufallszahlen haben die gleichen Namen. srand setzt dabei den Initialisierungswert für rand() mit x. Wird kein Argument übergeben, wird die aktuelle Uhrzeit dafür verwendet. rand() selbst gibt einen Wert im Bereich von 0 < x < 1 zurück.

sqrt(x)

sqrt() gibt die Wurzel aus x zurück.

gsub(r, s [, t])

Der String t wird nach dem regulären Ausdruck r durchsucht. Jeder gefundene Ausdruck wird mit s überschrieben:

gsub("Saturn", "Merkur", $1);
print($1);

Listing 6.32     gsub

index(s, t)

index() gibt die Position des Strings t im String s zurück.

length([s])

Diese Funktion gibt die Länge von einem String s (in Zeichen) zurück. Für den Fall, dass kein Argument gesetzt wurde, wird als Länge $0 zurückgegeben.

match(s, r)

Gibt die Position des regulären Ausdrucks r im String s zurück.

split(s, a [, r])

Mithilfe des regulären Ausdrucks r wird der String s auf das Array a aufgeteilt. Jeweils ein passender Ausdruck wird als Element im Array abgelegt. Die Anzahl der Elemente wird zurückgegeben.

sub(r, s [, t])

Der String t wird nach dem regulären Ausdruck r durchsucht. Der erste gefundene Ausdruck wird mit s überschrieben (also fast wie gsub()).

substr(s, i [, n])

Diese Funktion gibt einen Sub-String von s zurück. Dieser String fängt ab dem i-ten Zeichen an und endet entweder am String-Ende oder – sofern n gegeben ist – am n-ten Zeichen.

tolower(s) und toupper(s)

tolower() gibt den String s in einer Form zurück, bei der alle Großbuchstaben des Parameters durch die entsprechenden kleinen ersetzt werden (aus »S« wird »s« usw.). toupper ist für die gegenteilige Operation zuständig.

Zeitfunktionen

awk findet besonders im Bereich des Logdatei-Parsings Verwendung. Dort ist es oftmals nötig, bestimmte Uhrzeiten und Daten herauszufiltern oder zu setzen. gawk bietet hierfür einige Erweiterungen an.

systime() gibt die Anzahl der seit dem 1. Januar 1970 verstrichenen Sekunden zurück. Diesen Rückgabewert bezeichnet man als Timestamp.

Die Funktion strftime([Format [, Timestamp]]) wandelt diese Zahl in ein Format um, das Menschen leicht interpretieren können. Das Format können Sie dabei an die Bedürfnisse des Entwicklers bzw. der Entwicklerin angepasst werden und setzt sich aus verschiedenen Angaben der Form %X zusammen.

Da auch die strftime-Funktion einen Wert zurückgibt, kann dieser direkt mit print ausgegeben werden. Als Timestamp-Parameter verwenden wir wiederum den Rückgabewert der systime-Funktion:

user$ awk 'BEGIN {
    print "Heute ist der " strftime("%d.%m.%Y", systime());
}'
Heute ist der 25.06.2024

Listing 6.33     Beispielanwendung für die Zeitformatierung

Format

Steht für

%y

das Jahr im zweistelligen Format (08)

%Y

das Jahr im vierstelligen Format (2024)

%m

den Monat (01–12)

%h

den Monat in dreistelliger Schreibweise (Jan, ..., Nov, Dez)

%d

den Tag des Monats (01–31)

%H

die Stunde im 24-Stunden-Format (00–24)

%I

die Stunde im 12-Stunden-Format (00–12)

%p

die Minute (00–59)

%D

das komplette Datum im Format »Monat/Tag/Jahr«

Tabelle 6.3     Möglichkeiten der Zeitformate

6.3.17    Arrays und String-Operationen

Auch Arrays finden in awk ein Zuhause. Dabei wird wie bei Variablen ein beliebiger Name vergeben, der das Array bezeichnet. Die Elemente werden in eckigen Klammern eingebettet: array[7] = "Linux";.

awk verfügt über sogenannte assoziative Arrays. Das bedeutet, die Elemente können über String-Werte (statt nur über Indizes) angesprochen werden, wie es auch in Perl und einigen anderen Programmiersprachen möglich ist.

Des Weiteren kann der weiter oben angesprochene in-Operator dazu verwendet werden, die Arrays zu durchsuchen:

user$ awk '
BEGIN {
        array[0] = "InhaltA";
        array[1] = "InhaltB";

        print "Durchlaufe die Elemente 0-1 von array:"
        for(val in array)
                print "Element "val ": "array[val];
} END {
        tier["Hund"] = "Wuff";
        tier["Katze"] = "Miau";
        tier["Schaf"] = "Maeh";

        print "Durchlaufe die Elemente (nun mit Bezeichnern"
        print "statt Indizes) von tier:"
        for(val in tier)
                print val ": "tier[val];
}'
Durchlaufe die Elemente 0-1 von array:
Element 0: InhaltA
Element 1: InhaltB
Durchlaufe die Elemente (nun mit Bezeichnern
statt Indizes) von tier:
Schaf: Maeh
Hund: Wuff
Katze: Miau

Listing 6.34     Arrays in awk

Einzelne Array-Elemente können über das Schlüsselwort delete gelöscht werden: delete array[element];. Probieren Sie den obigen Code doch einfach einmal aus und fügen Sie zur Übung ein delete array["Katze"]; vor der for-Schleife ein.

6.3.18    Was noch fehlt

Dies war eine kurze Einführung in awk. Wir haben Ihnen einige durchaus grundlegende Eigenschaften dieser Sprache vorenthalten. Andere Autorinnen und Autoren schreiben über diese Sprache ganze (und zudem durchaus lesenswerte) Bücher – doch genau dies möchten wir an dieser Stelle nicht tun. Dafür legen wir Ihnen weiterführende Lektüre ans Herz. Sehen Sie sich auch die exzellente Manpage awk(1) an.