[[Vorlage(Getestet, general)]] {{{#!vorlage Wissen [:Pakete_installieren: Installation von Programmen] [:Terminal: Ein Terminal öffnen] }}} [[Inhaltsverzeichnis()]] [[Bild(Wiki/Icons/terminal.png, 48, align=left)]] [wikipedia: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. '''A'''ho, Peter J. '''W'''einberger und Brian W. '''K'''ernighan 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: {{{#!vorlage Paketinstallation mawk }}} Darüber hinaus existieren noch die Pakete [packages:gawk:] ([https://www.gnu.org/savannah-checkouts/gnu/gawk/manual/gawk.txt GNU-Implementation], englische Dokumentation im [:info:]-Format in [packages:gawk-doc:]) und [packages:original-awk:]. [https://linux.die.net/man/1/nawk 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äre_Ausdrücke: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: {{{#!vorlage Befehl 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 [wikipedia: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 [:Shell/Umleitungen#stdin-stdout-stderr-Kanaele-der-Bash: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: {{{#!vorlage Befehl awk '$2 == "localhost" { print $1 }' /etc/hosts }}} Ergebnis: {{{ 127.0.0.1 }}} awk kann die Eingaben auch vom Standardeingabekanal lesen: {{{#!vorlage Befehl 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 {{{#!vorlage Befehl awk -f Datei }}} ausgeführt werden. Wenn also das Beispielskript in der Datei '''localhostip.awk''' gespeichert wäre, würde der Aufruf so erfolgen: {{{#!vorlage Befehl awk -f localhostip.awk /etc/hosts }}} Alternativ kann man den Befehl `awk -f` auch in die Shebangzeile des Skripts packen: {{{#!code awk #!/usr/bin/awk -f $2 == "localhost" { print $1 } }}} und dann dem Skript Ausführungsrechte geben: {{{#!vorlage Befehl chmod a+x localhostip.awk }}} Der Aufruf erfolgt mit: {{{#!vorlage Befehl ./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: {{{#!vorlage 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. {{{#!vorlage Befehl 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). {{{#!code awk 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): {{{#!code awk 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): {{{#!code awk 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): {{{#!code awk 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". {{{#!vorlage Tabelle 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:]. {{{#!code awk { print $1 } }}} Werden die Spalten nicht durch Leerzeichen getrennt, kann man die Variable `FS` ändern, indem man awk mit folgendem Befehl aufruft: {{{#!vorlage Befehl 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: {{{#!code awk { nr += 1 sum += $0 } END { print "Anzahl:", nr, "Summe:", sum, "Durchschnitt:", sum / nr } }}} = Links = == Intern == * [:Skripting:] {Übersicht} weitere Interpreter-Sprachen * [:Programmierung:] {Übersicht} Übersichtsartikel == Extern == * [wikipedia:awk:Wikipedia-Artikel] * [https://www.tldp.org/LDP/abs/html/awk.html Kapitel zu awk] {en} im "Advanced Bash Scripting Guide" * [https://www-user.tu-chemnitz.de/~hot/unix_linux_werkzeugkasten/awk.html Die Programmiersprache AWK] {de} - TU Chemnitz #tag: Shell, Programmierung