awk
Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:
Dieser Artikel ist größtenteils für alle Ubuntu-Versionen gültig.
Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:
awk ist eine Skriptsprache zum Editieren und Analysieren von Texten. Eingabedaten werden dabei immer zeilenweise abgearbeitet. Der Name awk leitet sich von den Anfangsbuchstaben der Entwickler Alfred V. Aho, Peter J. Weinberger und Brian W. Kernighan ab.
Ein Werkzeug mit ähnlichen Aufgabengebieten findet man bei sed, während die Programmiersprache Perl durch ihre unzähligen Zusatzmodule praktisch unbegrenzt erweitert werden kann.
Installation¶
Der Interpreter ist in der Standardinstallation von Ubuntu bereits enthalten, kann ansonsten aber über das folgende Paket installiert [1] werden:
mawk
Befehl zum Installieren der Pakete:
sudo apt-get install mawk
Oder mit apturl installieren, Link: apt://mawk
Darüber hinaus existieren noch die Pakete gawk (GNU-Implementation, englische Dokumentation im info-Format in gawk-doc) und original-awk. Nawk, das „neue awk“, wird standardmäßig von Ubuntu geliefert.
Erste Schritte¶
awk-Programme bestehen immer aus Kombinationen von Anweisungsblöcken und zugehörigen Bedingungen, welche für die aktuelle Eingabezeile erfüllt sein müssen, damit der Anweisungsblock ausgeführt wird. Wird keine Bedingung angegeben, wird die Anweisung für jede Eingabezeile ausgeführt. Wird nur die Bedingung, jedoch keine Anweisung angegeben, so wird die Standardanweisung ausgeführt, welche die Eingabezeile ausgibt. Anweisungsblöcke werden immer in geschweiften Klammern zusammengefasst. Nach Konvention enden Dateinamen für awk-Skripte optional mit .awk.
Allgemeine Syntax mit Bedingung und Anweisung:
Bedingung { Anweisungen }
Bedingungen¶
Eine übliche Bedingung ist der Vergleich mit einem regulären Ausdruck:. awk
verwendet "Extended Regular Expressions", so wie auch von grep mit der Option -E
verwendet werden.
Dies wird wie folgt geschrieben:
/regulärer Ausdruck/ # Kurzform für Vergleich mit der aktuellen Zeile $0 ~ /regulärer Ausdruck/ # Langform /^Hallo / # Beispiel: alle Zeilen, die mit "Hallo " beginnen /[0-9]+\.[0-9]+/ # komplexeres Beispiel: alle Zeilen, die eine Dezimalzahl mit "." als Trennzeichen enthalten (z.B. 3.45)
Man kann auch andere Vergleiche durchführen. Statt ~
für "entspricht" lässt sich dabei auch immer !~
für "entspricht nicht" nutzen.
Ausdruck ~ /regulärer Ausdruck/ # allgemein $1 ~ /^Hallo$/ # Beispiel: vergleicht das erste Feld der Zeile (siehe "Variablen"); (fast) identisch zum 1. Beispiel oben
Schließlich kann man auch Vergleiche ohne reguläre Ausdrücke durchführen:
Ausdruck Operator Ausdruck # allgemein $1 == "Hallo" # Beispiel: wie "$1 ~ /^Hallo$/" $1 > 20 # Beispiel: prüft, ob der Wert im ersten Feld größer als 20 ist
Mehrere Bedingungen lassen sich auch mit &&
(und) bzw. ||
(oder) verknüpfen.
Spezielle Bedingungen¶
Es gibt die besonderen Bedingungen BEGIN
und END
. Anweisungen im BEGIN
Block werden ausgeführt bevor die erste Zeile der Eingabedaten eingelesen wird. Hier können z.B. eigene Variablen initialisiert werden. Anweisungen im END
Block werden nach der letzten Zeile ausgeführt.
Das Beispiel wertet die Datei /etc/hosts aus:
awk ' BEGIN { print "Die IP Adresse von localhost ist: " } $2 == "localhost" { print $1 } END { print "Das war die IP Adresse von localhost."} ' /etc/hosts
Ergebnis:
Die IP Adresse von localhost ist: 127.0.0.1 Das war die IP Adresse von localhost.
Variablen¶
awk setzt beim Einlesen jeder Zeile bestimmte Variablen. $0
ist immer die komplette Zeile. Mit $1
, $2
usw. kann man auf die einzelnen Felder der Zeile zugreifen. Felder werden in der Voreinstellung durch Leerraum (whitespace) voneinander getrennt. In der Variable NF
steht die Anzahl der Felder in der aktuellen Zeile. Somit kann man auf die Felder $1
- $NF
zugreifen. Die Variable NR
enthält die aktuelle Zeilennummer. Mit jeder neu eingelesenen Zeile wird NR
um eins erhöht.
Ausführung auf der Kommandozeile¶
awk-Skripte können auf der Kommandozeile ausgeführt werden. Eingabedaten werden entweder über den Standardeingabekanal gelesen oder aus einer oder mehreren angegebenen Dateien. Das Skript kann dabei in einfachen Hochkomma direkt hinter dem awk-Aufruf stehen. Wenn man zum Beispiel die IP-Adresse des localhost
-Eintrags aus der Datei /etc/hosts auslesen möchte, könnte man das über folgendes Skript tun:
awk '$2 == "localhost" { print $1 }' /etc/hosts
Ergebnis:
127.0.0.1
awk kann die Eingaben auch vom Standardeingabekanal lesen:
awk '$2 == "localhost" { print $1 }' < /etc/hosts
Ausführung als Skriptdatei¶
Längere awk-Skripte werden auf der Kommandozeile schnell unübersichtlich. Daher können Skripte auch als Datei gespeichert werden. Auf der Kommandozeile können diese dann mit
awk -f Datei
ausgeführt werden. Wenn also das Beispielskript in der Datei localhostip.awk gespeichert wäre, würde der Aufruf so erfolgen:
awk -f localhostip.awk /etc/hosts
Alternativ kann man den Befehl awk -f
auch in die Shebangzeile des Skripts packen:
1 2 | #!/usr/bin/awk -f $2 == "localhost" { print $1 } |
und dann dem Skript Ausführungsrechte geben:
chmod a+x localhostip.awk
Der Aufruf erfolgt mit:
./localhostip.awk /etc/hosts
Variablen¶
Variablen müssen in awk-Skripten nicht speziell initialisiert werden. Es kann jederzeit einer neuen Variable ein Wert zugewiesen werden.
Builtin-Variablen¶
awk belegt einige Variablen automatisch mit Werten. Diese können im laufenden Skript abgefragt und verändert werden, wobei Änderungen nicht bei allen Variablen sinnvoll sind. Einige davon zeigt die folgende Tabelle:
Name | Bedeutung |
ARGC | Anzahl der Argumente |
ARGV | Array mit den übergebenen Argumenten |
ENVIRON | Array der Umgebungsvariablen |
FILENAME | Name der Eingabedatei |
NF | Anzahl der Felder in derzeitigen Zeile |
NR | Anzahl der Zeilen seit Skriptstart |
FNR | Anzahl der Zeilen aus der aktuellen Eingabedatei |
FS | Der aktuelle Feldtrenner. Anhand dieses Trenners werden die Variablen $1 - $NF belegt. |
FIELDWIDTHS | Eine mit Leerzeichen getrennte Liste von Feldbreiten. Wenn diese Variable gesetzt wird, werden Felder nicht mehr anhand des Feldtrenners FS getrennt, sondern nach festen Breiten. Dies ist vor allem dann sinnvoll, wenn der auszuwertende Text mit festen Breiten formatiert ist. |
Variablen übergeben¶
Oft ist es sinnvoll, dem awk-Skript eigene Variablen zu übergeben, welche dann im Skript verwendet werden können. Mit dem Aufrufparameter -v
können solche Variablen gesetzt und im Skript genutzt werden.
awk -v pre="IP von localhost ist:" -v post="Das war die IP von localhost." ' BEGIN { print pre } $2 == "localhost" { print $1 } END { print post} ' /etc/hosts
Ergebnis:
IP Adresse von localhost ist: 127.0.0.1 Das war die IP von localhost
Programmsteuerung¶
Auch awk hat einige Funktionen zur Programmsteuerung, die wahrscheinlich schon von anderen Programmiersprachen bekannt sind.
Bedingungen¶
Mit if-else
können Bedingungen überprüft werden und der Programmablauf entsprechend gesteuert werden.
if (bedingung) { # Anweisungen wenn Bedingung erfüllt } else if (bedingung2) { # Anweisungen wenn Bedingung 2 erfüllt } else { # Anweisungen wenn Bedingung nicht erfüllt }
Das Beispiel überprüft, ob eine Zahl x
gerade oder ungerade ist. %
steht für "Modulo" (der Rest bei einer Division mit Rest).
1 2 3 4 5 | if (x % 2 == 0) { print x " ist eine gerade Zahl." } else { print x " ist eine ungerade Zahl." } |
Schleifen¶
Schleifen dienen der Wiederholung von Anweisungen.
while-Schleife¶
In der while
-Schleife wird eine Bedingung überprüft. Wenn die Bedingung wahr ist, wird der Schleifenkörper ausgeführt. Nach dem Schleifenkörper wird die Bedingung erneut überprüft usw. Es muss also sichergestellt werden, dass sich die Bedingung innerhalb der Schleife ändert, damit keine Endlosschleife entsteht.
while (Bedingung) { Anweisung(en) }
Beispiel (gibt die Zahlen 1-9 aus):
1 2 3 4 5 | i=1 while (i<10) { print i i = i + 1; } |
do-while-Schleife¶
Die do-while
-Schleife ist prinzipiell ähnlich zur while
-Schleife, mit dem Unterschied, dass die Bedingung erst nach dem Durchlauf des Schleifenkörpers überprüft wird. Sie wird also immer mindestens einmal durchlaufen.
do { Anweisung(en) } while (Bedingung)
Beispiel (gibt die Zahl 10 einmal aus, obwohl die Bedingung nicht erfüllt ist):
1 2 3 4 | i = 10 do { print i } while (i < 10) |
for-Schleife¶
Die for-Schleife ist immer dann hilfreich, wenn eine genau festgelegte Anzahl an Ausführungen des Schleifenkörpers gewünscht wird. Die Schleife besteht aus drei Teilen: Einer Initialisierung, einer Bedingung und einer Inkrement-Anweisung. Der Schleifenkörper wird ausgeführt, wenn die Bedingung erfüllt ist. Die Überprüfung der Bedingung findet bei der for
-Schleife wie bei der while
-Schleife vor dem Durchlauf des Schleifenkörpers statt.
for(Initialisierung; Bedingung; Inkrement) { Anweisung(en) }
Beispiel (gibt die Zahlen 1-9 aus):
1 2 3 | for (i=1; i<10; i++) { print i } |
break und continue¶
Mit den Befehlen break
und continue
kann der Ablauf innerhalb der Schleife kontrolliert werden. Mit break
wird die Schleife sofort verlassen. Mit continue
wird die der Schleifenkörper erneut von vorne durchlaufen, wodurch auf das continue
folgende Anweisungen übersprungen werden. Bei einer for
-Schleife wird dabei auch noch die Inkrement-Anweisung ausgeführt.
next, nextfile und exit¶
Mit next
kann awk angewiesen werden, sofort die nächste Zeile einzulesen und damit weiterzuarbeiten. Mit nextfile
wird die Abarbeitung der derzeitigen Datei beendet und mit der nächsten Datei weitergemacht oder das Skript beendet, falls es die letzte Eingabedatei war. Mit exit
wird das Skript sofort beendet.
Anweisungen¶
Hinter einer Anweisung kann ein Semikolon eingefügt werden. Dies ist immer notwendig, wenn mehrere Anweisungen auf einer Zeile stehen.
Hier sollen noch ein paar weitere Anweisungen vorgestellt werden. Mehr findet man in der Manpage von awk unter "8. Built-in functions" und "9. Input and output".
Anweisung | Nutzung |
gsub(regulärer Ausdruck, Ersetzung, Variable) | Ersetzt alle Vorkommen des regulären Ausdrucks in der Variablen. Wird keine Variable angegeben, wird $0 verwendet. |
sub(regulärer Ausdruck, Ersetzung, Variable) | Wie gsub , aber es wird höchstens eine Ersetzung vorgenommen. |
index(Zeichenkette 1, Zeichenkette 2) | Sucht Zeichenkette 2 in Zeichenkette 1 und gibt die Position in Zeichenkette 1 oder 0 für "nicht gefunden" zurück. |
match(Zeichenkette, regulärer Ausdruck) | Wie index , aber es wird nach einem regulären Ausdruck gesucht. |
length(Zeichenkette) | Gibt die Länge der Zeichenkette zurück. |
substr(Zeichenkette, Start, Länge) | Gibt den Ausschnitt aus der Zeichenkette zurück, der bei Index "Start" beginnt. Wird eine Länge angegeben, werden nur so viele Zeichen zurückgegeben. |
Beispiele¶
Beispiele für awk-Skripte findet man u.a. in der Manpage von awk unter "EXAMPLES". Ein paar sollen hier noch aufgeführt werden.
Spalten ausgeben¶
awk wird oft verwendet, um eine bestimmte Spalte (hier: die erste) auszugeben. Eine ähnliche Funktion hat cut.
1 | { print $1 } |
Werden die Spalten nicht durch Leerzeichen getrennt, kann man die Variable FS
ändern, indem man awk mit folgendem Befehl aufruft:
awk -F Neuer-FS-Wert '{ print $1 }'
Summe bilden¶
Dieses Skript erwartet Zeilen, die jeweils genau eine Zahl enthalten, und gibt die Anzahl der Zeilen, die Summe aller Zahlen sowie den Durchschnitt aus:
1 2 3 4 5 6 7 | { nr += 1 sum += $0 } END { print "Anzahl:", nr, "Summe:", sum, "Durchschnitt:", sum / nr } |
Links¶
Intern¶
Skripting weitere Interpreter-Sprachen
Programmierung Übersichtsartikel
Extern¶
Kapitel zu awk 🇬🇧 im "Advanced Bash Scripting Guide"
Die Programmiersprache AWK 🇩🇪 - TU Chemnitz