Linux Basics - awk Grundlagen

coding, Linux

awk - ein mächtiges Linuxtool zur Verarbeitung von Textdaten #

Das Programm awk wurde in den 1970er Jahren bei den Bell Labs entwickelt und ist also schon etwas älter. Der Name des Programms leitet sich dabei von den Namen der drei Entwickler Aho, Weinberger und Kernighan ab. Das Programm selber wurde entwickelt um strukturierte Daten aus Dateien auszulesen und anderweitig auszugeben, beispielsweise zur Erstellung von Reports. Dabei ist awk mittlerweile so komplex dass es rein formal eine vollwertige Programmiersprache darstellt, das keyword hier ist formal denn obwohl man prinzipiell mit awk Programme schreiben könnte ist das tool nicht dafür ausgelegt und awk Programme sind eher als Kuriosität zu betrachten. Da awk mittlerweile ein paar Jahre auf dem Buckel hat, wird das ursprüngliche Programm heute eigentlich nicht mehr verwendet, sondern die meisten Linux Distributionen bringen Weiterentwicklungen von awk mit wie bspw. gawk, mawk oder nawk. Diese sind in der Regel alle miteinander kompatibel, erweitern aber das Grundprogramm um eine Reihe von Funktionen, short cuts und quality-of-life Verbesserungen.

Warum sollte ich awk verwenden? #

Egal wie man es dreht und wendet, awk ist schon ein wenig angestaubt aufgrund seines Alters. Trotzdem ist awk ein unglaublich starkes tool - sofern man sich innerhalb der Grenzen seines Einsatzbereiches bewegt. Sprachen wie Perl oder Python sind mit Sicherheit vielseitiger, aber wenn man “nur” Daten bspw. aus einer Log-Datei extrahieren möchte ist awk eines der besten tools die man nutzen kann da die Skripte relativ kurz sind (oft nur 1-2 Zeilen lang) und awk wesentlich schneller arbeitet als beispielsweise ein Python Skript. Ebenfalls eignet sich awk laut meiner Erfahrung ganz wunderbar zur Formatierung von Output, beispielsweise wenn man das Ergebnis einer SQL Abfrage ein wenig umformatieren und aufhübschen möchte. Ein weiterer Faktor den man nicht unterschätzen darf ist die Tatsache dass awk in praktisch jeder Linux Distribution mitgeliefert wird und man nicht erst noch weitere Softwarepakete installieren muss.

Ein Awk-Programm besteht aus einer Reihe von Muster-Aktion Paaren. Das bedeutet, dass awk eine oder mehrere Dateien zeilenweise durchsucht und mit dem vorgegebenen Muster abgleicht. Wird eine passende Zeile gefunden, so wird die zugewiesene Aktion durchgeführt. Muster können hier beispielsweise durch regular expressions, Vergleichsoperationen auf Zahlen, Zeichenketten, Feldern, Variablen usw. vorgegeben sein.

Awk bietet also durchaus die Möglichkeit auch komplexere Skripte zu schreiben.

Arbeitsweise und Syntax von awk #

awk arbeitet indem es Daten zeilenweise einliest und anhand eines vorgegebenen Delimiters 1 in Felder zerlegt und diese dann ausgibt. Die Funktionsweise ist sed sehr ähnlich, aber erlaubt wesentlich komplexere Konstrukte.

Ein typischer awk Aufruf könnte beispielsweise so aussehen:

awk 'MUSTER { AKTION }' dateiname.txt

Dabei erfolgt der Aufruf von awk mittels des Programmnamens awk, einem sogenannten Muster-Aktionen-Paar und (in aller Regel) einer Datei auf welche die Operationen angewendet werden sollen (awk kann aber natürlich auch mit Daten aus einem Eingabestrom zurechtkommen).

Der Delimiter (Feld-Separator) #

Standardmäßig verwendet AWK einen oder mehrere aufeinanderfolgende Leerzeichen oder Tabs als Feld-Separator.

Man kann den Separator (Delimiter) jedoch frei bestimmen, was für die Analyse von Log-Dateien, die oft durch Doppelpunkte, Kommas oder Semikola getrennt sind, essenziell ist.

Der Feld-Separator wird mit der Option -F definiert:

# Trennt die Eingabe am Doppelpunkt
awk -F':' '{ print $1 }' /etc/passwd

Hierbei steht $1 für das erste Feld (Spalte), $2 für das zweite, und so weiter.

Muster-Aktionen-Paare im Detail #

