awk – mehr als nur ein Hilfsprogramm

AWK ist eines meiner liebsten Bash-Tools. Eigentlich ist es auch mehr als ein Tool. Es ist eine Programmiersprache, mit der man Textdateien bearbeiten und auswerten kann. Das besondere an awk ist aber, dass es sich nicht wie eine Programmiersprache anfühlt, da man in der Regel auf Programmstrukturen verzichten kann. Denn awk liest eine Datei ein und unterteilt die Zeile in einzelne Elemente anhand eines Trennzeichens. Das Standardtrennzeichen ist der „Whitespace“ also ein Konglomerat aus Leerzeichen, Tabulatoren etc. Der Whitespace betrachtet auch mehrere aufeinander folgende Leerzeichen als nur einen Trenner.

Nehmen wir einmal folgenden Satz:

Harald geht gerne spielen.

awk unterteilt diesen Text in einzelne Elemente und weist sie Variablen zu. Variablen kann man sich vorstellen wie Schachteln in die etwas hineingelegt wird.

$0 Gesamte Zeile: Harald geht gerne spielen.
$1 Erstes Element: Harald
$2 Zweites Element: geht
$3 Drittes Element: gerne
$4 Viertes Element: spielen.

„awk“ kann aber eine Zeile auch von hinten her lesen:

$NF Letztes Element: spielen
$(NF-1) Vorletztes Element: gerne
$(NF-2) Drittletztes Element: geht

Du kannst natürlich beliebig viele Felder zurückzählen. Wichtig sind dabei die mathematischen Klammern um die Berechnung. Diese Klammern sorgen dafür, dass zuerst die Feldnummer berechnet wird, bevor auf das Feld zugegriffen wird. Stell Dir vor, eine Tabelle hat 20 Felder. In der Variablen NF steht also 20. Ohne Berechnung wird also aus $NF $20. Wenn Du nun das drittletzte Feld haben willst, soll zuerst NF -2 berechnet werden, also 20-2 = 18. Danach steht in der Klammer die 18. Aus $(NF-2) wird also $(18) und nach Abzug der Klammern $18. Wenn Du dagegen die Klammer weglässt wird vom Inhalt von $20 zwei abgezogen. Wenn also in $20 z.B. das Wort Haus steht würde eben zuerst das Wort Haus aus $20 herausgeholt und dann zwei abgezogen. Das wäre eine sinnlose Berechnung.

Mit dem Ausgabebefehl print kannst Du nun die einzelnen Felder ausgeben lassen. Infolgendem Beispiel siehst Du wie „awk“ einen Text aufteilt und Du die Reihenfolge einzelner Felder ändern kannst. Leerzeichen oder Trennzeichen müssen im Printbefehl mit angegeben werden. Da es sich um Textausgaben handelt, müssen sie in doppelte Hochkommata eingeschlossen werden.

bashinho@mint ~ $ echo "Harald geht gerne spielen. "
Harald geht gerne spielen.
bashinho@mint ~ $ echo "Harald geht gerne spielen. " | awk '{ print $3 " " $4 " " $2 " " $1} '
gerne spielen. geht Harald

Verzichtest Du auf die Trennzeichen, würde die Ausgabe wie folgt aussehen.

bashinho@mint ~ $ echo "Harald geht gerne spielen . " | awk '{ print $3 $4 $2 $1} '
gernespielen.gehtHarald

Du kannst aber auch den Standard-Output Field Seperator mit einem Kommata triggern:
bashinho@mint ~ $ echo "Harald geht gerne spielen. " | awk '{ print $3 , $4 , $2 , $1} '
gerne spielen. geht Harald

Du kannst mit awk beliebige Textinhalte einfügen und auch Elemente weglassen, die nicht benötigt werden.

bashinho@mint ~ $ echo "Harald geht gerne spielen . " | awk '{ print "Bashinho " $2 " heim." } '
Bashinho geht heim.

Ist in dem Dokument ein anderer Trenner als der Whitespace definiert, wie zum Beispiel der Strichpunkt, so muss dieser Trenner explizit genannt werden. Hierfür gibt es den Parameter -F (für Field Seperator).

awk -F ';' '{print $1} '

Sind in einer Datei unterschiedliche Trenner vorhanden, ist es hilfreich zuerst die Trenner zu vereinheitlichen. Z.B. könnte in einer Liste stehen:

10.12.2015;Müller, Peter
12.12.2015;Meier, Klaus

Wenn Du es genau nimmst, hat dieser Datensatz vier unterschiedliche Trenner (Punkt, Strichpunkt, Komma und das Leerzeichen). Wenn Du auf die einzelnen Elemente zugreifen will, wäre es sinnvoll zuerst die Zeichen Punkt, Komma, Leerzeichen und Strichpunkt zu einem einzigen Trenner zusammenzuführen. Hierfür verwendest Du z.B. den Befehl tr. Es ist jedoch erstaunlich wie einfach die Vereinheitlichung von Trennern die Arbeit macht. Im Print-Befehl des awk kannst Du ja hinterher die einzelnen Elemente mit neuen Trennern versehen. (im folgenden Beispiel behalte ich den Trenner „Punkt“ weil ich das Datum nicht umformatieren will)

bashinho@mint ~ $ cat liste.txt | tr ";," " " | awk '{print $1,$2,$3}'
10.12.2015 Müller Peter
12.12.2015 Meier Klaus

Im folgenden Beispiel siehst Du das Entfernen aller Leerzeichen die am Anfang bzw am Ende einer Zeile stehen mit awk.

awk '{gsub(/^ *| *$/, "", $0);print $0}'

gsub ist ein Befehl zum Suchen und Ersetzen. Er erlaubt dabei die Verwendung von regulären Ausdrücken. Der Aufbau ist wie folgt: „gsub(/suchmuster/, „ersetzen“, Text/Variable)“. Das Suchmuster besagt einfach nur: „Wenn am Anfang der Zeile beliebig viele Leerzeichen stehen oder die Zeile mit beliebig vielen Leerzeichen endet.“ (In meinem Artikel über den über die regulären Ausdrücke, habe ich beschrieben wie dies geht.). Der Ersatz wiederum ist leer. Zwischen den beiden Hochkommata steht kein Zeichen. Der zu bearbeitende Text steht in $0 – also der ganzen Zeile. Mit dem Parameter –posix versteht awk nicht nur reguläre sondern auch erweiterte Ausdrücke:

awk --posix '$0 ~ /ab{1,3}c/ { print $0 }

Dieser Befehl überprüft ob in $0 – also der gesamten Zeile der Suchstring abc bzw. abbc bzw. abbbc vorkommt. Er enthält also eine Bedingung, die dafür sorgt, dass die geschweifte Klammer nur ausgeführt wird, wenn diese Bedingung zutrifft.

Die Option –posix steht nur im gawk zur Verfügung. Es gibt awk in drei Versionen. Neben der ursprünglichen aus den 70er Jahren(oawk) gibt es noch mawk (welches die großen UNIX-Distributionen einsetzen) und das freie gawk. Sollte in der Man-Page die Rede von mawk sein, kannst Du einfach mit „sudo apt install gawk“ zu der aktuellen gawk Version wechseln. Da gawk deutlich überarbeitet wurde, empfiehlt sich dieser Schritt sowieso.

In diesem Artikel habe ich nur einen kleinen Teil der Möglichkeiten des awk angesprochen. Aber du findest in meinen Youtube Videos sehr viele weitere Möglichkeiten die awk bietet.