awk ist mehr als ein Befehl — es ist eine kleine Programmiersprache, spezialisiert auf spaltenorientierte Texte. Per Default zerlegt awk jede Eingabezeile in Felder ($1, $2, …) und wendet Regeln nach dem Schema Pattern → Action an. Damit werden Logs gefiltert, CSV-Spalten extrahiert, Summen gezogen und Häufigkeiten gezählt — alles in einem einzigen Einzeiler.

Was awk macht

awk liest Text zeilenweise, zerlegt jede Zeile automatisch an einem Field Separator (Default: Whitespace) in Felder und führt eine Reihe von Regeln darauf aus. Eine Regel besteht aus einem Pattern und einer Action — das Pattern entscheidet, ob die Action läuft, und die Action sagt, was zu tun ist. Fehlt das Pattern, gilt die Action für jede Zeile; fehlt die Action, wird die Zeile ausgegeben.

Diese Doppelnatur macht awk zum Klassiker für strukturierte Daten: Logs, /etc/passwd-ähnliche Konfigdateien, CSV-Exports, ps-Ausgaben, df-Statistiken. Überall, wo jede Zeile ein Datensatz und jedes Whitespace-getrennte Stück ein Feld ist, ist awk das richtige Werkzeug.

Bash Erste Spalte einer Datei ausgeben
awk '{print $1}' datei.txt

Drei awk-Implementationen

Hinter dem Namen awk stecken mehrere Implementierungen mit unterschiedlichem Funktionsumfang. Je nach System ruft awk eine andere Variante auf — was bei portablen Skripten zur Falle werden kann.

ImplementationHerkunftEigenschaften
Original awkAho/Weinberger/Kernighan, 1977Der Klassiker; minimaler Funktionsumfang, kaum noch direkt im Einsatz
nawk (“new awk”)1985, POSIX-BasisFunktionen, mehrdimensionale Arrays, dynamische Regex
gawk (GNU awk)GNU-ProjektDefault auf den meisten Linux-Distributionen; viele Erweiterungen
BSD awkmacOS, FreeBSDSchlanker als gawk, teils inkompatibel

gawk bringt die meisten Extras mit: gensub, length(array), Netzwerk-I/O, Time-Funktionen, Internationalisierung. Auf Linux ist awk meist ein Symlink auf gawk. Auf macOS dagegen ist awk BSD-awk — wer GNU-Features nutzt, sollte sie portabel halten oder via Homebrew gawk installieren.

Bash Welche awk-Variante läuft hier?
awk --version 2>&1 | head -1

Grundsyntax: Pattern { Action }

Jede awk-Regel folgt dem Schema Pattern { Action }. Beide Teile sind optional, aber mindestens einer muss da sein. Ein leeres Pattern bedeutet “trifft auf jede Zeile zu”, eine leere Action bedeutet “gib die Zeile aus” (also { print }).

Bash Erste Spalte ausgeben
awk '{print $1}' file
Bash Nur Zeilen mit ERROR (kein Action-Block = print)
awk '/ERROR/' log
Bash Nur Zeile 5 ausgeben
awk 'NR==5' file

Mehrere Regeln werden einfach hintereinander geschrieben — awk probiert für jede Eingabezeile alle Regeln in der Reihenfolge ihres Auftretens. Das Skript steht entweder direkt in einfachen Quotes auf der Kommandozeile oder in einer Datei, die mit -f script.awk geladen wird.

Felder und Spezialvariablen

Die Stärke von awk liegt in seinen vordefinierten Variablen. Sie geben Zugriff auf Felder, Zeilennummern und steuern, wie Ein- und Ausgabe getrennt werden.

VariableBedeutung
$0Die komplette aktuelle Zeile
$1, $2, … $NDie einzelnen Felder der Zeile
NFNumber of Fields — Anzahl der Felder in der aktuellen Zeile
NRNumber of Records — laufende Zeilennummer über alle Eingaben
FNRWie NR, aber pro Datei zurückgesetzt
FSField Separator (Eingabe), Default Whitespace
OFSOutput Field Separator, Default Leerzeichen
RSRecord Separator (Eingabe), Default Newline
ORSOutput Record Separator, Default Newline
FILENAMEName der aktuell gelesenen Datei