Wie bereits erwähnt, besteht ein AWK-Programm aus Muster-Aktionen-Paaren. Eine Aktion wird nur ausgeführt, wenn das Muster für die aktuelle Zeile zutrifft. Fehlt das Muster, wird die Aktion auf jede Zeile angewendet. Fehlt die Aktion, wird die gesamte Zeile (print $0) ausgegeben, wenn das Muster zutrifft.

1. Muster: Reguläre Ausdrücke (RegEx) #

Die gebräuchlichste Form des Musters ist der reguläre Ausdruck. Er wird von Schrägstrichen (/) umschlossen.

Beispiel: Fehlermeldungen filtern #

Wenn Ihr eine Log-Datei nach Zeilen durchsuchen möchtet, die das Wort error enthalten, nutzt Ihr einen regulären Ausdruck:

# Durchsucht die gesamte Zeile ($0) nach dem Muster 'error'
awk '/error/ { print $0 }' logfile.txt

Da die Aktion die Standardaktion (print $0) ist, kann man diese sogar weglassen:

awk '/error/' logfile.txt

2. Muster: Vergleichsoperationen auf Feldern #

Hier wird eine Bedingung für den Inhalt oder Wert eines bestimmten Feldes gestellt.

Beispiel: HTTP-Status-Codes filtern #

Um alle Zeilen aus der access.log zu finden, bei denen der HTTP-Status-Code (Feld 9) gleich 404 ist, verwendet Ihr einen numerischen Vergleich:

# Zeigt alle Zeilen mit einem 404-Fehler
awk '$9 == 404 { print $0 }' access.log

3. Muster: BEGIN und END #

Diese beiden Muster sind bspw. für die Erstellung von Reports interessant, da sie Aktionen vor der eigentlichen Verarbeitung und nach Abschluss der Verarbeitung definieren. Dadurch kann man beispielsweise einer Auswertung eine Kopf- und Fußzeile hinzufügen:

awk '
BEGIN {

    # Setzt den Output Field Separator (OFS) für schönere Ausgabe

​    OFS="\t"

    # Gibt den Header aus

​    print "--- Start des Reports ---"
​    print "IP\tStatus\tLetztes Feld"
​    print "-------------------------"
}
{

    # Hauptaktion: Wird auf jede Zeile angewendet

​    print $1, $9, $NF
}
END {

    # Gibt die Fußzeile aus und meldet, wie viele Zeilen verarbeitet wurden

​    print "-------------------------"
​    print "Ende des Reports. Verarbeitete Zeilen: " NR
}' access.log

Felder und Variablen #

AWK definiert beim Einlesen jeder Zeile automatisch einige spezielle Variablen:

VariableBeschreibung
$0Die komplette eingelesene Zeile.
$1, $2, $NDas erste, zweite oder N-te Feld (die N-te Spalte).
NFDie Anzahl der Felder in der aktuellen Zeile (Number of Fields).
NRDie Zeilennummer der aktuellen Zeile (Number of the Record).
FSDer Feld-Separator (Field Separator). Kann mit -F überschrieben werden.

Beispiel: Felder filtern und ausgeben #

Angenommen, Sie haben eine Log-Datei (access.log) mit Leerzeichen als Separator, und Sie möchten nur die IP-Adresse (Feld 1) und den HTTP-Status-Code (Feld 9) ausgeben.

# Access.log: 192.168.1.1 - [10/Feb/2023:10:00:00] "GET /index.html" 200 ...
awk '{ print "IP: " $1, " | Status: " $9 }' access.log
# Ausgabe: IP: 192.168.1.1 | Status: 200

Beispiel: Das letzte Feld ausgeben #

In manchen Log-Dateien ändert sich die Anzahl der Felder. Um immer das letzte Feld auszugeben (z.B. eine Fehlermeldung), nutzt man die Variable NF:

# Gibt immer das letzte Feld der Zeile aus
awk '{ print $NF }' logfile.txt

sed und awk arbeiten wunderbar zusammen #

Man kann awk sehr gut mit sed kombinieren. Anbei ein paar Beispiele. Eine kurze sed Einführung gibt es im Artikel zu sed.

Weitere Informationen #

Dieser Artikel kann und soll nur einen groben Überblick geben. Umfangreichere Informationen und eine ganze Liste der vordefinierten Variablen, Parameter etc. findet man entweder lokal in den man und info pages oder aber online.

Als refresher: die man pages ruft man auf über man awk und die info pages über info awk.

Als einfache und gute Alternative kann ich hier allerdings noch das Tool tldr empfehlen, über das ebenfalls nochmal ein Artikel folgen wird.

Fußnoten #

  1. Ein Delimiter kann beispielsweise ein Leerzeichen, Tab, Doppelpunkt, Komma o.ä. sein und in awk frei bestimmt werden.