|
|
(32 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) |
Zeile 1: |
Zeile 1: |
| {{DISPLAYTITLE:gawk}} | | {{DISPLAYTITLE:Kategorie:gawk}} |
| | [[Kategorie:Linux/Befehl]] |
| | [[Kategorie:Bash/Scripting]] |
| | [[Kategorie:Regular Expression]] |
|
| |
|
| '''awk''' ist eine Programmiersprache zur Bearbeitung und Auswertung beliebiger Textdaten
| | {{DEFAULTSORT:Kategorie:gawk}} |
| | |
| = Einführung =
| |
| * Man kann awk als Weiterentwicklung oder Ergänzung des Streameditors sed betrachten, sie teilen gewisse syntaktische Elemente wie etwa reguläre Ausdrücke.
| |
| * Im Unterschied zu sed stehen in awk aber C-ähnliche Strukturen (if .. then .. else, verschiedene Schleifen, C-Formate …) zur Verfügung, die einen wesentlich leichteren Programmaufbau erlauben.
| |
| * In der Minimalanwendung wird awk in Shell-Skripten eingesetzt, um als Filter zum Beispiel Dateinamen zusammenzusetzen.
| |
| * Mit ausführlicheren Programmen gelingt es, Textdateien zu bearbeiten, umzuformen oder auszuwerten.
| |
| * Dazu stehen neben den üblichen Stringfunktionen aber auch mathematische Grund-Funktionen zur Verfügung.
| |
| * Der Name "awk" ist aus den Anfangsbuchstaben der Nachnamen ihrer drei Autoren Alfred V. Aho, Peter J. Weinberger und Brian W. Kernighan zusammengesetzt.
| |
| * Der freie Awk-Abkömmling der GNU-Gemeinde gawk zeigt sich zu POSIX konform und wird in den folgenden Abschnitten behandelt.
| |
| | |
| | |
| * '''AWK''' ergibt sich auf den Initialen seiner Erfinder * Alfred V. '''A'''ho, Peter J. '''W'''einberger, Brian '''K'''ernighan
| |
| * und erweiterte erstmals 1978 den Werkzeugfundus von Unix Version 7. Jener Kernighan prägte übrigens gemeinsam mit Dennies Ritchie maßgeblich die Entstehung der Programmiersprache C
| |
| * wen wundern da noch die Analogien beider Sprachen?
| |
| * 1987 wartete Awk mit einer komplett überarbeiteten Fassung auf.
| |
| * Wesentliche Bestandteile fanden im später definierten POSIX-Standard Einzug.
| |
| | |
| * Der wesentliche Unterschied von Awk zu anderen Programmiersprachen wie den Skriptsprachen der UNIX Shells, C oder Tcl/Tk besteht in der ''datenorientierten Arbeitsweise'', während die typischen Vertreter prozeduraler Programmiersprachen ''funktionsorientiert'' wirken.
| |
| | |
| * Ein awk-Programm wirkt implizit wie eine endlose Schleife, die fortwährend durchlaufen wird, bis keine Daten mehr in der Eingabe stehen oder das Programm »bewusst« verlassen wird, d. h. der Steuerfluss ist maßgeblich durch die Daten gegeben.
| |
| | |
| * In den meisten anderen Programmiersprachen wird das Hauptprogramm aber einmalig initiiert und Funktionen beeinflussen den Fortschritt der Berechnung.
| |
| | |
| * Awk ähnelt somit eher dem Streameditor (sed); er vermag allerdings bedeutend mehr als die »bloße« Modifikation von Textdateien, denn Awk kennt Variablen, Vergleiche, Funktionen, Schleifen u.a.m. und ermöglicht eine Interaktion mit dem System.
| |
| | |
| * Da in Linux-Installationen nahezu ausschließlich die GNU-Implementierung des Werkzeugs vorzufinden ist, werden wir nachfolgend immer die Bezeichnung »awk« verwenden und meinen damit eigentlich »gawk«.
| |
| | |
| * Zeigen die Beispiele auf Ihrem System abweichende oder fehlerhafte Reaktionen, überprüfen Sie, ob »awk« in ihrem System ein Link auf »gawk« ist.
| |
| | |
| = Reguläre Ausdrücke in Mustern =
| |
| siehe [[Gawk:Reguläre Ausdrücke in Mustern]]
| |
| | |
| = Datenfelder und Variablen =
| |
| siehe [[gawk:Datenfelder und Variablen]]
| |
| | |
| = Operatoren =
| |
| siehe [[Gawk:Operatoren]]
| |
| | |
| = Kontrollstrukturen =
| |
| * Der Mustervergleich arbeitet über die Eingabedaten und regelte, ob für den aktuellen Datensatz Aktionen durchzuführen ist.
| |
| * Die im weiteren vorgestellten Konstrukte gestatten den Kontrollfluss innerhalb einer solchen Aktionsfolge.
| |
| == Bedingte Ausführung ==
| |
| === if-else ===
| |
| Wohl jede Programmiersprache verfügt über ein if-else-Konstrukt, die Realisierung von awk orientiert sich an der C-Syntax:
| |
| if (Bedingung) {
| |
| # diese Aktionen
| |
| }
| |
| [ else {
| |
| # jene Aktionen
| |
| } ]
| |
| | |
| * Evaluiert die Bedingung zu wahr (!=0), so werden die Aktionen des '''if '''-Zweigs ausgeführt, anderenfalls wird der optionale '''else '''-Zweig betreten.
| |
| * Folgt einem Zweig nur eine einzelne Aktion, können die geschweiften Klammern entfallen.
| |
| * Das nachfolgende Beispiel widerspricht eigentlich dem Einsatzzweck von Awk, bedarf es doch keinerlei Eingabedaten.
| |
| * Es testet, ob das Skript mit (mind.) einem Argument aufgerufen wurde.
| |
| * Fehlt dieses, endet das Skript mit einer Fehlerausgabe, anderenfalls wird der Wert des ersten Arguments potenziert:
| |
| $ '''cat potenz.awk'''
| |
| #!/usr/bin/awk -f
| |
|
| |
| BEGIN {
| |
| if (ARGV[1] = "") {
| |
| print "Benutze: ./potenz.awk 'beliebiger_Wert'";
| |
| exit 1;
| |
| }
| |
| else {
| |
| print "x^x =", ARGV[1]**ARGV[1];
| |
| }
| |
| }
| |
| | |
| '''Anmerkung'''
| |
| Während der Abhandlung zu Variablen wurde erwähnt, dass nicht initialisierte Variablen intern mit »0« (numerischer Kontext) bzw.
| |
| | |
| * mit »""« (Zeichenkettenkontext) belegt werden.
| |
| * Da »0« eine erlaubte Eingabe des Skripts darstellt, erzwingen wir mit dem Vergleich mit der leeren Zeichenkette letzteren Kontext.
| |
| * Awk kennt kein zu case äquivalentes Konstrukt, sodass das zur Bewertung mehrerer Bedingungen verschachtelte if-else-Anweisungen verwendet werden:
| |
| if (Bedingung 1) {
| |
| # Aktionen
| |
| }
| |
| else if (Bedingung 2) {
| |
| # Aktionen
| |
| }
| |
| [...]
| |
| else {
| |
| # Aktionen
| |
| }
| |
| | |
| * Bei mehreren erfüllten Bedingungen werden einzig die Aktionen der ersten zutreffenden ausgeführt.
| |
| * Wird keine Bedingung wahr, werden alle Aktionen des optionalen else-Zweigs abgearbeitet.
| |
| | |
| === Conditional-if ===
| |
| Manchmal soll einer Variable in Abhängigkeit vom Ergebnis einer Bedingung ein Wert zugewiesen werden.
| |
| | |
| Mit »if-else« wäre das folgende Fragment denkbar:
| |
| if (Bedingung)
| |
| variable = Wert_1
| |
| else
| |
| variable = Wert_2
| |
| | |
| * Prägnanter ist die Schreibweise des Conditional-if:
| |
| * variable = Bedingung ? Wert_1 :Wert_2
| |
| * Es bleibt Ihnen überlassen, welche Varianten Sie wählen.
| |
| * Dir letztere Schreibweise unterstreicht deutlicher das Ansinnen, einer Variable einen Wert zuzuweisen.
| |
| | |
| Da conditional-if im Gegensatz zu if-else einen Wert liefert, finden sich auch hin und wieder Situationen, wo eine Umschreibung per if-else umständlich, wenn nicht gar unmöglich ist:
| |
| $ '''awk 'BEGIN { x=1; while (x <= 3) printf("%d Osterei%s\n", x, x++>1 ?"er":"") }''''
| |
| 1 Osterei
| |
| 2 Ostereier
| |
| 3 Ostereier
| |
| | |
| '''printf '''wird im Abschnitt Ein/Ausgabe besprochen.
| |
| | |
| == Schleifen ==
| |
| === for ===
| |
| Die for-Schleife wird bevorzugt, wenn eine bekannte Anzahl Durchläufe der Anweisungen im Schleifenrumpf erfolgen soll:
| |
| | |
| for (Zähler initialisieren; Zähler testen; Zähler verändern) {
| |
| # Aktionen
| |
| }
| |
| | |
| * Die geschweiften Klammern dürfen entfallen, wenn nur eine einzelne Aktion zum Schleifenrumpf gehört.
| |
| * Die mit Zähler initialisieren, Zähler testen und Zähler verändern bezeichneten Ausdrücke beschreiben den »gebräuchlichen« Verwendungszweck.
| |
| * Wie auch in C wird der erste Ausdruck (Zähler initialisieren) einmalig vor Eintritt in die Schleife ausgeführt.
| |
| | |
| Der zweite Ausdruck (Zähler testen) wird jeweils ''vor dem nächsten'' Durchlauf der Schleife betrachtet und der dritte Ausdruck (Zähler verändern) wird nach Bearbeitung der Anweisung des Schleifenrumpfes berechnet:
| |
| $ '''awk 'BEGIN {for ( i=0; i<3; i++) print i;}''''
| |
| 0
| |
| 1
| |
| 2
| |
| $ '''awk 'BEGIN {for ( i=0; i++<3; ) print i;}''''
| |
| 1
| |
| 2
| |
| 3
| |
| | |
| * Die Beispiele zeigen auch, dass die Angabe der Ausdrücke optional ist.
| |
| | |
| Analog zu C kann eine Endlosschleife wie folgt angegeben werden:
| |
| $ '''awk 'BEGIN {for (;;) printf(".") }''''
| |
| * Was Awk im Gegensatz zu C allerdings nicht gestattet, ist die Angabe einer kommaseparierten Liste von Ausdrücken (»for (i=0,j=0;...;...)«).
| |
| | |
| === while ===
| |
| '''while '''-Schleifen werden bevorzugt, wenn die Anzahl der Schleifendurchläufe vom Ergebnis eines Ausdrucks abhängt.
| |
| while (Bedingung) {
| |
| # Aktionen
| |
| }
| |
| * Die geschweiften Klammern dürfen entfallen, wenn nur eine einzelne Aktion zum Schleifenrumpf gehört.
| |
| | |
| Das folgende Beispiel berechnet die Fakultät zu einer per Argument übergebenen Zahl:
| |
| $ '''cat fakultaet.awk'''
| |
| #!/usr/bin/awk -f
| |
|
| |
| BEGIN {
| |
| if (ARGC < 2 || (ARGV[1] !~ /^[0-9]+$/)){
| |
| print "Aufruf: ./fakultaet.awk <Zahl>";
| |
| exit 1;
| |
| }
| |
|
| |
| fakultaet=1;
| |
| zahl=ARGV[1];
| |
|
| |
| while (zahl > 1)
| |
| fakultaet*=zahl--;
| |
|
| |
| print "Fakultaet von", ARGV[1], "ist", fakultaet;
| |
| }
| |
| | |
| $ '''./fakultaet.awk 5'''
| |
| Fakultaet von 5 ist 120
| |
| | |
| === do-while ===
| |
| In Situationen, in denen ein Programmabschnitt ''mindestens einmal'' - und in Abhängigkeit von einer Bedingung weitere Male - zu durchlaufen ist, bietet sich die '''do-while '''-Schleife an:
| |
| do {
| |
| # Aktionen
| |
| } while (Bedingung)
| |
| | |
| Eine Anwendung könnte der Test einer Nutzereingabe sein, die solange zu wiederholen ist, bis sie dem geforderten Format entspricht:
| |
| do {
| |
| printf("Geben Sie eine Zahl ein: ");
| |
| getline zahl < "-";
| |
| } while ( zahl !~ /^digit:+$/ )
| |
| | |
| '''getline '''wird im Abschnitt Ein/Ausgabe behandelt.
| |
| | |
| == Weitere Kontrollanweisungen ==
| |
| * Betrachtet man das Hauptprogramm von Awk als eine Schleife (die über alle Zeilen der Eingabe läuft), so besitzen die nachfolgend vorgestellten Anweisungen eine Gemeinsamkeit: Sie verändern den Kontrollfluss von Schleifen.* '''break '''und '''continue '''wirken mit '''for '''und '''[do-]while '''zusammen
| |
| * '''exit, next '''und '''nextfile '''betreffen die Hauptschleife.
| |
| | |
| === break ===
| |
| * Mit '''break '''kann eine for-, while- oder do-while-Schleife vorzeitig verlassen werden.
| |
| Die Programmausführung fährt mit der der Schleife unmittelbar folgenden Anweisung fort.
| |
| $ '''awk 'BEGIN { for (;;) if (x++ = 5) break; print x}''''
| |
| 6
| |
| | |
| === continue ===
| |
| * Wird '''continue '''innerhalb einer for-, while- oder do-while-Schleife ausgeführt, wird die Bearbeitung des aktuellen Schleifendurchlaufs abgebrochen und der nächste Durchlauf begonnen.
| |
| $ '''awk 'BEGIN { for (i=1;i<10;i++) {if (i%2) continue; print i } }''''
| |
| 2
| |
| 4
| |
| 6
| |
| 8
| |
| | |
| === exit ===
| |
| * Der '''exit '''-Befehl dient zum (vorzeitigen) Beenden der Hauptschleife von Awk.
| |
| * Das Programm wird unverzüglich mit Ausführung des optionalen END-Statements fortgeführt.
| |
| * Als Argument kann '''exit '''ein Ausdruck mitgegeben werden.
| |
| * Der Wert des Ausdrucks ist der Rückgabewert von Awk.
| |
| * Ohne Angabe des Arguments wird implizit 0 angenommen.
| |
| * Eine diesbezügliche Ausnahme bildet die Verwendung von '''exit '''innerhalb der END-Anweisungen.
| |
| | |
| Fehlt hier das Argument, gilt ein in der Hauptschleife (bzw. innerhalb von BEGIN) gesetzter Wert:
| |
| $ '''awk 'BEGIN { exit 3 }; END { exit }'; echo $?'''
| |
| 3
| |
| | |
| $ '''awk 'BEGIN { exit 3 }; END { exit 0 }'; echo $?'''
| |
| 0
| |
| | |
| === next ===
| |
| '''next '''wirkt wie '''continue, '''nur dass jetzt der aktuelle Durchlauf des Hauptprogramms unterbrochen und mit dem nächsten Durchlauf - sprich: mit der nächsten Zeile der Eingabe - fortgefahren wird.
| |
| | |
| === nextfile ===
| |
| * Diese Erweiterung von GNU-Awk (nicht POSIX) wirkt wie next, d.h.
| |
| * es wird zum Beginn des Hauptprogramms gesprungen.
| |
| * Die aktuelle Datei wird geschlossen und als Datensatz die erste Zeile der nächsten Eingabedatei geladen.
| |
| | |
| = Ein- und Ausgabe =
| |
| == Eingabe ==
| |
| Der Funktion '''getline''' kann eine Quelldatei über die Standardeingabe übergeben werden
| |
| '''getline''' [variable] [< "<Datei>"]
| |
| | |
| <Kommando> | '''getline''' [variable]
| |
| | |
| * Im einfachsten Fall des Funktionsaufrufs ohne Argumente lädt '''getline '''einfach die nächste Eingabezeile.
| |
| * Alle im Skript folgenden Anweisungen arbeiten folglich mit diesem neuen Datensatz.
| |
| * In dieser Form manipuliert '''getline '''die Awk-Variablen $0, NR, NF und FNR.
| |
| * Folgt dem Funktionsaufruf eine Variable, so landet der Inhalt der Eingabe in dieser.
| |
| * Der Arbeitspuffer mit dem aktuellen Datensatz ($0) wird hierbei nicht verändert, jedoch werden die gelesenen Daten aus der Eingabe entfernt.
| |
| | |
| Das folgende Beispiel nutzt das Verhalten, um jeweils zwei Zeilen der Eingabe zu einer Zeile in der Ausgabe zusammenzufassen:
| |
| $ '''cat 2to1.awk'''
| |
| #!/usr/bin/awk -f
| |
| {
| |
| if ( (getline nextLine) < 1)
| |
| nextLine="";
| |
| print $0, nextLine;
| |
| }
| |
| | |
| * '''getline '''liefert bei erfolgreich gelesener Eingabe eine »1« zurück.
| |
| * Das Beispiel nutzt den Rückgabewert, um die Variable zurückzusetzen, falls in der Eingabe eine ungerade Anzahl Datensätze steht.
| |
| * Eine »-1« liefert '''getline '''im Fehlerfall; eine »0« bei Erreichen des Dateiendes.
| |
| | |
| '''getline '''in Verbindung mit < "<Datei>" versucht den nächsten Datensatz aus der Datei zu lesen.
| |
| * Dieser steht in $0 zur Verfügung, FN enthält die Anzahl der Felder dieses Dateisatzes.
| |
| * Mit jedem Aufruf wird ein weiterer Datensatz aus der Datei geliefert.
| |
| | |
| Um von der Standardeingabe zu lesen, ist als Dateiname »-« anzugeben:
| |
| $ '''awk 'BEGIN {getline < "-"; print "Die Eingabe war ", $0; }''''
| |
| | |
| '''Anmerkung'''
| |
| Speziell in gawk steht der Dateiname »/dev/stdin« zur Verfügung, der anstatt dem Minus verwendet werden kann.
| |
| | |
| Als weitere Quelle kann '''getline '''seine Eingaben auch aus einer Pipe beziehen.
| |
| | |
| Auf der linken Seite der Pipe muss ein Kommando stehen, das in doppelte Anführungszeichen einzuschließen ist:
| |
| $ '''awk 'BEGIN {"date" | getline datum; close("date"); print "Aktuelles Datum: ", datum;}''''
| |
| Aktuelles Datum: Mit Dez 12 21:42:35 CET 2001
| |
| | |
| '''close '''wird im Anschluss behandelt.
| |
| | |
| == Escape-Sequenzen ==
| |
| Escape-Sequenzen sind nicht auf die Verwendung in Ausgaben beschränkt, werden aber in diesem Zusammenhang häufig genutzt.
| |
| {|class="wikitable"
| |
| |-
| |
| | | '''\a'''
| |
| | | Alert (Piepton)
| |
| |-
| |
| | | '''\b'''
| |
| | | Backspace (Cursor ein Zeichen nach links)
| |
| |-
| |
| | | '''\f'''
| |
| | | Formfeed (Cursor auf nächste Zeile, eine Position weiter)
| |
| |-
| |
| | | '''\n'''
| |
| | | Zeilenumbruch
| |
| |-
| |
| | | '''\r'''
| |
| | | Carriage return (Cursor an Anfang der aktuellen Zeile)
| |
| |-
| |
| | | '''\t'''
| |
| | | Horizontaler Tabulator
| |
| |-
| |
| | | '''\v'''
| |
| | | Vertikaler Tabulator
| |
| |-
| |
| | | '''\ddd'''
| |
| | | 1-3 oktale Ziffern
| |
| |-
| |
| | | '''\xHEX'''
| |
| | | Hexadezimale Zahl
| |
| |-
| |
| | | '''\c'''
| |
| | | Das Zeichen c selbst (i.A.
| |
| * zur Darstellung von Sonderzeichen verwendet)
| |
| |-
| |
| |}
| |
| | |
| $ '''awk 'BEGIN {print "\\f\f führt\f zu\f einem\f Treppeneffekt:)";}''''
| |
| \f
| |
| führt
| |
| zu
| |
| einem
| |
| Treppeneffekt:)
| |
| | |
| == Ausgabe mit print ==
| |
| '''print '''([Parameter [, Parameter] ])
| |
| | |
| '''print '''ist für die Ausgabe zu bevorzugen, wenn Sie keine Anforderungen an die Formatierung stellen.
| |
| * Geben Sie hierzu einfach die Liste der auszugebenden Ausdrücke, jeweils getrennt durch ein Komma, an.
| |
| * Jedes Element wird in der Ausgabe vom Folgenden durch ein Leerzeichen getrennt werden.
| |
| * Dem letzten Element lässt '''print '''einen Zeilenumbruch folgen (das voreingestellte Verhalten kann mittels der Variablen OFS und ORS geändert werden)
| |
| * Sie können gar auf jegliche Elemente verzichten.
| |
| * In dem Fall gibt '''print '''die aktuell bearbeitet Zeile aus, d.h.
| |
| * zwischen »print« und »print $0« besteht bez.
| |
| * des Ergebnisses kein Unterschied.
| |
| * Die an '''print '''zu übergebenden Argumente können wahlweise in runde Klammern eingeschlossen werden.
| |
| * Notwendig ist dies, wenn die Ausdrücke den Vergleichsoperator »>« enthalten, da dieser sonst als Ausgabeumleitung verstanden wird.
| |
| * Es ist kein Syntaxfehler, wenn Sie auf die Kommata zwischen den auszugebenden Ausdrücken verzichten.
| |
| | |
| Awk interpretiert dies als das Zusammenhängen von Zeichenketten, sodass als ersichtlicher Unterschied zumeist das trennende Leerzeichen fehlt:
| |
| $ '''awk 'BEGIN { print "foo", "bar";}''''
| |
| foo bar
| |
| | |
| $ '''awk 'BEGIN { print "foo" "bar";}''''
| |
| foobar
| |
| | |
| Eine begrenzte Formatierung lässt sich für numerische Werte erzwingen, indem das in OFMT gespeicherte Ausgabeformat (Voreinstellung "%.6g") geändert wird:
| |
| $ '''awk 'BEGIN { print rand();}''''
| |
| 0.487477
| |
| | |
| $ '''awk 'BEGIN { OFMT="%.8f"; print rand();}''''
| |
| 0.48747681
| |
| | |
| $ '''awk 'BEGIN { OFMT="%.1f"; print rand();}''''
| |
| 0.5
| |
| * Beachten Sie die enthaltene mathematisch korrekte Rundung der Werte!
| |
| | |
| == Formatierte Ausgabe mit printf ==
| |
| Die Syntax von '''printf '''kann ihre Anlehnung an die verbreitete Sprache nicht verbergen.
| |
| '''printf''' (Formatzeichenkette[, Parameter(liste)])
| |
| | |
| * Zwar dürfen in Awk die runden Klammern auch entfallen, aber das ist fast schon der einzige Unterschied zu C.
| |
| | |
| Die Formatzeichenkette kann jedes ASCII-Zeichen, Escape-Sequenzen oder einen der folgenden Platzhalter umfassen:
| |
| {|class="wikitable"
| |
| |-
| |
| | | '''%c'''
| |
| | | Ein einzelnes ASCII-Zeichen
| |
| |-
| |
| | | '''%d'''
| |
| | | Eine ganze Zahl
| |
| |-
| |
| | | '''%i'''
| |
| | | Eine ganze Zahl (wie d, aber konform zu POSIX)
| |
| |-
| |
| | | '''%e'''
| |
| | | Gleitkommazahl in Exponentendarstellung (2.e7)
| |
| |-
| |
| | | '''%E'''
| |
| | | Gleitkommazahl in Exponentendarstellung (2.E7)
| |
| |-
| |
| | | '''%f'''
| |
| | | Gleitkommazahl in Fließdarstellung
| |
| |-
| |
| | | '''%g'''
| |
| | | e oder f, je nachdem, was kürzer ist
| |
| |-
| |
| | | '''%G'''
| |
| | | E oder f, je nachdem, was kürzer ist
| |
| |-
| |
| | | '''%o'''
| |
| | | Oktale Zahl
| |
| |-
| |
| | | '''%s'''
| |
| | | Zeichenkette
| |
| |-
| |
| | | '''%x'''
| |
| | | Hexadezimale Zahl mit a-e
| |
| |-
| |
| | | '''%X'''
| |
| | | Hexadezimale Zahl mit A-E
| |
| |-
| |
| | | '''%%'''
| |
| | | % selbst
| |
| |-
| |
| |}
| |
| | |
| * Die Anzahl der Elemente der Parameterliste muss mindestens die Anzahl Platzhalter in der Formatzeichenkette umfassen. Überschüssige Angaben werden schlicht ignoriert.
| |
| * Der Typ des i-ten Parameters sollte zum i-ten Platzhalter »passen«, allerdings reagiert Awk recht großzügig und wandelt die Typen ggf. ineinander um.
| |
| | |
| Meist weicht das Resultat aber vom Gewünschten ab.
| |
| $ '''awk 'BEGIN { printf("%i\n", 11.100); }''''
| |
| 11
| |
| | |
| $ '''awk 'BEGIN { printf("%e\v%E\n", 11.100, 11.100); }''''
| |
| 1.110000e+01
| |
| 1.110000E+01
| |
| | |
| $ '''awk 'BEGIN { printf("%f\n", 11.100); }''''
| |
| 11.100000
| |
| | |
| $ '''awk 'BEGIN { printf("%x\n", 11.100); }''''
| |
| b
| |
| | |
| $ '''awk 'BEGIN { printf("%s\t%s\n", 11.100, "11.100"); }''''
| |
| 11.1 11.100
| |
| | |
| * Eine exakte Positionierung und angepasstes Format der auszugebenden Parameter wäre mit den Platzhaltern allein nicht immer möglich, daher gestattet '''printf '''- C lässt grüßen - die Angabe von Breite und Ausrichtung eines Parameters sowie eines Modifizierers.
| |
| * Für Gleitkommaangaben kommt noch die Genauigkeit der Nachkommastellen hinzu.
| |
| | |
| Die optionalen Angaben stehen zwischen Prozentzeichen und Formatidentifikator:
| |
| %ModifiziererBreite.GenauigkeitFormatidentifikator
| |
| * Der wohl gebräuchlichste Modifizierer beeinflusst die horizontale Anordnung eines Parameters.
| |
| | |
| In der Voreinstellung rechtsbündig, erzwingt ein dem Prozentzeichen folgendes Minus die linksbündige Ausrichtung:
| |
| $ '''awk 'BEGIN { for (i=0; i<6; i++) printf("%4i\n", i**i) }''''
| |
| 1
| |
| 1
| |
| 4
| |
| 27
| |
| 256
| |
| 3125
| |
| | |
| $ '''awk 'BEGIN { for (i=0; i<6; i++) printf("%-4i\n", i**i) }''''
| |
| 1
| |
| 1
| |
| 4
| |
| 27
| |
| 256
| |
| 3125
| |
| | |
| * Als Modifizierer stehen zur Verfügung:
| |
| {|class="wikitable"
| |
| |-
| |
| | | '''-'''
| |
| | | Linksbündige Ausrichtung
| |
| |-
| |
| | | '''+'''
| |
| | | Das Vorzeichen bei nummerischen Werten wird stets angegeben
| |
| |-
| |
| | | '''Leerzeichen'''
| |
| | | Bei positiven nummerischen Werten wird ein Leerzeichen vorangestellt
| |
| |-
| |
| | | '''0'''
| |
| | | Nummerische Werte werden von links her mit Nullen aufgefüllt
| |
| |-
| |
| | | '''#'''
| |
| | | Druckt bspw.
| |
| |-
| |
| |}
| |
| * bei "%x" ein "0x" vor den Wert
| |
| $ '''awk 'BEGIN { printf ("%#x %04i %+4i\n", 12, 12, 12)}''''
| |
| 0xc 0012 +12
| |
| * Von C's printf hat Awk ebenso die variable Angabe der Breiten- und Genauigkeitswerte übernommen.
| |
| * Anstatt des Wertes steht im Formatierer nun ein Stern.
| |
| * Der zu wählende Wert erscheint in der Argumentenliste vor dem jeweiligen Argument (Reihenfolge und Anzahl sollte übereinstimmen!):
| |
| | |
| == Übliche Form ==
| |
| $ '''awk 'BEGIN { printf("%12.11f\n", 11/13 ) }''''
| |
| | |
| == Dynamische Form ==
| |
| $ '''awk 'BEGIN {width=12; precision=11; printf("%*.*f\n", width, precision, 53/17 ) }''''
| |
| 0.84615384615
| |
| | |
| == Umleitung der Ausgabe ==
| |
| Die nachfolgend am Beispiel von '''print '''demonstrierten Mechanismen funktionieren ebenso mit '''printf.'''
| |
| * Normalerweise landen die Ausgaben auf dem Terminal (Standardausgabe).
| |
| | |
| Ihr Ziel kann mittels der schon von den Shells bekannten Umleitungen ebenso eine Datei oder eine Pipe sein.
| |
| '''print Argument > Ausgabedatei '''
| |
| <div >Schreiben in Datei; existiert diese, wird ihr Inhalt überschrieben </div>
| |
| '''print Argument >> Ausgabedatei '''
| |
| <div >Anfügen ans Ende der Datei; existiert sie nicht, wird sie erzeugt </div>
| |
| '''print Argument | Kommando '''
| |
| <div >Schreiben in eine Pipe, aus der ein Kommando liest </div>
| |
| * Anwendungsbeispiele für den Nutzen der Umleitung von Ausgaben in einer Datei finden sich reichlich in der alltäglichen Administration, z.B.
| |
| * bei der Auswertung der Logdateien.
| |
| * Gerade bei Servern häufen sich die Meldungen in der Datei /var/log/messages nur allzu schnell.
| |
| * Per Hand regelmäßig nach verdächtigen Zeilen Ausschau zu halten, wird rasch zur Last.
| |
| * So könnte Awk eine vorherige Selektion treffen und thematische Logdateien anlegen.
| |
| * Das folgende Beispiel zeigt eine Anwendung, die die Nachrichten des sshd und des telnetd filtert:
| |
| #!/usr/bin/awk -f
| |
| # 'filter.awk' filtert Meldungen des sshd und telnetd
| |
| # Aufruf: filter.awk /var/log/messages
| |
|
| |
| /.*sshd.*/ { print >> ENVIRON["HOME"]"/sshd.log"}
| |
| /.*in.telnetd.*/ { print >> ENVIRON["HOME"]"/telnet.log"}
| |
| * Verwenden Sie in einem der Argumente von print(f) den Vergleichsoperator »>«, so müssen Sie die Argumente klammern, da der Operator ansonsten als Umleitung verstanden wird!
| |
| * Die Ausgabe von print(f) in eine Pipe zu speisen, bietet sich an, falls das Ergebnis durch ein Kommando weiter bearbeitet werden soll.
| |
| * Schließen Sie dazu das Kommando inklusive seiner Argumente in doppelte Anführungszeichen ein.
| |
| * Als Beispiel werden Funde von telnet-Nachrichten sofort per Mail an den lokalen Administrator gemeldet:
| |
| #!/usr/bin/awk -f
| |
| # 'mailer.awk' meldet Nachrichten des telnetd an Root
| |
| # Aufruf: mailer.awk /var/log/messages
| |
|
| |
| /.*in.telnetd.*/ { print | "mail -s 'Telnet-Kontakt' root" }
| |
| * Derartige Kommandoaufrufe lassen sich auch in einer Variable speichern und darüber referenzieren:
| |
| ...
| |
| BEFEHL="mail -s 'Telnet-Kontakt' root"
| |
| ...
| |
| /.*in.telnetd.*/ { print | BEFEHL }
| |
| | |
| == Spezielle Dateinamen ==
| |
| Analog zu den Dateideskriptoren der Shells kennt GNU-Awk (nicht POSIX!) spezielle Dateinamen, um die Umleitung in konkrete Kanäle zu realisieren.
| |
| * Wenn Sie den letzten Befehl mit der Pipe als Muster hernehmen, sollte Ihnen zur Ausgabe über den Standardfehler-Deskriptor zumindest eine trickreiche Variante einfallen:
| |
| { print | "cat 1>&2" }
| |
| | |
| Eleganter geht es mit der »Datei« /dev/stderr:
| |
| { print | "/dev/stderr" }
| |
| | |
| Awk kennt folgende Dateien:
| |
| '''/dev/stdin''' Standardeingabe
| |
| '''/dev/stdout''' Standardausgabe
| |
| '''/dev/stderr''' Standardfehlerausgabe
| |
| '''/dev/fd/x''' Die mit dem Dateideskriptor x verbundene Datei
| |
| | |
| * Ein Zugriff auf bspw. /dev/fd/5 bedingt, dass zuvor eine Datei mit dem Deskriptor verbunden wurde.
| |
| | |
| '''Beispiel'''
| |
| | |
| $ '''exec 5>test.log'''
| |
| $ '''awk 'BEGIN {print "Testausgabe" > "/dev/fd/5"} ''''
| |
| | |
| Nun raten Sie einmal, was in der Datei »test.log« drin steht? Vollständigkeitshalber sei noch erwähnt, dass Awk einige Dateien kennt, um Informationen über die Prozessumgebung auszulesen. Dies sind:
| |
| '''/dev/pid''' Prozess-ID
| |
| '''/dev/ppid''' Prozess-ID des Eltern-Prozesses
| |
| '''/dev/pgrpid''' Prozess-Gruppen-ID
| |
| '''/dev/user''' 4 Werte zum Eigentümer des Prozesses
| |
| $ '''awk 'BEGIN{ getline < "/dev/user";'''
| |
| > print "UID", $1
| |
| > print "EUID", $2
| |
| > print "GID", $3
| |
| > print "EGID", $4
| |
| > } '
| |
| UID 500
| |
| EUID 500
| |
| GID 100
| |
| EGID 100
| |
| | |
| == Schließen von Dateien und Pipes ==
| |
| * Wenn Sie im Laufe eines Awk-Programms mehrfach via '''getline '''Daten aus ein und derselben Datei lesen, so liefert ein Aufruf die jeweils nächste Zeile dieser.
| |
| * Bezieht '''getline '''seine Daten aus einer Pipe und erfolgt auch hierbei der mehrfache Zugriff auf ein und denselben Befehl, so wird der Befehl nur einmalig ausgeführt und jeder '''getline '''-Aufruf bringt den nächsten Datensatz aus der Ausgabe des Befehls hervor.
| |
| Nicht immer jedoch ist dies das gewünschte Verhalten, bspw. wenn ein Befehl nur eine einzige Ausgabezeile liefert oder wir im Laufe der Berechnung an der ersten Zeile einer Datei interessiert sind:
| |
| $ awk 'BEGIN {
| |
| > for (i=0; i<2; i++) {
| |
| > "date" | getline x;
| |
| > print x;
| |
| > system("sleep 65")}
| |
| > }'
| |
| Don Dez 13 16:00:30 CET 2001
| |
| Don Dez 13 16:00:30 CET 2001
| |
| * Dass im Beispiel beide Male dieselbe Zeit ausgegeben wird, war sicherlich nicht das beabsichtigte Ergebnis.
| |
| Abhilfe schafft das Schließen der Pipe mit '''close''':
| |
| $ awk 'BEGIN {
| |
| > for (i=0; i<2; i++) {
| |
| > "date" | getline x
| |
| > print x
| |
| > system("sleep 65")
| |
| > close("date") }
| |
| > }'
| |
| Don Dez 13 16:01:10 CET 2001
| |
| Don Dez 13 16:02:15 CET 2001
| |
| * Verwenden Sie '''close '''im Zusammenhang mit Dateien, wenn Sie aus diesen »von vorn« lesen möchten.
| |
| | |
| = Arrays =
| |
| * Ein Array ist eine Variable, die einen Satz von Daten - Elemente genannt - aufnehmen kann.
| |
| * Der Elementzugriff erfolgt über einen Index, der eine Nummer oder eine Zeichenkette sein kann, wobei - im Falle von Zeichenketten - die Groß- und Kleinschreibung stets keine Rolle spielt, unabhängig vom Wert der internen Variablen IGNORECASE! Der Name eines Arrays darf nicht mit dem Namen einer »einfachen« Variablen kollidieren.
| |
| * Die Größe eines Arrays muss nicht vorab angegeben werden.
| |
| * Jederzeit lassen sich weitere Elemente zum Array hinzufügen, löschen oder der Wert eines Elements verändern.
| |
| == Einfügen / Löschen von Elementen ==
| |
| * Mit jeder Zuweisung eines Wertes an einen neuen Index wächst das Array um ein weiteres Element.
| |
| Im Falle eines existierenden Indizes wird der alte Wert durch den neuen ersetzt:
| |
| Array [Index] = Wert
| |
| * Neben eindimensionalen Arrays gestattet Awk auch die Verwendung mehrdimensionaler Felder.
| |
| * Im zweidimensionalen Fall sind zwei Indizes zur Adressierung eines Elements notwendig:
| |
| * ZweiDimensionalesArray [Index1, Index2] = Wert
| |
| * Intern bildet Awk mehrdimensionale Felder auf ein eindimensionales ab, indem die einzelnen Indizes implizit zu einer einzelnen Zeichenkette verkettet werden.
| |
| * Um die Indizes identifizieren zu können, wird der durch '''SUBSEP '''definierte Separator als Trennzeichen zwischen diese gesetzt.
| |
| * In der Voreinstellung von SUBSEP wird bspw. aus »f[1,2]« intern ein »f["[mailto:1@2 1@2]"]«.
| |
| * Beide Angaben sind äquivalent.
| |
| * Zum Entfernen eines Elements dient der '''delete '''-Operator.
| |
| Erforderlich ist die Angabe des Indexes des zu löschenden Elements:
| |
| '''delete''' array [Index]
| |
| * Wird '''delete '''einzig der Name eines Array übergeben - also ohne einen Index - werden sämtliche Elemente des Feldes gelöscht.
| |
| | |
| == Index-Zugriff ==
| |
| * Der Zugriff auf ein Element eines Feldes ist stets über seinen Index möglich.
| |
| * Dies setzt voraus, dass der Index bekannt ist.
| |
| | |
| Erfolgt eines Referenzierung mittels eines unbekannten Indizes, wird eine leere Zeichenkette als Ergebnis geliefert und gleichzeitig diese als Element des Arrays angelegt:
| |
| $ '''awk 'BEGIN { f[A]="foo";
| |
| > printf("_%s_%s_\n", f[A], f[B]);}''''
| |
| _foo_foo_
| |
| | |
| * Eventuell haben Sie mit dem Verständnis des Beispiels so Ihre Probleme? Kein Wunder, es wurde bewusst ein Fehler eingebaut.
| |
| * Und zwar werden A und B als Namen von Variablen betrachtet und beide zur leeren Zeichenkette evaluiert.
| |
| * Hieraus resultiert, dass jeweils auf »f[""]« - also auf einundenselben Feldindex - zugegriffen wird.
| |
| | |
| Die korrigierte Variante bringt das vorhergesagte Ergebnis:
| |
| $ a'''wk 'BEGIN { f["A"]="foo";
| |
| > printf("_%s_%s_\n", f["A"], f["B"]);}''''
| |
| _foo__
| |
| * Das automatische Anlegen eines neuen Indizes im Falle dessen Nichtexistenz ist vermutlich in etlichen Fällen unerwünscht.
| |
| | |
| Aus diesem Grund bietet Awk eine Abfrage an, um festzustellen, ob ein Index existiert:
| |
| if ( Index in Array ) {
| |
| # tue etwas
| |
| }
| |
| | |
| Ein komplexeres Beispiel zum Indexzugriff druckt die Felder der Datei /etc/passwd in tabellarischer Form, wobei die notwendige Spaltenbreite berechnet wird:
| |
| #!/usr/bin/awk -f
| |
|
| |
| BEGIN {
| |
| FS=":";
| |
| while ( getline < "/etc/passwd" )
| |
| for (i=1; i<=6; i++)
| |
| if (max[i] < length($i)) { max[i] = length($i)}
| |
|
| |
| close("/etc/passwd" )
| |
|
| |
| while ( getline < "/etc/passwd" )
| |
| printf("%-*s %*s %*s %-*s %-*s\n",
| |
| max[1], $1, max[3], $3,
| |
| max[4], $4, max[5], $5,
| |
| max[6], $6);
| |
| }
| |
| | |
| == Scannen eines Arrays ==
| |
| * Unter »Scannen eines Arrays« wollen wir den sequentiellen Zugriff auf alle enthaltenen Elemente in der Reihenfolge ihres Einfügens verstehen.
| |
| * Im Falle eindimensionaler Felder wird in Awk einfach in einer '''for '''-Schleife eine Variable der Reihe nach mit jedem Index aus dem Array verbunden.
| |
| | |
| Im Schleifenrumpf kann nachfolgend auf das mit dem Index verbundene Elemente zugegriffen werden.
| |
| for ( Index in Array ) {
| |
| # tue etwas (mit Array[Index])
| |
| }
| |
| | |
| Das nachfolgende Beispiel demonstriert die Anwendung des Scannens, indem eine Liste der Häufigkeiten des Auftretens von Wörtern aus der Eingabe generiert wird:
| |
| #!/usr/bin/awk -f
| |
|
| |
| BEGIN {
| |
| RS="[[:space:]]"
| |
| }
| |
| { ++wort[$0] }
| |
| END {
| |
| for (x in wort)
| |
| printf("%-20s %d\n", x, wort[x]);
| |
| }
| |
| | |
| Ein simpler Test beweist, dass das Skript (»WorkCounter.awk« genannt) tatsächlich funktioniert:
| |
| $ '''echo drei eins drei zwei zwei drei | WordCounter.awk'''
| |
| zwei 2
| |
| drei 3
| |
| eins 1
| |
| * Der Zugriff auf die Elemente in einem mehrdimensionalen Feld kann analog erfolgen, wenn der Elementzugriff über den verketteten Index genügt.
| |
| Werden hingegen die originalen Indizes benötigt, ist etwas mehr Aufwand zu betreiben:
| |
| for ( VollerIndex in Array ) {
| |
| split ( VollerIndex, Hilfsfeld, SUBSEP )
| |
| # Index1 steht in Hilfsfeld[1],
| |
| # Index2 steht in Hilfsfeld[2]) usw.
| |
| }
| |
| }
| |
| | |
| * Die Zeichenkettenfunktion '''split '''soll im folgenden Abschnitt behandelt werden.
| |
| * Sie trennt den Inhalt von VollerIndex an den durch SUBSEP vorgegebenen Positionen auf und speichert die einzelnen Bestandteile in Hilfsfeld.
| |
| | |
| Ein sinnfreies Beispiel veranschaulicht die Methodik der Index-Aufsplittung:
| |
| $ awk 'BEGIN {
| |
| > f[1,"foo",0815] = "egal";
| |
| > for (i in f) {
| |
| > split (i,hf,SUBSEP);
| |
| > for (x in hf) print hf[x]
| |
| > } }
| |
| 1
| |
| foo
| |
| 815
| |
| | |
| = Eingebaute Funktionen =
| |
| * Der bevorzugte Einsatzbereich von Awk ist die automatische Generierung von Reports und Statistiken.
| |
| * So existieren zahlreiche eingebaute Funktionen, die Awk zur Auswertung von Daten jeglicher Art prädestinieren.
| |
| * Eine Funktion ist gekennzeichnet durch einen Namen und der Liste der Argumente, die dem Namen, eingeschlossen in runde Klammern, folgen.
| |
| * Es ist bei einigen Funktionen zulässig, weniger Argumente anzugeben, als die Funktion eigentlich bedingt; welche voreingestellten Werte dann Awk einsetzt, unterscheidet sich von Funktion zu Funktion.
| |
| * Stets ein Syntaxfehler hingegen ist, mehr Argumente einer Funktion mitzugeben, als bei ihrer Definition vereinbart wurden.
| |
| * Finden Ausdrücke als Argumente Verwendung, so werden diese vor der Übergabe an die Funktion ausgewertet, d.h.
| |
| * bspw Funktion(i++); liefert (zumeist) ein anderes Ergebnis als Funktion(i); i++;.
| |
| * Allerdings ist die Reihenfolge der Auswertung der Argumente unspezifiziert; eine Funktion wie atan2(x++, x-1); kann in unterschiedlichen Implementierungen zur unterschiedlichen Ergebnissen führen.
| |
| == Mathematische Funktionen ==
| |
| Zur numerischen Berechnung stellt Awk folgende Funktionen zur Verfügung:
| |
| {|class="wikitable"
| |
| |-
| |
| | |
| |
| | | Liefert den Arcus-Tangens (in rad) von x/y
| |
| |-
| |
| | | <tt>'''cos(x) '''</tt>
| |
| | | Liefert den Consinus von x
| |
| |-
| |
| | | <tt>'''exp(x) '''</tt>
| |
| | | Berechnet ex
| |
| |-
| |
| | | <tt>'''int(x) '''</tt>
| |
| | | Ganzzahliger Wert von x, wobei »in Richtung 0« gerundet wird
| |
| |-
| |
| | | <tt>'''log(x) '''</tt>
| |
| | | Liefert den natürlichen Logarithmus von x
| |
| |-
| |
| | | <tt>'''rand() '''</tt>
| |
| | | Liefert eine Zufallszahl im Bereich von [0,1]
| |
| |-
| |
| | | <tt>'''sin(x) '''</tt>
| |
| | | Liefert den Sinus von x
| |
| |-
| |
| | | <tt>'''sqrt(x)'''</tt>
| |
| | | Liefert die Quadratwurzel von x
| |
| |-
| |
| | | <tt>'''srand([x]) '''</tt>
| |
| | | Setzt den Startwert für die Zufallszahlen-Generierung [mittels rand()].
| |
| * Geliefert wird der »alte« Startwert
| |
| |-
| |
| |}
| |
| | |
| * Die Funktion zur Erzeugung von Zufallszahl liefert nicht wirklich zufällige Zahlen, sondern eine Folge relativ gleichverteilter Zahlen im Intervall [0,1].
| |
| * Mit jedem Start eines Awk-Programms wird somit stets dieselbe Folge von Zufallszahlen generiert.
| |
| * Genügt dieser »Zufall« nicht aus, muss mit '''srand() '''ein neuer Bezugspunkt für '''rand() '''gesetzt werden. '''srand() '''ohne Argument erzeugt diesen Startwert aus aktuellem Datum und Uhrzeit, womit '''rand() '''tatsächlich den Eindruck zufälliger Werte erweckt.
| |
| * Einige Anwendungsbeispiele sollen die Verwendung der Funktionen demonstrieren:
| |
| # Berechnung von π
| |
| $ '''awk 'BEGIN { printf("π=%.50f\n", 4*atan2(1,1)); }''''
| |
| π=3.14159265358979311599796346854418516159057617187500
| |
| | |
| # Ganzzahliger Anteil eines Wertes
| |
| $ '''awk 'BEGIN { print int(-7), int(-7.5), int (7), int(7.5)}''''
| |
| -7 -7 7 7
| |
| | |
| # Der natürliche Logarithmus von e1
| |
| $ '''awk 'BEGIN { print log(exp(1))}''''
| |
| 1
| |
| | |
| '''Anmerkung'''
| |
| * Das Beispiel der Berechnung von π zeigt die Genauigkeitsschranke der verwendeten Awk-Implementierung; ab der 48. Nachkommastelle ist Schluss.
| |
| * Allerdings genügt die interne Darstellung, um bei Umkehrrechnungen auf den Ausgangswert zu kommen (log(exp(1))).
| |
| | |
| == Zeichenkettenfunktionen ==
| |
| * Die nachfolgend diskutierten Funktionen durchsuchen oder manipulieren Zeichenketten.
| |
| {|class="wikitable"
| |
| |-
| |
| | | <tt>'''gsub(Regex,Ersatz,[String])'''</tt>
| |
| | | Jedes Vorkommen des Regulären Ausdrucks Regex in String wird durch Ersatz ersetzt.
| |
| * Wird String nicht angegeben, wird $0 bearbeitet.
| |
| * Rückgabewert ist die Anzahl der Ersetzungen.
| |
| |-
| |
| | | <tt>'''index(Suchstring,Muster)'''</tt>
| |
| | | Liefert die Position, an der Muster in Suchstring erstmals vorkommt oder 0
| |
| |-
| |
| | | <tt>'''length([String])'''</tt>
| |
| | | Liefert die Länge von String; fehlt String, wird die länge von $0 zurückgegeben
| |
| |-
| |
| | | <tt>'''match(String,Regex)'''</tt>
| |
| | | Liefert die Position des ersten Auftretens des Regulären Ausdrucks Regex in String; setzt RSTART und RLENGTH
| |
| |-
| |
| | | <tt>'''split(String,Feld,[Seperator])'''</tt>
| |
| | | Zerlegt String in einzelne Elemente und legt diese in Feld ab.
| |
| * Als Trennzeichen dient Seperator oder FS, falls dieser nicht angegeben wurde.
| |
| * Rückgabewert ist die Anzahl der Elemente.
| |
| |-
| |
| | | <tt>'''sprintf("Format",Ausdruck)'''</tt>
| |
| | | Anwendung wie '''printf, '''anstatt einer Ausgabe gibt die Funktion die resultierende Zeichenkette zurück
| |
| |-
| |
| | | <tt>'''sub(Regex,Ersatz,[String])'''</tt>
| |
| | | Arbeitsweise wie '''gsub, '''es wird aber nur das erste Muster ersetzt
| |
| |-
| |
| | | <tt>'''substr(string,p,[l])'''</tt>
| |
| | | Gibt eine Teilzeichenkette von string der Länge l, beginnend an Position p zurück
| |
| |-
| |
| | | <tt>'''tolower(string)'''</tt>
| |
| | | Wandelt Groß- in Kleinbuchstaben um, Rückgabewert ist die neue Zeichenkette
| |
| |-
| |
| | | <tt>'''toupper(string)'''</tt>
| |
| | | Wandelt Klein- in Großbuchstaben um, Rückgabewert ist die neue Zeichenkette
| |
| |-
| |
| |}
| |
| == Ermitteln der längsten Zeile einer Datei ==
| |
| $ '''expand default.htm | awk '{x = (x < length()) ? length():x;} END {print x}''''
| |
| 566
| |
| == In welcher Zeile und welcher Position erscheint ein Muster ==
| |
| $ '''awk '/Partitionstabelle/ {print "Zeile:", NR, "Spalte", index($0,"Partitionstabelle")}'''' Linuxfibel/installbefore.htm
| |
| Zeile: 804 Spalte 14
| |
| Zeile: 818 Spalte 81
| |
| Zeile: 837 Spalte 57
| |
| Zeile: 838 Spalte 41
| |
| Zeile: 843 Spalte 41
| |
| Zeile: 972 Spalte 12
| |
| | |
| == Sonstige Funktionen ==
| |
| {|class="wikitable"
| |
| |-
| |
| | | <tt>'''close(Datei) '''</tt>
| |
| | | Schließt eine zuvor zum Lesen oder Schreiben geöffnete Datei oder die Pipe, die als Eingabe oder Ausgabe eines Kommandos diente (dann muss anstatt eines Dateinamens der Kommandoaufruf angegeben werden)
| |
| |-
| |
| | | <tt>'''fflush(Datei)'''</tt>
| |
| | | Leert unverzüglich den Puffer einer »gepufferten« Ausgabe.
| |
| * D.h.
| |
| * veränderte Daten einer Datei werden sofort zurückgeschrieben oder die in eine Pipe geleitete Ausgabe eines Kommandos wird sofort aus dieser entnommen.
| |
| * Bei fehlendem Argument entleert Gawk die Standardausgabe; bei Angabe der leeren Zeichenkette ("") werden die Puffer sämtlicher offener Ausgabedateien und Pips entleert.
| |
| |-
| |
| | | <tt>'''system(Kommando) '''</tt>
| |
| | | Gestattet die Ausführung von beliebigen Kommandos.
| |
| * Nach Ende eines solchen wird im Awk-Programm fortgefahren.
| |
| * system liefert den Status des letzten gestarteten Kommandos zurück.
| |
| |-
| |
| | | <tt>'''systime()'''</tt>
| |
| | | Liefert die Systemzeit in Sekunden (seit 1.1.1970)
| |
| |-
| |
| | | <tt>'''strftime([Format [,Zeitstempel]])'''</tt>
| |
| | | Liefert eine Datumszeichenkette.
| |
| * Ohne Zeitstempel wird die aktuelle Systemzeit verwendet, das Format entspricht dem der gleichnamigen C-Funktion.
| |
| |-
| |
| |}
| |
| * Beispiele zur Anwendung von '''close() '''finden sich im Text genügend.
| |
| | |
| Hier soll eine unnütze Anwendung von '''systime() '''den Abschnitt beschließen.
| |
| $ '''awk 'BEGIN { start = systime();
| |
| > for (i = 1; i < 10000000; i++) ;
| |
| > end = systime();
| |
| > print "Gesamtzeit betrug", end - start, "Sekunden.";
| |
| }''''
| |
| Gesamtzeit betrug 6 Sekunden.
| |
| | |
| = Eigene Funktionen =
| |
| * Die eingebauten Funktionen decken einen weiten, aber doch nicht jeden Anwendungsbereich ab.
| |
| * Erst eigene Funktionen eröffnen den Weg zu effizienten Programmen, vor allem dann, wenn identische Abläufe wiederholt im Programm auftreten.
| |
| == Funktionsdefinition ==
| |
| * Eine Funktion muss vor ihrer ersten Verwendung definiert sein.
| |
| * Ihre Definition erfolgt zweckmäßig zu Beginn eines Awk-Skripts, erlaubt ist sie auch zwischen BEGIN-Block und Hauptprogramm bzw. zwischen Hauptprogramm und END-Block.
| |
| Der allgemeine Aufbau einer Funktion ist:
| |
| function name([Parameter [, Parameter] ]) {
| |
| # Anweisungen...
| |
| }
| |
| | |
| * Als Funktionsname sind alle Kombinationen aus Buchstaben, Ziffern und dem Unterstrich zulässig, wobei zu Anfang keine Ziffer stehen darf.
| |
| * In einem Programm darf eine Funktion nicht gleich lauten wie der Bezeichner einer Variable.
| |
| * Als Argumente können beliebig viele Parameter an eine Funktion übergeben werden, wobei das Komma als Trennzeichen dient.
| |
| | |
| Als erstes Beispiel berechnet eine Funktion '''facultiy '''die Fakultät zu einer Zahl x:
| |
| function faculty(x) {
| |
| if (x = 0) return 1;
| |
| return x*faculty(x-1)
| |
| }
| |
| * Unsere Version von '''faculty '''ist rekursiv realisiert, was Awk problemlos akzeptiert.
| |
| * Zur Rückehr aus einer Funktion kann '''return '''mit optionalem Rückgabewert verwendet werden.
| |
| * Fehlt '''return, '''kehrt die Funktion nach Ausführung der letzten Anweisung zurück.
| |
| | |
| == Funktionsaufruf ==
| |
| * Zur Verwendung der Funktion rufen Sie diese einfach auf.
| |
| * Im Gegensatz zu den eingebauten Funktionen darf bei nutzerdefinierten Funktionen kein Leerzeichen zwischen Funktionsname und der öffnenden runden Klammer stehen!
| |
| * Die Angabe von weniger Parametern als definiert ist zulässig; abhängig vom Kontext werden diese als leere Zeichenkette oder als 0 interpretiert.
| |
| | |
| Im folgenden Programm wird der zu berechnende Wert per Kommandozeilenargument übergeben:
| |
| #!/usr/bin/awk -f
| |
|
| |
| function faculty(x) {
| |
| if (x = 0) return 1;
| |
| return x*faculty(x-1)
| |
| }
| |
|
| |
| BEGIN {
| |
| if (ARGC < 2) {
| |
| print "Fehlendes Argument!";
| |
| exit 1;
| |
| }
| |
| if (ARGV[1] !~ /^[[:digit:]]+$/) {
| |
| print "Unzulaessiges Argument!";
| |
| exit 2;
| |
| }
| |
| print faculty(ARGV[1]);
| |
| }
| |
| | |
| == Lokale und globale Variablen, Parameterübergabe ==
| |
| * Damit haben Sie fast das Rüstzeug beisammen, um eigene komplexe Funktionen zu verfassen.
| |
| * Doch die eigenwillige Semantik Semantik der Verwendung von Variablen kann zumindest für den Programmierneuling schnell zu Stolperfalle werden.
| |
| * Mit einer Ausnahme hat eine Funktion grundsätzlichen Zugriff auf alle Variablen, die im Programm bis zum Aufruf der Funktion eingeführt wurden.
| |
| * Solche '''globalen '''Variablen können in der Funktion verändert, gelöscht (nur Array-Elemente [delete]) oder sogar neu eingeführt werden.
| |
| '''Lokale '''Variablen sind einzig jene, die in der Parameterliste benannt wurden.
| |
| * Diese Variablen verdecken ggf. vorhandene gleichnamige Variablen des Programms, sodass Änderungen an diesen in der Funktion »außen« nicht sichtbar werden.
| |
| * Das folgende Programm demonstriert die Verwendung von globalen und lokalen Variablen
| |
| $ '''cat context.awk'''
| |
| #!/usr/bin/awk -f
| |
|
| |
| function context(x, a)
| |
| {
| |
| printf("In der Funktion...\n")
| |
| printf("\tx = %d\n\ta = %d\n\tb = %d\n", x, a, b);
| |
| x = a = b = 99;
| |
| }
| |
|
| |
| BEGIN {
| |
| x = a = 100;
| |
| printf("Vor Aufruf der Funktion...\n")
| |
| printf("\tx = %d\n\ta = %d\n\tb = %d\n", x, a, b);
| |
|
| |
| context(10);
| |
|
| |
| printf("Nach Aufruf der Funktion...\n")
| |
| printf("\tx = %d\n\ta = %d\n\tb = %d\n", x, a, b);
| |
| }
| |
| | |
| $ '''./context.awk'''
| |
| Vor Aufruf der Funktion...
| |
| x = 100
| |
| a = 10
| |
| b = 0
| |
| In der Funktion...
| |
| x = 10
| |
| a = 0
| |
| b = 0
| |
| Nach Aufruf der Funktion...
| |
| x = 100
| |
| a = 100
| |
| b = 99
| |
|
| |
| Das abschließende Beispiel dreht einem das Wort im Munde um...
| |
| function reverse (x) {
| |
| if (length(x) = 0)
| |
| return "";
| |
| return substr(x, length(x), 1) reverse(substr(x,1, length(x)-1));
| |
| }
| |
| | |
| = Weitere Informationen =
| |
| == Intern ==
| |
| # [[gawk:Beispiele]]
| |
| | |
| == Weblinks ==
| |
| # http://de.wikibooks.org/wiki/Awk
| |
| # http://www.gnu.org/software/gawk/manual/
| |
| | |
| [[Kategorie:Linux:Befehl]]
| |
| [[Kategorie:Bash:Scripting]]
| |
| [[Kategorie:Programmierung]]
| |
| [[Kategorie:Linux:Dateien]]
| |
| {{DEFAULTSORT:gawk}}
| |