awk — Feldbasierte Textverarbeitung und Auswertung
Spaltenbasierten Text mit awk verarbeiten — Felder extrahieren, nach Mustern filtern, mit Arrays aggregieren und Ausgaben formatieren.
awk ist eine kleine, aber vollständige Programmiersprache für spaltenbasierten Text. Es liest die Eingabe Zeile für Zeile, zerlegt jede Zeile in Felder ($1, $2, … $NF) und führt dein Programm für jeden Datensatz aus – damit ist es die naheliegende Wahl für Logdateien, CSV-/TSV-Daten und tabellarische Befehlsausgaben. Während grep Zeilen findet und sed sie umschreibt, kannst du mit awk nach Feldern selektieren, Summen und Mittelwerte berechnen, mit assoziativen Arrays Häufigkeitstabellen aufbauen und sauber formatierte Auswertungen ausgeben. Dieser Guide behandelt das Wesentliche: Felder und Trenner, Mustererkennung, Bedingungen, BEGIN-/END-Blöcke, Arithmetik, String-Funktionen und die Rezepte, zu denen du immer wieder greifst.
Grundlagen
awk '{print}' <file> — Gibt jede Zeile der Datei aus (wie cat).
awk '{print}' data.txtawk '{print $0}' <file> — Gibt jede ganze Zeile aus. $0 steht für die gesamte Zeile.
awk '{print $0}' data.txtawk '{print $1}' <file> — Gibt das erste Feld (die erste Spalte) jeder Zeile aus. Felder werden per Whitespace getrennt.
awk '{print $1}' access.logawk '{print $1, $3}' <file> — Gibt bestimmte Felder durch ein Leerzeichen getrennt aus (Output Field Separator).
awk '{print $1, $3}' data.txtawk '{print $NF}' <file> — Gibt das letzte Feld jeder Zeile aus. NF ist die Anzahl der Felder.
awk '{print $NF}' access.logawk '{print $(NF-1)}' <file> — Gibt das vorletzte Feld jeder Zeile aus.
awk '{print $(NF-1)}' data.txtawk 'NR==<n>' <file> — Gibt nur eine bestimmte Zeilennummer aus. NR ist die Nummer des aktuellen Datensatzes (der aktuellen Zeile).
awk 'NR==5' data.txtawk 'NR>=<n> && NR<=<m>' <file> — Gibt einen Zeilenbereich aus.
awk 'NR>=10 && NR<=20' data.txtFeldtrenner
awk -F'<sep>' '{print $1}' <file> — Legt einen eigenen Eingabe-Feldtrenner fest.
awk -F',' '{print $1}' data.csvawk -F':' '{print $1, $3}' <file> — Verwendet den Doppelpunkt als Feldtrenner (nützlich für /etc/passwd).
awk -F':' '{print $1, $3}' /etc/passwdawk -F'\t' '{print $2}' <file> — Verwendet den Tabulator als Feldtrenner für TSV-Dateien.
awk -F'\t' '{print $2}' data.tsvawk -F'[,;:]' '{print $1}' <file> — Verwendet eine Regex als Feldtrenner (trifft auf jedes der Zeichen).
awk -F'[,;:]' '{print $1}' mixed.txtawk 'BEGIN{FS=","; OFS="\t"} {print $1, $2}' <file> — Legt Eingabe-Feldtrenner (FS) und Ausgabe-Feldtrenner (OFS) fest.
awk 'BEGIN{FS=","; OFS="\t"} {print $1, $2}' data.csvawk -v OFS=',' '{$1=$1; print}' <file> — Ändert den Ausgabetrenner. Der Trick $1=$1 zwingt awk, die Zeile neu aufzubauen.
awk -v OFS=',' '{$1=$1; print}' whitespace.txtMustererkennung
awk '/<pattern>/' <file> — Gibt Zeilen aus, die auf ein Regex-Muster passen (wie grep).
awk '/ERROR/' log.txtawk '!/<pattern>/' <file> — Gibt Zeilen aus, die NICHT auf ein Muster passen (wie grep -v).
awk '!/^#/' config.iniawk '$<n> ~ /<pattern>/' <file> — Gleicht ein Muster gegen ein bestimmtes Feld ab.
awk '$3 ~ /error/' log.txtawk '$<n> !~ /<pattern>/' <file> — Gibt Zeilen aus, bei denen ein bestimmtes Feld NICHT auf ein Muster passt.
awk '$1 !~ /^192\.168/' access.logawk '/<start>/,/<end>/' <file> — Gibt Zeilen zwischen zwei Mustern aus (einschließender Bereich).
awk '/BEGIN/,/END/' config.txtawk '/pattern/ {print $2}' <file> — Gibt nur ein bestimmtes Feld aus Zeilen aus, die auf ein Muster passen.
awk '/GET/ {print $7}' access.logBedingungen & Vergleiche
awk '$<n> == "<value>"' <file> — Gibt Zeilen aus, bei denen ein Feld einer bestimmten Zeichenkette entspricht.
awk '$3 == "ERROR"' log.txtawk '$<n> != "<value>"' <file> — Gibt Zeilen aus, bei denen ein Feld einem Wert nicht entspricht.
awk '$1 != "localhost"' hosts.txtawk '$<n> > <value>' <file> — Gibt Zeilen aus, bei denen ein numerisches Feld einen Schwellwert überschreitet.
awk '$5 > 1000' data.txtawk '$<n> > <value> && $<m> < <value>' <file> — Kombiniert mehrere Bedingungen mit && (UND).
awk '$3 > 100 && $4 < 500' sales.txtawk '$<n> > <value> || $<m> == "<value>"' <file> — Kombiniert Bedingungen mit || (ODER).
awk '$5 > 1000 || $3 == "CRITICAL"' log.txtawk 'NF > 0' <file> — Gibt nur nicht-leere Zeilen aus (Zeilen mit mindestens einem Feld).
awk 'NF > 0' messy.txtawk 'NF == <n>' <file> — Gibt Zeilen mit genau N Feldern aus.
awk 'NF == 4' data.txtawk 'length > <n>' <file> — Gibt Zeilen aus, die länger als N Zeichen sind.
awk 'length > 80' source.pyBEGIN- & END-Blöcke
awk 'BEGIN {print "Header"} {print} END {print "Footer"}' <file> — Führt Code vor der Verarbeitung (BEGIN) und nach allen Zeilen (END) aus.
awk 'BEGIN {print "Name\tScore"} {print $1"\t"$2} END {print "---"}' results.txtawk 'BEGIN {<init>} {<body>} END {<summary>}' <file> — Klassische awk-Struktur: initialisieren, jede Zeile verarbeiten, dann zusammenfassen.
awk 'BEGIN {sum=0} {sum+=$1} END {print "Total:", sum}' numbers.txtawk 'END {print NR}' <file> — Gibt die Gesamtzahl der Zeilen einer Datei aus.
awk 'END {print NR}' data.txtawk 'END {print NR, "lines,", NF, "fields in last line"}' <file> — Gibt nach der Verarbeitung aller Zeilen eine Zusammenfassung aus.
awk 'END {print NR, "lines processed"}' access.logArithmetik & Aggregation
awk '{sum += $<n>} END {print sum}' <file> — Summiert alle Werte einer bestimmten Spalte.
awk '{sum += $3} END {print sum}' sales.txtawk '{sum += $<n>} END {print sum/NR}' <file> — Berechnet den Durchschnitt einer Spalte.
awk '{sum += $2} END {print "Average:", sum/NR}' scores.txtawk 'BEGIN {max=0} {if ($<n> > max) max=$<n>} END {print max}' <file> — Findet den größten Wert in einer Spalte.
awk 'BEGIN {max=0} {if ($3 > max) max=$3} END {print "Max:", max}' data.txtawk 'NR==1 || $<n> < min {min=$<n>} END {print min}' <file> — Findet den kleinsten Wert in einer Spalte.
awk 'NR==1 || $2 < min {min=$2} END {print "Min:", min}' data.txtawk '{count[$<n>]++} END {for (k in count) print k, count[k]}' <file> — Zählt das Vorkommen jedes eindeutigen Werts in einer Spalte (Häufigkeitstabelle).
awk '{count[$1]++} END {for (ip in count) print ip, count[ip]}' access.logawk '{sum[$1] += $2} END {for (k in sum) print k, sum[k]}' <file> — Summiert Werte gruppiert nach einer Schlüsselspalte (wie SQL GROUP BY).
awk '{sum[$1] += $3} END {for (dept in sum) print dept, sum[dept]}' expenses.txtString-Funktionen
awk '{print length($0)}' <file> — Gibt die Länge jeder Zeile aus.
awk '{print length($0), $0}' data.txtawk '{print toupper($0)}' <file> — Wandelt jede Zeile in Großbuchstaben um.
awk '{print toupper($0)}' input.txtawk '{print tolower($0)}' <file> — Wandelt jede Zeile in Kleinbuchstaben um.
awk '{print tolower($0)}' input.txtawk '{gsub(/<pattern>/, "<replacement>"); print}' <file> — Globale Ersetzung in jeder Zeile (wie sed s///g).
awk '{gsub(/foo/, "bar"); print}' input.txtawk '{sub(/<pattern>/, "<replacement>"); print}' <file> — Ersetzt nur das erste Vorkommen in jeder Zeile.
awk '{sub(/^[ \t]+/, ""); print}' messy.txtawk '{print substr($0, <start>, <length>)}' <file> — Extrahiert eine Teilzeichenkette aus jeder Zeile (1-basierte Startposition).
awk '{print substr($0, 1, 10)}' data.txtawk '{n=split($0, arr, "<sep>"); print arr[1]}' <file> — Zerlegt eine Zeichenkette anhand eines Trenners in ein Array. Gibt die Anzahl der Elemente zurück.
awk '{n=split($0, parts, ","); print parts[2]}' data.csvawk 'match($0, /<pattern>/) {print substr($0, RSTART, RLENGTH)}' <file> — Extrahiert den von einer Regex gefundenen Teil. Setzt RSTART und RLENGTH.
awk 'match($0, /[0-9]+\.[0-9]+/) {print substr($0, RSTART, RLENGTH)}' data.txtFormatierte Ausgabe
awk '{printf "%-20s %s\n", $1, $2}' <file> — Gibt mit printf formatierte, ausgerichtete Spalten aus.
awk '{printf "%-20s %10s\n", $1, $2}' data.txtawk '{printf "%05d %s\n", NR, $0}' <file> — Gibt Zeilennummern auf 5 Stellen mit Nullen aufgefüllt aus.
awk '{printf "%05d %s\n", NR, $0}' script.shawk '{printf "%.2f\n", $1}' <file> — Formatiert Zahlen mit 2 Nachkommastellen.
awk '{printf "$%.2f\n", $3}' prices.txtawk '{printf "%s,%s,%s\n", $1, $2, $3}' <file> — Wandelt durch Whitespace getrennte Daten in CSV um.
awk '{printf "%s,%s,%s\n", $1, $2, $3}' data.txtawk -v OFS='\t' '{$1=$1; print}' <file> — Wandelt beliebige Whitespace-Trennung in tabulatorgetrennte Ausgabe um.
awk -v OFS='\t' '{$1=$1; print}' data.txtVariablen & Zuweisung
awk -v <var>=<value> '{print <var>, $1}' <file> — Übergibt eine externe Variable an das awk-Programm.
awk -v threshold=100 '$3 > threshold {print}' data.txtawk -v var="$SHELL_VAR" '{print var, $0}' <file> — Übergibt eine Shell-Variable an awk.
awk -v user="$USER" '{print user, $0}' log.txtawk '{$<n> = "<value>"; print}' <file> — Ersetzt den Wert eines bestimmten Felds und gibt die geänderte Zeile aus.
awk '{$2 = "REDACTED"; print}' users.txtawk '{$(NF+1) = "<value>"; print}' <file> — Hängt ein neues Feld an das Ende jeder Zeile an.
awk -v OFS=',' '{$(NF+1) = "new_col"; print}' data.csvArrays & Deduplizierung
awk '!seen[$0]++' <file> — Entfernt doppelte Zeilen unter Beibehaltung der Reihenfolge (wie sort -u, aber ohne zu sortieren).
awk '!seen[$0]++' list.txtawk '!seen[$<n>]++' <file> — Entfernt Duplikate anhand eines bestimmten Felds.
awk '!seen[$1]++' data.txtawk '{a[$1]+=$2} END {for (k in a) print k, a[k]}' <file> — Aggregiert Werte nach Schlüssel mithilfe eines assoziativen Arrays.
awk -F',' '{a[$1]+=$2} END {for (k in a) print k, a[k]}' sales.csvawk '{a[$1]++} END {for (k in a) if (a[k]>1) print k, a[k]}' <file> — Findet und gibt nur doppelte Einträge aus.
awk '{a[$1]++} END {for (k in a) if (a[k]>1) print k, a[k]}' access.logawk '{a[NR]=$0} END {for (i=NR; i>=1; i--) print a[i]}' <file> — Kehrt die Reihenfolge der Zeilen um (wie tac).
awk '{a[NR]=$0} END {for (i=NR; i>=1; i--) print a[i]}' data.txtMehrere Dateien & Pipelines
<command> | awk '{print $<n>}' — Extrahiert eine bestimmte Spalte aus einer Befehlsausgabe.
ps aux | awk '{print $1, $11}'<command> | awk 'NR>1 {print $<n>}' — Extrahiert eine Spalte aus einer Befehlsausgabe und überspringt die Kopfzeile.
df -h | awk 'NR>1 {print $1, $5}'awk 'FNR==1 {print "--- " FILENAME " ---"} {print}' <file1> <file2> — Verarbeitet mehrere Dateien mit einem Trenner. FNR wird pro Datei zurückgesetzt, FILENAME enthält den Namen.
awk 'FNR==1 {print "--- " FILENAME " ---"} {print}' *.logawk '{print > "output_" $<n> ".txt"}' <file> — Teilt eine Datei anhand eines Feldwerts in mehrere Dateien auf.
awk -F',' '{print > "dept_" $1 ".csv"}' employees.csvawk '{print | "sort"}' <file> — Leitet die awk-Ausgabe an einen externen Befehl weiter.
awk '{print $1}' access.log | sort | uniq -c | sort -rnHäufige Rezepte
awk -F',' 'NR==1 {for (i=1;i<=NF;i++) header[i]=$i} NR>1 {for (i=1;i<=NF;i++) print header[i]": "$i; print ""}' <file> — Stellt CSV-Daten vertikal dar, mit den Spaltenüberschriften als Beschriftung.
awk -F',' 'NR==1 {for (i=1;i<=NF;i++) h[i]=$i} NR>1 {for (i=1;i<=NF;i++) print h[i]": "$i; print ""}' users.csvawk '{for (i=NF; i>0; i--) printf "%s ", $i; printf "\n"}' <file> — Kehrt die Feldreihenfolge in jeder Zeile um.
awk '{for (i=NF; i>0; i--) printf "%s ", $i; printf "\n"}' data.txtawk 'NR==FNR {a[$1]; next} $1 in a' <file1> <file2> — Gibt Zeilen aus file2 aus, deren erstes Feld in file1 vorkommt (wie ein Join/Lookup).
awk 'NR==FNR {a[$1]; next} $1 in a' ids.txt data.txtawk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print sum}' <file> — Summiert alle Felder einer Zeile (Zeilensummen).
awk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print $0, sum}' matrix.txtawk 'NR%<n>==0' <file> — Gibt jede N-te Zeile aus.
awk 'NR%5==0' data.txtawk '{$1=""; print substr($0,2)}' <file> — Entfernt das erste Feld und gibt den Rest der Zeile aus.
awk '{$1=""; print substr($0,2)}' data.txt Fazit
awk spielt seine Stärken in dem Moment aus, in dem deine Daten Spalten haben: awk '{print $2}', ein schnelles sum += $3 in einem END-Block oder eine einzeilige Häufigkeitstabelle mit count[$1]++ ersetzt oft ein ganzes Skript. Beginne mit -F, um den Feldtrenner zu setzen, stütze dich auf NR/NF für Zeilen- und Feldzahlen, und denke daran: awk liest die Eingabe, ohne deine Dateien zu verändern – die Ausgabe geht nach stdout, leite sie also bewusst um, wenn du Ergebnisse zurückschreibst. Eine häufige Stolperfalle: awk-Regex-Literale kennen kein /i-Flag, für eine Suche ohne Beachtung der Groß-/Kleinschreibung nutzt du daher gawks IGNORECASE=1 oder tolower(). Für alles jenseits weniger Zeilen ist GNU-awk (gawk) die leistungsfähigste und am weitesten verbreitete Implementierung.
Weiterführende Links
- GNU-awk-(gawk)-Handbuch – umfassende Referenz zur Sprache (englisch)
- ubuntuusers-Wiki: awk – deutschsprachige Einführung mit praktischen Beispielen