FS setzt man entweder per -F auf der Kommandozeile oder im BEGIN-Block. $NF ist ein Idiom für “letztes Feld”, $(NF-1) für das vorletzte. Das Feld-Indexing ist 1-basiert — $0 ist die ganze Zeile, nicht das erste Feld.

Bash Field Separator auf Komma setzen
awk -F',' '{print $2}' daten.csv
Bash FS und OFS im BEGIN-Block
awk 'BEGIN{FS=":"; OFS="|"} {print $1, $7}' /etc/passwd

BEGIN und END

Zwei spezielle Patterns laufen nicht pro Zeile, sondern einmal: BEGIN vor der ersten Zeile, END nach der letzten. Damit baut man Header, Footer, Initialisierungen und Summen.

Bash Summe der zweiten Spalte
awk 'BEGIN{sum=0} {sum+=$2} END{print sum}' zahlen.txt

BEGIN ist der richtige Ort, um FS, OFS und eigene Variablen zu setzen, Header auszugeben oder Lookup-Tabellen zu initialisieren. END summiert auf, druckt Statistiken oder iteriert über assoziative Arrays, die während des Hauptlaufs gefüllt wurden. Mehrere BEGIN- und END-Blöcke sind erlaubt und werden in Reihenfolge abgearbeitet.

Bash Header- und Footer-Block
awk 'BEGIN{print "Start"} {print NR, $0} END{print "Total:", NR}' file

Pattern-Typen

Patterns sind nicht auf Regex beschränkt. awk kennt mehrere Formen, die sich kombinieren lassen.

PatternBedeutungBeispiel
/REGEX/Zeile matcht Regex/ERROR/
$N OPERATOR WERTFeld-Vergleich$3 > 100
/START/,/END/Range zwischen zwei Patterns/BEGIN/,/END/
! /REGEX/Negation!/DEBUG/
PAT1 && PAT2Logisches UND$3 > 100 && /ERROR/
PAT1 || PAT2Logisches ODER/WARN/ || /ERROR/
NR==NZeilennummer-MatchNR==1 (nur Header)

Range-Patterns sind besonders praktisch für das Extrahieren von Blöcken — alles zwischen zwei Markern, etwa zwischen BEGIN_LOG und END_LOG. Das Range-Pattern bleibt aktiv, bis das End-Pattern matcht, und schließt beide Marker mit ein.

Bash Nur ERROR-Zeilen mit Spalte 3 über 100
awk '/ERROR/ && $3 > 100 {print $0}' app.log
Bash Block zwischen Markern extrahieren
awk '/BEGIN_BLOCK/,/END_BLOCK/' input.txt

Kontrollstrukturen und Built-ins

Innerhalb der Action stehen alle klassischen Kontrollstrukturen zur Verfügung: if/else, for, while, do-while, break, continue. Variablen brauchen keine Deklaration — werden sie genutzt, existieren sie. Strings und Zahlen werden bei Bedarf automatisch konvertiert.

Bash if/else und for-Schleife über alle Felder
awk '{
  if ($3 > 100) print $1, "hoch"
  else print $1, "niedrig"
  for (i = 1; i <= NF; i++) total += $i
}' werte.txt

Die wichtigsten eingebauten Funktionen:

FunktionWirkung
length(s)Länge eines Strings (oder ganzer Zeile bei length())
substr(s, m, n)Teilstring ab Position m, optional n Zeichen
split(s, arr, sep)String in Array zerlegen
sub(re, repl, s)Erste Regex-Trefferstelle ersetzen
gsub(re, repl, s)Alle Regex-Trefferstellen ersetzen
match(s, re)Position des ersten Regex-Treffers
index(s, sub)Position eines Substrings
printf / sprintfFormatierte Ausgabe wie in C
int(x), rand(), srand()Numerische Helfer

printf mit %-20s %10.2f\n formatiert Spalten sauber — wesentlich praeziser als print mit einfachem Komma.

Assoziative Arrays

Das Killer-Feature von awk: Arrays sind immer assoziativ. Jeder String, jede Zahl kann als Schlüssel dienen — keine Initialisierung, keine Größen-Definition. Damit werden Häufigkeitsanalysen zu Einzeilern.

Bash Häufigkeit pro Wert in Spalte 1
awk '{count[$1]++} END {for (k in count) print count[k], k}' log.txt

count[$1]++ zählt für jeden Wert in Spalte 1 hoch — beim ersten Auftreten ist count[$1] undefiniert, wird aber implizit als 0 behandelt und auf 1 erhöht. Im END-Block iteriert for (k in count) über alle Schlüssel.

Bash Zugriffe pro IP in einem Webserver-Log
awk '{ips[$1]++} END {for (ip in ips) print ips[ip], ip}' access.log | sort -rn | head -20

Die Iterations-Reihenfolge ist nicht garantiert — awk nutzt intern eine Hashtabelle. Wer sortiert ausgeben will, leitet die Ausgabe an sort weiter (siehe Beispiel oben). gawk kennt zusätzlich PROCINFO["sorted_in"], mit dem sich for (k in arr) direkt sortiert iterieren lässt.

Praxis-Patterns

Die Einzeiler, die im Alltag immer wieder gebraucht werden — als Vorlage zum Anpassen.

Bash Spalten-Summe
awk '{s+=$2} END{print s}' werte.txt
Bash Eine CSV-Spalte herauspicken
awk -F',' '{print $3}' daten.csv
Bash Häufigkeitsanalyse, sortiert
awk '{count[$1]++} END {for (k in count) print count[k], k}' log | sort -rn
Bash Durchschnitt einer Spalte
awk '{s+=$1; n++} END {print s/n}' zahlen.txt
Bash Bedingtes Drucken: nur Zeilen mit Spalte 3 > 1000
awk '$3 > 1000 {print $1, $3}' werte.txt
Bash passwd umformatieren: Username | Shell
awk -F: 'BEGIN{OFS="|"} {print $1, $7}' /etc/passwd
Bash Lookup-Tabelle aus File1, Filter auf File2
awk 'NR==FNR{a[$1]=1; next} $2 in a' liste.txt daten.txt

Das letzte Pattern ist ein Klassiker: Im ersten Durchlauf (NR==FNR — globale Zeilennummer gleich Datei-Zeilennummer, also erste Datei) wird ein Lookup-Set aufgebaut. next springt zur nächsten Zeile, ohne weitere Regeln zu prüfen. Sobald die zweite Datei beginnt, ist NR > FNR und nur die zweite Regel greift: gibt die Zeile aus, wenn Spalte 2 im Set steht.

Bash printf mit fester Formatierung
awk '{printf "%-20s %10.2f\n", $1, $2}' werte.txt

Stolperfallen

Default-FS ist Whitespace mit Sonderverhalten

Der Default-Field-Separator ist nicht einfach ein Leerzeichen, sondern “Whitespace mit Speziallogik”: mehrere Leerzeichen oder Tabs zählen als ein Trenner, führende und abschließende Whitespaces werden ignoriert. Sobald man -F',' oder -F':' setzt, gilt diese Magie nicht mehr — zwei Kommas hintereinander erzeugen ein leeres Feld, führendes Komma erzeugt ein leeres $1. Beim Wechsel von Default-FS auf einen Zeichen-FS ändert sich also das Feld-Modell.

print mit Komma vs. ohne Komma

print "x" "y" gibt xy aus — Strings werden ohne Komma einfach konkateniert. print "x", "y" dagegen baut eine OFS-getrennte Liste, also x y mit Default-OFS. Wer print $1 $2 schreibt, bekommt die Felder zusammengeklebt; print $1, $2 setzt das OFS dazwischen. Diese stille Bedeutungsverschiebung ist eine der häufigsten Anfänger-Quellen für kaputte Ausgaben.

Variablen brauchen kein Dollarzeichen — nur Felder

In awk-Skripten ist name = "Linus"; print name korrekt — kein $ vor name. Das $ referenziert ausschließlich Felder: $1 ist Feld 1, $NF ist das letzte Feld, $(i+1) ist das Feld an der Position i+1. Wer aus Shell-Gewohnheit $name schreibt, erhält das Feld an Position name — und da name als String oder 0 interpretiert wird, ist $name gleich $0, also die ganze Zeile. Kein Fehler, aber auch nicht gemeint.

gsub modifiziert $0 als Side-Effect

gsub(/foo/, "bar") ohne dritten Parameter operiert auf $0 — und damit ändert sich nicht nur der String, sondern auch die Felder $1, $2, … werden neu zerlegt. awk '/foo/ {gsub(/foo/, "bar"); print}' druckt die modifizierte Zeile, was meistens gewollt ist. Aber eine spätere Regel sieht eine andere Zeile als das Original — was bei mehrstufigen Skripten überraschen kann. Wer nur ersetzen, aber nicht den Record verändern will, nimmt eine eigene Variable: s = $0; gsub(/foo/, "bar", s).

Float-Vergleich mit == ist gefährlich

if ($1 == 0.1 + 0.2) ... schlägt fehl — awk rechnet wie C in IEEE-754-Floats, und 0.1 + 0.2 ist nicht exakt 0.3. Für Geld- oder Vergleichswerte entweder mit Toleranz arbeiten (if ((a-b)^2 &lt; 1e-9)) oder die Werte als Integer-Cents führen. Für Ausgabe immer printf "%.2f" verwenden, sonst druckt awk 0.30000000000000004 und ähnliche Schönheiten.

GNU-Extensions brechen auf macOS BSD-awk

length(arr) (Anzahl der Schlüssel im Array), gensub, PROCINFO, asort, asorti, strftime, systime und einige andere sind GNU-spezifisch. Auf macOS oder FreeBSD bricht ein Skript, das diese nutzt, mit “syntax error” oder unerwartetem Verhalten ab. Wer portable Skripte schreibt, beschränkt sich auf POSIX-awk — oder installiert via Homebrew gawk und ruft das Skript explizit damit auf.

Single-Quotes innerhalb des Skripts sind ein Krampf

Das awk-Skript steht typischerweise in einfachen Quotes — awk '...'. Will man im Skript selbst ein einfaches Anführungszeichen nutzen, muss man die Shell-Quotes brechen: awk 'BEGIN{print "it'\''s"}'. Sauberer ist die -v-Variante: awk -v q="'" 'BEGIN{print "it" q "s"}' — Variablen werden mit -v name=wert von außen reingegeben und verhalten sich wie normale awk-Variablen.

awk vs. perl vs. python — Komplexität ist die Grenze

Für Einzeiler und einfache Spalten-Logik ist awk unschlagbar kompakt. Sobald aber JSON, reguläre Ausdrücke mit Backreferences, Datei-Joins über mehrere Dimensionen, externe HTTP-Requests oder mathematische Bibliotheken ins Spiel kommen, lohnt sich der Wechsel zu perl, python oder ruby. Faustregel: wenn das awk-Skript länger als 20 Zeilen wird, lieber portieren — die Lesbarkeit leidet schneller als bei Sprachen mit klassischer Struktur.

awk parst KEIN quoted CSV

Echte CSV-Dateien können Felder in Anführungszeichen enthalten, in denen Kommas vorkommen: "Smith, John",42,"New York". awk -F',' zerlegt das in fünf Felder, nicht in drei. Für korrektes CSV-Parsing braucht es spezialisierte Werkzeuge wie csvkit (csvcut, csvgrep), mlr (Miller) oder xsv. awk ist ein Tool für einfache, klar getrennte Spaltenformate — für alles, was Quoting, Escaping oder mehrzeilige Felder kennt, gibt es bessere Optionen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Textverarbeitung

Zur Übersicht