Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
Über die Autoren
Über dieses Buch
Linux vs. BSD
1 Der Kernel
2 Die Grundlagen aus Anwendersicht
3 Die Shell
4 Reguläre Ausdrücke
5 Tools zur Dateibearbeitung
6 Die Editoren
7 Shellskriptprogrammierung
8 Benutzerverwaltung
9 Grundlegende Verwaltungsaufgaben
10 Netzwerk-Grundlagen
11 Anwendersoftware für das Netzwerk
12 Netzwerkdienste
13 Mailserver unter Linux
14 LAMP
15 DNS-Server
16 Secure Shell
17 Die grafische Oberfläche
18 Window-Manager und Desktops
19 X11-Programme
20 Multimedia und Spiele
21 Softwareentwicklung
22 Crashkurs in C und Perl
23 Sicherheit
24 Prozesse und IPC
25 Bootstrap und Shutdown
26 Dateisysteme
27 Virtualisierung und Emulatoren
A Die Installation
B Lösungen zu den einzelnen Aufgaben
C Kommandoreferenz
D X11-InputDevices
E MBR
F Die Buch-DVDs
G Glossar
H Literatur

Download:
- ZIP, ca. 6,3 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Linux von Johannes Plötner, Steffen Wendzel
Das distributionsunabhängige Handbuch
Buch: Linux

Linux
2., aktualisierte und erweiterte Auflage
1119 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1090-4
gp 4 Reguläre Ausdrücke
  gp 4.1 Aufbau von regulären Ausdrücken
  gp 4.2 Der Stream-Editor sed
    gp 4.2.1 Was bringt mir sed?
    gp 4.2.2 Erste Schritte mit sed
    gp 4.2.3 sed-Befehle
    gp 4.2.4 Nach Zeilen filtern
    gp 4.2.5 Wiederholungen in regulären Ausdrücken
  gp 4.3 grep
    gp 4.3.1 grep -E und egrep
    gp 4.3.2 Geschwindigkeitsvergleich
  gp 4.4 awk
    gp 4.4.1 Nutzen und Interpreter
    gp 4.4.2 Der Aufruf von awk
    gp 4.4.3 Erste Gehversuche
    gp 4.4.4 Der Anweisungsblock
    gp 4.4.5 Variablen
    gp 4.4.6 Arrays
    gp 4.4.7 Bedingte Anweisungen
    gp 4.4.8 Schleifen
    gp 4.4.9 Funktionen in awk
    gp 4.4.10 Ein paar Worte zum Schluss
  gp 4.5 Zusammenfassung
  gp 4.6 Aufgaben


Galileo Computing

4.4 awk  downtop

Bei awk handelt es sich um eine Programmiersprache für deren Anwendung sich die Autoren, die sie beschreiben wollen, immer interessante Beispiele für deren Anwendung ausdenken. In [Herold03A] werden beispielsweise Lebensmitteltabellen und Bundesliegaergebnisse ausgewertet, in [VoReJo97A] begnügt man sich mit einer Nummernliste, die nach Telefonnummern aussieht, und in unserem Buch »Einstieg in Linux« ([WendPloe04A]) sind es Wohnorte und Benutzer-IDs. Mal sehen, was wir diesmal nehmen.

Die drei Zeichen, aus denen der Name des Programms besteht, leiten sich von den Namen derer ab, die awk (den awk-Interpreter) programmierten:

Alfred V. Aho, Peter J. Weinberger und Brian W. Kernighan. Diese drei schrieben übrigens auch ein Buch ([AhWeKe88A]) über awk.

Doch mittlerweile gibt es neuere Bücher zu dieser Sprache, etwa [Herold03A] oder ganz einfach das vorliegende Buch – nach dem Lesen (und Verstehen) dieses Unterkapitels werden Sie das für den täglichen Umgang mit awk Wichtige wissen. Ein vollständiges Buch zur awk-Programmiersprache ersetzt es jedoch nicht.

Bei OpenBSD-Systemen finden Sie ein weiteres awk-Buch der Autoren im Verzeichnis /usr/share/doc/usd/16.awk. Lassen Sie dort make durchlaufen, was die Datei paper.ps erzeugt, die Sie beispielsweise mit GhostView (gv) unter X11 ansehen können.


Galileo Computing

4.4.1 Nutzen und Interpreter  downtop

Die Programmiersprache dient, ähnlich wie sed, zum Auseinandernehmen und Verarbeiten von Streams. Jedoch bietet awk Ihnen weitaus umfangreichere Möglichkeiten. Es handelt sich dabei schließlich um eine ganze Programmiersprache. Solch eine Skriptsprache benötigt natürlich auch einen Interpreter, der die Anweisungen im Code einliest und ausführt. Dieser Interpreter nennt sich schlicht awk. awk ist die älteste Interpreter-Implementierung von awk, es gibt noch neuere Versionen wie nawk (u.a. BSD) und gawk von GNU. In diesem Buch arbeiten wir mit der Variante (n)awk.

Die Syntax von awk ist sehr stark an die Programmiersprache C angelehnt, zudem ist die Sprache äußerst einfach zu erlernen – bereits mit wenigen Zeilen Code kann ein komplexes Problem gelöst werden, für das man in Sprachen wie C einige hundert Zeilen Quellcode benötigen würde. <Allerdings sind interpretierte Sprachen dafür auch viel langsamer als ein in eine Binärdatei übersetztes C-Programm. Eine gute Ausnahme stellt Common-Lisp dar, das auch ohne Bytecode-Übersetzung relativ schnell läuft.>


Galileo Computing

4.4.2 Der Aufruf von awk  downtop

Ein awk-Aufruf setzt sich aus mehreren Parametern zusammen, die teilweise optional sind.

$ awk [Ausdruck] [{ Anweisungen }] [Datei]

Listing 4.21    awk-Aufrufschema

Ausdruck

Der erste Parameter ist ein regulärer Ausdruck. Dieser Ausdruck muss nicht immer übergeben werden. Da awk, wie auch sed, den Input-Stream zeilenweise durcharbeitet, kann durch den Ausdruck-Parameter jedoch eine Filterung realisiert werden. In englischen Büchern nennt man diesen Ausdruck oft »Pattern«, in einigen deutschsprachigen Büchern aber auch »Muster« – gemeint ist immer das Gleiche: der reguläre Ausdruck.

Anweisungen

Den zweiten Parameter stellen die awk-Anweisungen – der eigentliche Skriptcode – dar. Diese Anweisungen legen fest, welche Manipulationen am Input-Stream durchgeführt werden sollen. Wichtig ist dabei, dass diese Anweisungen in geschweifte Klammern eingebettet werden. Auch der Anweisungen-Parameter muss nicht immer übergeben werden.

Datei

Die Datei legt die Datei fest, aus der der Input-Stream gelesen werden soll. Sie müssen auch diesen Parameter nicht angeben – awk liest in diesem Fall von der Standardeingabe oder aus einer Pipe.

Wie Sie sehen, scheinen alle awk-Parameter optionaler Natur zu sein, doch dies stimmt nicht ganz: Es muss immer entweder ein Pattern oder eine Aktion (oder beides) angegeben werden, ganz ohne Pattern und Aktion verweigert awk den Dienst.

Zudem muss ein Input-Stream vorhanden sein, dabei ist es jedoch egal, ob dieser nun aus einer angegebenen Datei oder einer Pipe gelesen wird.


Galileo Computing

4.4.3 Erste Gehversuche  downtop

Reguläre Ausdrücke

Testen wir doch einmal das bisher Gelernte, nämlich reguläre Ausdrücke im Zusammenhang mit awk. Dabei werden wir zunächst einmal nur einen regulären Ausdruck, aber keine awk-Anweisungen übergeben. Als Input-Stream soll dabei die Datei /etc/group dienen.

Um den kompletten Inhalt der Datei auszugeben, muss eigentlich nur ein regulärer Ausdruck angegeben werden, der zwangsweise auf alle Zeilen in der Input-Datei zutrifft. Auf den ersten Blick bietet sich dabei der Ausdruck ».« an. Im Falle der Datei /etc/group mag dies auch funktionieren, da so jede ausgegeben wird Zeile in der ein beliebiges Zeichen enthalten ist.

Sofern allerdings eine Leerzeile in einer Datei vorkommt, würde dies nicht mehr funktionieren. Dieses Problem kann umgangen werden, indem man ein beliebig häufiges Vorkommen eines beliebigen Zeichens als regulären Ausdruck angibt:

$ awk '/(.)*/' /etc/group 
wheel:*:0:root,cdp_xe 
daemon:*:1:daemon 
kmem:*:2:root 
sys:*:3:root 
tty:*:4:root 
operator:*:5:root 
bin:*:7: 
news:*:8: 
wsrc:*:9: 
users:*:10: 
...

Listing 4.22    awk gibt eine Datei aus.

Möchten wir nun alle Zeilen herausfiltern, in denen ein »n« enthalten ist, geht auch dies völlig problemlos:

$ awk '/n/' /etc/group 
daemon:*:1:daemon 
bin:*:7: 
news:*:8: 
_identd:*:29: 
_fingerd:*:33: 
_sshagnt:*:34: 
_kadmin:*:60: 
_token:*:64: 
crontab:*:66: 
network:*:69:

Listing 4.23    awk filtert nach »n«.

Wie Sie sehen, kann awk (wie es bereits bei sed der Fall war) auf diese Weise die (Grund)Aufgaben des Programms grep übernehmen.

Anweisungen

Lässt man nun den regulären Ausdruck weg und verwendet an seiner Stelle eine einfache Anweisung, kann man ebenfalls den Dateiinhalt ausgeben. Da nun kein Filter (eben durch einen regulären Ausdruck) vorgegeben ist, werden alle Zeilen des Input-Streams an die Anweisungen zur Verarbeitung weitergegeben. Nun müssen diese Zeilen nur noch durch eine entsprechende Ausgabe-Anweisung auf dem Bildschirm ausgegeben werden. Dazu verwenden wir die Anweisung print.

$ awk '{print\' /etc/group} 
wheel:*:0:root,cdp_xe 
daemon:*:1:daemon 
kmem:*:2:root 
sys:*:3:root 
tty:*:4:root 
operator:*:5:root 
bin:*:7: 
news:*:8: 
wsrc:*:9: 
users:*:10: 
...

Listing 4.24    awk mit Anweisungen

Nun beides zusammen

Nun sind wir so weit, dass wir beide Parameter, nämlich den des regulären Ausdrucks und den der Anweisungen, kombinieren können. Das hierfür verwendete Beispiel ist nicht sonderlich sinnvoll, verdeutlicht jedoch sehr einfach die Funktionsweise von awk.

Hierzu lassen wir den regulären Ausdruck alle Zeilen herausfiltern, in denen ein »x« enthalten ist. Diese Zeilen werden an den Anweisungsblock weitergereicht. Da der Anweisungsblock nur die Anweisung print enthält, werden alle gefilterten Zeilen auf dem Bildschirm ausgegeben.

$ awk '/x/ {print\' /etc/group} 
wheel:*:0:root,cdp_xe 
_x11:*:35: 
proxy:*:71: 
cdp_xe:*:1000:

Listing 4.25    Regulärer Ausdruck und eine Anweisung


Galileo Computing

4.4.4 Der Anweisungsblock  downtop

Im Anweisungsblock – also in dem Teil, der in zwei geschweifte Klammern eingebettet wird – sind ein paar Regeln zu beachten.

Anweisungen separieren

In awk können Anweisungen nicht einfach hintereinandergeschrieben werden. Damit die Shell weiß, wo eine Anweisung endet und eine neue beginnt, muss – wie in der Shell – eine Separierung der Anweisungen erfolgen. Auch in awk wird diese Separierung durch ein Semikolon (;) realisiert. Außerdem können Anweisungen durch Zeilen separiert werden.

{ 
       Anweisung1 
       Anweisung2 
       Anweisung3  Anweisung4      // Fehler! 
       Anweisung3 ; Anweisung4    // Richtig! 
}

Listing 4.26    Anweisungen

Eine weitere Möglichkeit

Neben der Separierung durch ein Semikolon ist es noch möglich, Anweisungen durch geschweifte Klammern zu separieren.

Dies wird bei bedingten Anweisungen verwendet, funktioniert aber auch außerhalb. Allerdings gehört die Verwendung von geschweiften Klammern außerhalb von bedingten Anweisungen zum schlechten (weil unübersichtlichen)

Programmierstil und wird daher nur der Vollständigkeit halber erwähnt.

{ 
      { Anw1 } Anw2 { Anw3 } Anw4 
      { 
          Anw5 
      } Anw6 
}

Listing 4.27    Anweisungen mit {}

BEGIN und END

Bisher wissen Sie nur, dass es so etwas wie einen Anweisungsblock gibt und dass in diesen die Anweisungen hineingepackt werden. In dem Anweisungsblock werden alle Anweisungen nacheinander ausgeführt, was allerdings nicht immer so einfach ausreicht.

Der Hauptanweisungsblock wird für jede Zeile erneut ausgeführt. Was aber tut man, wenn man vor der Bearbeitung der einzelnen Zeilen einige Anweisungen von awk ausführen lassen möchte? Und was tut man, wenn man möchte, dass, nachdem alle Eingabezeilen verarbeitet worden sind, noch weitere Anweisungen ausgeführt werden?

Die Antwort auf diese Fragen liefert die Unterteilung der Anweisungsblöcke in drei Bereiche: den Anfangsteil, den Hauptteil und den Endteil. Man könnte auch von einer aufsatzähnlichen Einteilung in Einleitung, Hauptteil und Schluss sprechen, falls Sie es sich anhand dieser Analogie einfacher merken können.

Der Anfangsteil wird durch das Schlüsselwort BEGIN eingeleitet, der Endteil durch das Schlüsselwort END.

Der Hauptteil benötigt kein Schlüsselwort und wird, wie bereits bekannt, einfach durch geschweifte Klammern begrenzt. Diese Klammern werden auch zur Begrenzung der BEGIN- und END-Blöcke verwendet. Somit ergibt sich der folgende schematische Aufbau eines awk-Skripts:

BEGIN { 
        Anweisung1; 
        Anweisung2; 
        ... 
        AnweisungN; 
} 
 
{ 
        Anweisung1; 
        ... 
        AnweisungN; 
} 
 
END { 
        Anweisung1; 
        Anweisung2; 
        ... 
        AnweisungN; 
}

Listing 4.28    Der Aufbau eines awk-Skripts

Nehmen wir einmal an, es soll eine Datei mit drei Zeilen verarbeitet werden und vor und nach der Verarbeitung ein bestimmter Text ausgegeben werden. Diese Textausgabe wird mit der print-Anweisung umgesetzt, die wir im Laufe des Kapitels noch näher betrachten werden. Dabei ist zu beachten, dass wir den auszugebenden Text in Anführungszeichen geschrieben haben.

$ awk ' BEGIN { 
      print "Es folgt der Dateiinhalt:" 
} 
 
{ 
      print 
} 
 
END { 
      print "Das Ende der Datei ist erreicht." 
      print "Tschüss!" 
\ ' /tmp/Inputdatei}

Listing 4.29    END und BEGIN angewandt

Wenn /tmp/Inputdatei den Inhalt

zeile1 
zeile2 
zeile3

Listing 4.30    /tmp/Inputdatei

hat, ergibt sich beim Ausführen des Befehls die folgende Ausgabe:

Es folgt der Dateiinhalt: 
zeile1 
zeile2 
zeile3 
Das Ende der Datei ist erreicht. 
Tschüss!

Listing 4.31    Die Ausgabe des awk-Aufrufs

Kommentare

Um Kommentare in einem awk-Skript unterzubringen, verwendet man das Rauten-Zeichen (#). Alles, was hinter diesem Zeichen steht, wird vom Interpreter als Kommentar interpretiert und nicht weiter beachtet. Kommentare bieten Ihnen die Möglichkeit, Ihre Skripts übersichtlicher zu gestalten und komplizierte Anweisungsabschnitte zu kommentieren.

Besonders, wenn man nach einigen Monaten oder gar Jahren noch einmal etwas an einem alten awk-Skript verändern möchte (oder muss), wird man sich darüber freuen, dass man komplizierte Stellen im Skriptcode mit Kommentaren versehen hat.

{ 
     # Spalte 3 enthält den Benutzernamen 
 print $3 
}

Listing 4.32    Ein simpler Beispielkommentar

Lange Zeilen

Manchmal, etwa bei langen Berechnungsformeln, kommen sehr lange Zeilen zustande. Lange Zeilen lassen sich jedoch je nach Editor relativ schlecht editieren und sind unübersichtlich. awk bietet die Möglichkeit, diese langen Zeilen auf mehrere kleine aufzuteilen, wobei man einfach einen Backslash (\) an das Ende einer Zeile stellt, die in der nächsten Zeile fortgesetzt werden soll.

      # Dies geht nicht. Die zweite Zeile würde als 
      # eigene Anweisung interpretiert werden und im 
      # awk-Interpreter einen Fehler verursachen: 
      print "Dies ist ein Text. Und hier ist" 
      "noch ein Wert:" 
 
      # So ist es richtig: 
      print "Dies ist ein Text. Und hier ist" \ 
      "noch ein Wert:"

Listing 4.33    Anwenden des Backslashs


Galileo Computing

4.4.5 Variablen  downtop

Eine Variable ist ein transparentes Mittel, um auf eine Adresse im Speicherbereich zuzugreifen. Dabei gibt man einer Variable einen Namen. Über diesen Namen kann man immer wieder auf diesen Speicherbereich zugreifen und die darin enthaltenen Daten manipulieren. Bei diesen Daten kann es sich sowohl um eine Zahl als auch um einen oder mehrere Buchstaben oder auch um eine Kombination aus Zahlen, großen und kleinen Buchstaben und Sonderzeichen, etwa einem $, handeln.

In awk wird dabei zwischen einer Variable, die nur aus Zahlen besteht (mit denen sich rechnen beziehungsweise vergleichen lässt), und einem so genannten String unterschieden, dem man neben Zahlen eben auch Buchstaben und Sonderzeichen zuweisen kann.

Der Name einer Variable sollte dabei nur aus Ziffern, Buchstaben und Unterstrichen bestehen. Dabei ist jedoch zu beachten, dass das erste Zeichen im Namen keine Ziffer ist. Gültige Variablennamen sind also beispielsweise »katze«, »katze2«, »_katze« oder »_123KaTzE321_«. Ungültig wären hingegen: »123katze«, »&katze« oder »#Katze«, wobei Letzteres als Kommentar gewertet werden würde.

Zudem sollten Variablen keine Namen von Builtin-Funktionen wie print oder printf tragen, da dies zu Problemen führt. Diese Builtin-Funktionen müssen Sie allerdings noch nicht zu diesem Zeitpunkt kennen, wir werden sie jedoch im weiteren Verlauf dieses Kapitels noch besprechen.

Variablen deklarieren und initialisieren

Eine Variable wird deklariert, indem man ihren Namen im awk-Skript einfügt. Dadurch weiß awk zumindest schon einmal, dass es eine Variable mit diesem Namen gibt. Über den Speicherbereich, der dieser Variable zugewiesen wird, müssen Sie sich übrigens nicht kümmern, dies erfolgt nämlich völlig transparent.

Die Initialisierung einer Variable, also die Zuweisung eines Wertes, wird durch das Gleichheitszeichen (=) realisiert. Dies geschieht in der Form variable=Wert und funktioniert sowohl mit Zahlen als auch mit ganzen Strings:

BEGIN { 
   # Der Name einer Katze sei Felix, 
   KatzenName="Felix" 
 
   # ihr Wohnort sei Ettenbeuren. 
   Wohnort="12345 Ettenbeuren" 
 
   # Die Hausnummer hingegen sei 123. 
   Hausnummer=123 
}

Listing 4.34    Variablen deklarieren und initialisieren

Werte manipulieren

Die Werte von Variablen kann man auf verschiedene Arten manipulieren. Entweder weist man einen komplett neuen Wert durch das Gleichheitszeichen zu oder manipuliert auf Basis des bisherigen. Bei der Manipulation ist zu unterscheiden, ob man Zahlenwerte oder Strings manipuliert. Mit Zahlenwerten können Rechenoperationen durchgeführt werden, bei Strings geht es um die Manipulation von Zeichen.

   KatzenName="Felix" 
   # Nun wird der alte Wert verworfen und durch 
   # "Mauzi" ersetzt 
   KatzenName="Mauzi"

Listing 4.35    Erneute Wertzuweisung durch =

Zahlenwerte manipulieren

Um zunächst einmal grundlegende Rechenoperationen durchführen zu können, muss man Zahlenwerte manipulierennn können.

Zum Rechnen sind nicht unbedingt Variablen notwendig, man kann auch direkt rechnen und das Ergebnis einer Berechnung ausgeben lassen. Jedoch ist es oftmals sehr sinnvoll, ein Ergebnis oder die Zahlen, die in eine Rechnung einfließen, in Variablen zu packen. Das macht die Programmierung einfacher und die Programmstruktur übersichtlicher, dynamischer und verständlicher.

Nehmen wir zur Erläuterung dieses Satzes einmal folgende Situation: Wir wollen eine Rechnung durchführen, bei der der Wert einer Eingabe mit einer aktuellen Prozentzahl und einigen weiteren Werten verrechnet werden soll. Dies könnte etwa so aussehen:

{ 
   neuwert = $1 * 104 + 49 – ( 12 * 13 ) + ( 12 / 4 ) 
}

Und nehmen wir weiterhin an, jeder dieser Werte ist dynamisch und muss eventuell ständig angepasst werden. Da wäre es doch viel einfacher, wenn man den einzelnen Werten sinnvolle Variablennamen geben würde, was in den Code beispielsweise folgendermaßen implementiert werden könnte:

{ 
   Prozentsatz      = 104 
   Zuschuss         = 49 
   AnzahlBroetchen  = 12 
   Preis            = 13 
   AnzahlKartoffeln = 12 
   Personen         = 4 
 
   neuwert = $1 * Prozentsatz + Zuschuss – \ 
             ( AnzahlBroetchen * Preis ) + \ 
             ( AnzahlKartoffeln / Personen ); 
   print neuwert 
}

Doch welche Möglichkeiten stehen einem beim Rechnen mit awk eigentlich zur Verfügung? Zum einen sind dies diverse Operatoren und zum anderen Builtin Funktionen. Im Folgenden werden die Operatoren beschrieben, die Builtin-Funktionen werden später in einem eigenen Abschnitt erläutert.

+ – * / ^

Die grundlegenden Rechenoperationen gehen bereits aus dem vorherigen Listing hervor. Dabei handelt es sich um Addition (+), Subtraktion (-), Division (/) und Multiplikation (*). Das Dach-Zeichen (^) dient zur Angabe eines Exponenten, 2^8 ergäbe beispielsweise 256. Des Weiteren können Klammern verwendet werden.

Modulo

Das Modulo-Zeichen (%) führt eine Division durch und gibt den Rest dieser Division zurück. Die Anweisung 10%3 würde beispielsweise den Wert »1« ergeben.

+= -= /= *= ^= %=

awk stellt noch eine weitere Möglichkeit bereit, um die eben genannten Rechenoperationen durchzuführen. Diese hat allerdings nur den Sinn, den Code übersichtlicher und kürzer zu gestalten. Dabei können Rechenoperationen verkürzt werden, die abhängig vom Wert einer Variable derselben Variable einen neuen Wert zuweisen. So kann man den Code-Ausschnitt

var = var + 1

Listing 4.36    Lange Form

auch in der kurzen Form

var += 1

Listing 4.37    Kurze Form

schreiben. Dies funktioniert mit der Addition, Subtraktion, Division, Multiplikation, Modulo und dem Exponenten-Operator.

++ --

Doch awk kann noch mehr: nämlich in- und dekrementieren. Dabei wird der Zahlenwert einer Variable um den Wert 1 erhöht

(Inkrementierung) bzw. veringert (Dekrementierung). Die Inkrementierung wird durch das doppelte Plus-Zeichen, die Dekrementierung durch ein doppeltes Minus-Zeichen angewandt. Diese beiden Möglichkeiten der Wertmanipulation sind besonders in Schleifen von großer Bedeutung, zudem verkürzen sie den Code. Denn die Anweisung

Personen = Personen – 1;

Listing 4.38    Dekrementierungsbeispiel

kann durch die Dekrementierung verkürzt als

Personen--;

Listing 4.39    Dekrementierung

geschrieben werden, was umgekehrt natürlich auch für die Inkrementierung gilt.

Vorher oder nacher?

Bei der In- und Dekrementierung ist allerdings zwischen der Prä- und Post-Variante zu unterscheiden. Was bedeutet dies? Nun, um es nicht unnötig kompliziert zu formulieren: Wird der In- bzw. Dekrementierungsoperator vor eine Variable gestellt (das ist die Prä-Variante), wird ihr Wert vor der Durchführung einer Anweisung verändert; steht der Operator hingegen hinter einer Variable (das ist die Post-Variante), wird erst die Anweisung und dann die Wertveränderung durchgeführt. Das folgende Beispiel verdeutlicht dies:

BEGIN { 
    Personen = 2; 
 
    # gibt '2' aus: 
    print Personen; 
 
    # gibt '3' aus: 
    print ++Personen; 
 
    # gibt auch '3' aus: 
    print Personen--; 
 
    # gibt '1' aus: 
    print --Personen; 
}

Listing 4.40    Vorher oder nacher?

Interpretervariablen

awk kennt neben den Variablen, die Sie selbst erstellen können, auch sogenannte Builtin-Variablen. Diese Builtin-Variablen haben vordefinierte Namen, die alle großgeschrieben sind. Diese Variablen werden genauso verwendet wie die herkömmlichen Variablen.

Die von Ihrem Interpreter unterstützten Variablen finden Sie in der zugehören Manpage. Hier ist eine Liste der von jedem Interpreter unterstützten Interpretervariablen sowie deren Nutzen:

  • $0
  • In dieser Variable steht der komplette Inhalt einer eingelesenen Zeile.
  • $n
  • (Dabei steht n für eine Zahl größer null.) In $1 ... $n sind die einzelnen Spalteninhalte einer eingelesenen Zeile gespeichert. Dies zu wissen ist besonders wichtig, da Sie fast immer auf diese Variablen zurückgreifen müssen. Hier ein kleines Anwendungsbeispiel zur Verdeutlichung:
$ cat /tmp/myfile 
root      0    /root 
swendzel  1000 /home/swendzel 
$ awk '{ 
   print "Benutzername: " $1 "\tUser-ID: " $2 
         "\tHomedir: " $3 
\' /tmp/myfile} 
Benutzername: root      User-ID: 0     Homedir: \ 
  /root 
Benutzername: swendzel  User-ID: 1000  Homedir: \ 
  /home/swendzel

Listing 4.41    $n anwenden

  • ARGC
  • (argument count) ARGC enthält die Anzahl der awk übergebenen Argumente.
  • ARGV
  • (argument vector) ARGV ist ein Array und enthält die übergebenen Argumente selbst.
  • CONVFMT
  • (converting format) Diese Variable legt das Format für die Konvertierung von Zahlenwerten aus Variablen in Strings fest. Diese Variable ist standardmäßig auf den Wert »%.6g« gesetzt. Dies bedeutet, dass die ersten sechs Nachkommastellen hinter dem Komma in einen String übernommen werden. Dies lässt sich jedoch ändern, wodurch Ihnen eine äußerst genaue Ausgabe von Kommawerten zur Verfügung steht:
$ awk 'BEGIN { 
   print "1 / 3 = " 1/3 
   CONVFMT="%.15g"; 
   print "1 / 3 = " 1/3 
\'} 
1 / 3 = 0.333333 
1 / 3 = 0.333333333333333

Listing 4.42    Verändern von CONVFMT

  • ENVIRON
  • (environment) ENVIRON ist ein Array, in dem die Umgebungsvariablen der Shell enthalten sind. Die jeweiligen Index-Elemente des Arrays enthalten dabei den zugehörigen Wert der jeweiligen Variablen.
$ awk 'BEGIN { 
   print "Terminal:  " ENVIRON["TERM"]; 
   print "Mailqueue: " ENVIRON["MQUEUE"]; 
\'} 
Terminal:  xterm-color 
Mailqueue: /var/spool/mqueue

Listing 4.43    ENVIRON nutzen

  • FILENAME
  • In dieser Interpretervariable ist der Name der Datei gespeichert, die derzeit als Input dient. Sofern der Interpreter die Eingabedatei wechselt, wird auch der Wert von FILENAME neu gesetzt.
  • FNR
  • (file number records) In dieser Variable ist die aktuelle Anzahl der bisher verarbeiteten Eingabezeilen (= records) gespeichert.
$ awk '{ print FNR \' /etc/passwd} 
1 
2 
3 
4 
5 
...

Listing 4.44    FNR

  • FS
  • (field separator) Diese Variable ist von großer Bedeutung. Sie legt das Zeichen fest, das zur Trennung von einzelnen Spalten in der Eingabedatei dient. Normalerweise wird hierfür das Tab- und Newline-Zeichen verwendet. Diese Variable kann übrigens auch durch den Parameter -Fx beim awk-Aufruf gesetzt werden, wobei »x« das Separierungszeichen ist.
$ awk -F: '{ 
   print "User: " $1 "\tShell: " $7 
\' /etc/passwd} 
User: root      Shell: /usr/local/bin/zsh 
User: daemon    Shell: /sbin/nologin 
User: operator  Shell: /sbin/nologin 
...

Listing 4.45    Aufteilen der passwd

  • NF
  • (number of fields) In dieser Variable, die für jede Eingabezeile neu gesetzt wird, steht die aktuelle Anzahl von Spalten der jeweiligen Zeile. Dies ist besonders bei Eingabedateien von Nutzen, bei denen die Anzahl der Spalten von Zeile zu Zeile variiert. Ein üblicher Vertreter solcher Dateien ist /etc/services.
$ awk '{ print NF \' /etc/services} 
2 
4 
3 
2

Listing 4.46    NF in /etc/services

  • NR
  • (number of records) NR enthält die aktuelle Anzahl der bereits verarbeiteten Eingabezeilen.
$ cat /tmp/file1 
zeile1 
zeile2 
zeile3 
$ cat /tmp/file 
hier steht 
auch etwas 
drin ;-) 
$ awk '{ print NR \' /tmp/file[12]} 
1 
2 
3 
4 
5 
6

Listing 4.47    NR funktioniert auch über mehrere Dateien.

  • OFMT
  • (output format) Diese Variable hat eine ähnliche Wirkung wie CONVFMT. Hierbei wird jedoch nicht geregelt, wie Zahlen in Strings, sondern wie Zahlen in der direkten Ausgabe umgewandelt werden sollen. Ein Beispiel soll diesen Unterschied verdeutlichen:
$ awk 'BEGIN { 
   CONVFMT="%.14g" 
   X=1.1234567890123456 
   print X 
\'} 
1.12346 
$ awk 'BEGIN { 
   OFMT="%.14g" 
   X=1.1234567890123456 
   print X 
\'} 
1.1234567890123

Listing 4.48    CONVFMT im Vergleich zu OFMT

  • OFS
  • (output field separator) Diese Variable funktioniert analog zur Variable FS. Nur ist OFS nicht für die Separierung der Eingabespalten, sondern für die Separierung der Ausgabespalten zuständig.
  • ORS
  • (output record separator) Dieses Feld separiert die einzelnen Zeilen bei der Ausgabe. Standardmäßig ist dieser Variable das Newline-Zeichen (\n) zugewiesen.
  • RLENGTH
  • (regular expression length) RLENGTH gibt die Länge des regulären Ausdrucks an, der durch die Funktion match() gefunden wurde.
  • RS
  • (input record separator) Der Wert dieser Variable legt das Zeichen fest, das die einzelnen Eingabezeilen separiert. Normalerweise ist dies das Newline-Zeichen.
  • RSTART
  • (regular expression start) Diese Variable enthält den Wert der Anfangsposition des regulären Ausdrucks im String, der der match()-Funktion übergeben wurde.
  • SUBSEP
  • (subscript separator) Der Wert von SUBSEP legt das Separierungszeichen der einzelnen Array-Elemente fest. In der Regel muss der Inhalt dieser Variable nicht verändert werden.

Galileo Computing

4.4.6 Arrays  downtop

Neben normalen Variablen gibt es in awk auch noch sogenannte Arrays. Als einen Array kann man sich eine ganze Reihe von Variablen vorstellen. Am besten verdeutlicht man die Funktionsweise von Arrays anhand eines Beispiels.

Nehmen wir einmal an, es gibt drei Katzen. Allen diesen Katzen soll ein bestimmtes Alter zugewiesen werden. Dies kann mithilfe von Arrays in awk sehr simpel realisiert werden. Wir legen ein Array mit drei Elementen an. Dabei steht jedes Element für eine Katze. Diesen Elementen (also quasi den einzelnen Variablen des Arrays) weisen wir jeweils einen Wert – das Alter – zu. Unser Array hat dabei wie eine Variable einen einfachen Namen – MyArray – bekommen.

$ awk 'BEGIN { 
   MyArray[1]=3; 
   MyArray[2]=8; 
   MyArray[3]=3; 
 
   print "Felix ist " MyArray[1] " Jahre alt."; 
   print "Mauzi ist " MyArray[2] " Jahre alt."; 
   print "Schröder ist " MyArray[3] " Jahre alt."; 
}' 
Felix ist 3 Jahre alt. 
Mauzi ist 8 Jahre alt. 
Schröder ist 3 Jahre alt.

Listing 4.49    Arrays in awk

Assoziative Arrays

Da awk auch sogenannte assoziative Arrays unterstützt, muss nicht durch Index-Nummern auf die Elemente zugegriffen werden, nein, man kann die Elemente benennen. Wir benennen diese Elemente im Folgenden jeweils nach dem Namen der jeweiligen Katze. Dies erleichtert das Arbeiten mit Arrays sehr, da man sich nicht mit den Nummern von Array-Elementen herumschlagen muss.

$ awk 'BEGIN { 
   MyArray["Felix"]=3; 
   MyArray["Mauzi"]=8; 
   MyArray["Schröder"]=3; 
 
   print "Felix ist " MyArray["Felix"] " Jahre alt."; 
   print "Mauzi ist " MyArray["Mauzi"] " Jahre alt."; 
   print "Schröder ist " MyArray["Schröder"] \ 
         " Jahre alt."; 
\'} 
Felix ist 3 Jahre alt. 
Mauzi ist 8 Jahre alt. 
Schröder ist 3 Jahre alt.

Listing 4.50    Assoziative Arrays in awk

Bei awk-Arrays wird der Element-Index in eckigen Klammern angegeben. Dies kann eine Zahl oder ein Assoziativwert, etwa ein String, sein. Einen Assoziativwert muss man dabei in Anführungszeichen schreiben. Einem Array-Element weist man, wie auch einer Variable, durch ein Gleichheitszeichen einen Wert zu.

in

Gehen wir nun noch einen Schritt weiter. Dazu verwenden wir den Operator in. Diesen bauen wir in eine sogenannte for-Schleife ein. <Schleifen werden in Kürze behandelt. Hier reicht es allerdings erst einmal aus, dass Sie wissen, dass die for-Schleife so lange die nachstehende Anweisung ausführt, bis alle Array-Elemente durchgearbeitet worden sind.>

Diese Schleife geht in diesem Fall durch den in-Operator jedes Array-Element durch, das im Array MyArray existiert.

in weist dabei den Namen eines Elements der Variable i zu.

$ awk 'BEGIN { 
   MyArray["Felix"]=3; 
   MyArray["Mauzi"]=8; 
   MyArray["Schröder"]=3; 
 
   # 'i' selbst ist nun der Name 
   # MyArray[i] ist der zugehörige Wert 
   for (i in MyArray) 
      print i " ist " MyArray[i] " Jahre alt."; 
\'} 
Mauzi ist 8 Jahre alt. 
Schröder ist 3 Jahre alt. 
Felix ist 3 Jahre alt.

Listing 4.51    in-Operator und for-Schleife

delete

Wenn nun eine dieser Katzen verstirbt (was natürlich nicht schön wäre), müsste man sie aus dem Array entfernen.

Und natürlich können in awk auch irgendwie irgendwelche Elemente irgendeines Arrays entfernt werden. Dies geht sogar sehr einfach:

mit der delete-Anweisung.

$ awk 'BEGIN { 
   MyArray["Felix"]=3; 
   MyArray["Mauzi"]=8; 
   MyArray["Schröder"]=3; 
 
   delete MyArray["Mauzi"]; 
 
   for (i in MyArray) 
      print i " ist " MyArray[i] " Jahre alt."; 
\'} 
Schröder ist 3 Jahre alt. 
Felix ist 3 Jahre alt.

Listing 4.52    in-Operator und for-Schleife


Galileo Computing

4.4.7 Bedingte Anweisungen  downtop

Eigentlich wollte ich dieses Unterkapitel mit dem Satz »Eine bedingte Anweisung – darunter versteht man eine Anweisung, die bedingt ausgeführt wird.« einleiten. Ich hoffe dieser Satz hat wenigstens etwas Erheiterndes für Sie an sich – ich finde ihn toll.

Eine bedingte Anweisung besteht aus zwei Komponenten: der Bedingung selbst – sie ist eine Wertabfrage, etwa von einer Variable – und den Anweisungen bzw. Anweisungsblöcken. Davon gibt es oftmals sogar zwei: einen für den Fall, dass die Bedingung nicht erfüllt ist, und einen für den Fall, dass die Bedingung erfüllt ist.

Die primäre Möglichkeit, eine bedingte Anweisung zu verwenden, besteht in der if-Anweisung. Diese Anweisung wird in der folgenden Form in ein awk-Skript implementiert:

if ( Bedingung ) { 
    Anweisung1; 
    Anweisung2; 
}

Listing 4.53    Implementierung von if in einem awk-Skript

Dabei ist zu beachten, dass die geschweiften Klammern für den Anweisungsblock nur dann notwendig sind, wenn mehr als eine Anweisung im Falle einer erfüllten Bedingung ausgeführt werden soll. Soll beispielsweise nur eine print-Anweisung ausgeführt werden, wäre der folgende Code völlig korrekt:

if(1) 
   print $3

Listing 4.54    print

Aufbau einer Bedingung

Um nun eine if-Anweisung in das Skript einzubauen, müssen Sie zunächst erst einmal wissen, wie sich eine Bedingung eigentlich aufbaut. Dabei wird zwischen wahr (Wert=1) und falsch (Wert=0) unterschieden. Ist eine Bedingung erfüllt (also wahr), so wird/werden die Anweisung(en) ausgeführt, die auf die bedingte Anweisung folgen.

wahr=1; 
falsch=0; 
 
if(wahr) 
   print "Dieser Text wird ausgegeben." 
 
if(falsch) 
   print "Dieser Text wird nicht ausgegeben."

Listing 4.55    wahr und falsch

Vergleichs- operatoren

Damit eine Bedingung jedoch sinnvoll zum Einsatz kommen kann, muss die Möglichkeit bestehen, auch Variablen-Werte zu überprüfen.

Dies kann mit den Operatoren Größer-Gleich (>=), Kleiner-Gleich ( <=), Größer (>), Kleiner ( <), Gleich (==) und Ungleich (!=) bewerkstelligt werden.

wahr=1; 
falsch=0; 
 
if(falsch==0) 
   print "Diese Bedingung ist erfüllt!" 
 
if(falsch<wahr) 
   print "Diese Bedingung ist ebenfalls erfüllt!" 
 
if(falsch>=wahr) 
   print "Diese Bedingung ist NICHT erfüllt!" 
 
if(wahr!=0) 
   print "Aber diese ist erfüllt!"

Listing 4.56    Vergleichsoperatoren

!

Das Ausrufezeichen dient zur Negierung einer Bedingung. Möchten Sie beispielsweise erreichen, dass die Bedingung a==1 genau nicht erfüllt ist, kann diese auch durch

if(!a==1)

Listing 4.57    !a==1

ersetzt werden. Diese Methode eignet sich hervorragend, um zu prüfen, ob ein Wert »falsch« ist:

if(!a) { 
   ... 
}

Listing 4.58    !a

|| und &&

Nehmen wir einmal an, es sollen 100 Anweisungen im Falle einer erfüllten Bedingung ausgeführt werden – und diese 100 Anweisungen sollen auch in dem Fall ausgeführt werden, falls eine andere Bedingung erfüllt ist. Dann wäre es doch sinnvoll, diese Anweisungen nur ein einziges Mal in einen Anweisungsblock einzubauen. Dies ist in awk sehr einfach möglich. Man kann mehrere Bedingungen an eine bedingte Anweisung wie if übergeben. Diese kann man dann durch ein UND (&&) oder ein ODER (||) verknüpfen. Bei einer UND-Verknüpfung werden die Anweisungen nur ausgeführt, wenn alle damit verknüpften Anweisungen »wahr« sind. Bei einer ODER-Verknüpfung muss nur eine der miteinander verknüpften Bedingungen erfüllt werden, um die Ausführung des Anweisungsblocks zu veranlassen.

$ cat /tmp/myfile 
root      0    /root 
swendzel  1000 /home/swendzel 
$ awk '{ 
   # ODER: Nur eine Bedingung MUSS erfüllt seien. 
   # Eine ODER-Bedingung ist auch erfüllt, wenn 
   # BEIDE Teilbedingungen erfüllt sind. 
   if(0 || 1) 
      print $0 
\' /tmp/myfile} 
root      0    /root 
swendzel  1000 /home/swendzel 
$ awk '{ 
   # Hier ist die erste Bedingung "falsch", weshalb 
   # die Gesamtbedingung nicht erfüllt ist, da bei 
   # einer UND-Verknüpfung alle Teilbedingungen er- 
   # füllt sein müssen. 
   if(0 && 1) 
      print $0 
\' /tmp/myfile} 
$

Listing 4.59    && und ||

Klammern

In einer Bedingung lassen sich im Übrigen auch Hierarchien einbauen und Teilbedingungen separieren. Dies wird durch Einklammerung realisiert. Im folgenden Beispiel ist die Bedingung nur erfüllt, wenn die erste Bedingung (1) wahr ist und entweder wahr den Wert 1 oder falsch den Wert »1« oder »2« hat:

if( 1 && ( wahr==1 || (falsch==1 || falsch==2) ) ) 
   print $0

Listing 4.60    Klammerung in Bedingungen

else

Eine weitere einfache, aber wiederum äußerst nützliche Fähigkeit von awk (und so ziemlich jeder anderen Programmiersprache) ist das Ausführen von Anweisungen, die nur dann ausgeführt werden, wenn eine Bedingung nicht erfüllt ist. Dazu verwendet man das Schlüsselwort else in Verbindung mit einer if-Anweisung.

if(wahr==321) { 
   print $2 $1 
} else { 
   print "Fehler: wahr hat nicht den Wert 321!" 
}

Listing 4.61    if-else


Galileo Computing

4.4.8 Schleifen  downtop

Um es nicht unnötig kompliziert zu machen: Eine Schleife ist nichts weiter als eine bedingte Anweisung, bei der angegeben wird, wie oft der zugehörige Anweisungsblock ausgeführt werden soll. Die einfachste Form einer Schleife ist die while-Schleife. Mit dieser Schleife werden wir uns auch als Erstes beschäftigen.

while

Die while-Schleife hat äußerlich den gleichen Aufbau wie eine if-Anweisung. Ihr Anweisungsblock (oder eine Einzelanweisung) wird so oft ausgeführt, wie die gegebene Bedingung erfüllt ist.

while ( Bedingung ) { 
   Anweisung1; 
   Anweisung2; 
}

Listing 4.62    while-Schleife

In der Regel verwendet man Schleifen in Verbindung mit einer Variable. Sollen beispielsweise alle Zahlen von 1 bis 10.000 ausgegeben werden, so werden Sie kaum alle Zahlen selbst in den Code schreiben oder einzeln einer Variable zuweisen. Mit einer Schleife lässt sich dies mit wenigen Zeilen Skriptcode realisieren. Dazu verwenden wir einfach irgendeine Variable, die wir so lange hochzählen, bis ein Maximalwert erreicht ist.

$ awk 'BEGIN { 
  Kundennummer=1; 
 
  while(Kundennummer <= 10000) { 
     print "Kunde: " Kundennummer 
     Kundennummer++ 
  } 
\'} 
Kunde: 1 
Kunde: 2 
Kunde: 3 
...

Listing 4.63    Beispiel

Dieser Code bietet uns nun eine hervorragende neue Möglichkeit: Egal, wie oft wir die Ausführung einer Anweisung oder eines Anweisungsblocks hintereinander stattfinden lassen wollen, wir müssen sie trotzdem nur ein einziges Mal implementieren. Wenn der obige Code nun nicht 10.000-mal, sondern 9491849-mal oder 0-mal ausgeführt werden soll, müssen wir nur die Zahl 10000 durch eine andere ersetzen, und awk erledigt den Rest.

Schachtelung von Schleifen

Es ist möglich, mehrere Schleifen ineinanderzuschachteln. Dabei ist nichts weiter zu beachten, außer dass man den Code möglichst übersichtlich schreiben sollte.

Zur Vertiefung des bisher Gelernten soll nun ein Beispiel folgen: Es sollen alle Zahlen ausgegeben werden, die größer als 1 und kleiner als 30 sind und durch 3 teilbar sind.

Um diese Aufgabe zu lösen, bauen wir zunächst eine while-Schleife, in der alle Zahlen von 1 bis 29 durchgezählt werden. Für das Durchzählen inkrementieren wir bei jedem Schleifendurchlauf den Wert der Variable Zahl. Um zu prüfen, ob eine Zahl durch 3 teilbar ist, verwenden wir den bereits bekannten Modulo-Operator. Wenn diese Modulo-Operation den Wert 0 zurückgibt, gab es bei der Divison keinen Rest, Zahl ist also durch 3 teilbar. Ist dies der Fall, geben wir mit print den Wert der jeweiligen Zahl aus.

$ awk 'BEGIN { 
  Zahl=1; 
 
  while(Zahl<30) { 
     if(Zahl%3==0) 
        print Zahl 
     Zahl++ 
  } 
\'} 
3 
6 
9 
12 
15 
18 
21 
24 
27

Listing 4.64    Verschachtelung von Schleifen

Endlosschleifen

Eine Endlosschleife ist eine niemals endende Schleife. Diese macht oftmals nur bei größeren Softwareprojekten Sinn, etwa bei einem Hintergrundprozess. In awk sind Endlosschleifen eher selten anzutreffen. Bei solch einer Schleife wird einfach eine immer erfüllte Bedingung übergeben, also etwa »1« oder »27!=31«.

while(1) 
   print "Dieser Text wird unendlich oft ausgegeben."

Listing 4.65    Endlosschleife

break und continue

Um die Verwendung von Schleifen zu vereinfachen, kann man das Verhalten des Skripts bezüglich der Schleife innerhalb des Anweisungsblocks der Schleife beeinflussen. Dies mag sich kompliziert anhören, ist es aber nicht. Es gibt nämlich nur zwei Möglichkeiten, die Abarbeitung der Schleife zu beeinflussen:

  • break
  • break bricht die Schleife ab. Daraufhin werden die nächsten Anweisungen hinter der Schleife behandelt. Um die obige Endlosschleife beispielsweise nur einmal zu durchlaufen, könnte man hinter die print-Anweisung eine break-Anweisung setzen:
while(1) { 
   print "Dieser Text wird unendlich oft ausgegeben." 
   break 
}

Listing 4.66    break

  • continue
  • continue hingegen bricht nur die aktuelle Abarbeitung des Anweisungsblocks einer Schleife ab. Um die obige Endlosschleife beispielsweise so umzuprogrammieren, dass sie niemals die print-Anweisung aufruft, sondern schlicht nichts tut, außer vor sich hin zu laufen, müsste man nur eine continue-Anweisung vor die print-Anweisung setzen.
while(1) { 
   continue 
   print "Dieser Text wird unendlich oft ausgegeben." 
}

Listing 4.67    continue

do-while

Eine besondere Form der Schleife ist die do-while-Schleife. Dies ist eine while-Schleife, deren Anweisungen mindestens einmal ausgeführt werden. Die erste Ausführung des Anweisungsblocks ist also unbedingt, für alle weiteren muss die Bedingung jedoch erfüllt sein.

do { 
   Anweisung1; 
   Anweisung2; 
   ... 
} while ( Bedingung )

Listing 4.68    do-while

Würden wir das obige Beispiel also in eine do-while-Schleife übertragen, würde dies folgendermaßen aussehen:

$ awk 'BEGIN { 
  Zahl=1; 
 
  do { 
     if(Zahl%3==0) 
        print Zahl 
     Zahl++ 
  \ while(Zahl<30)} 
\'} 
3 
6 
9 
12 
15 
18 
21 
24 
27

for-Schleife

Gegenüber der (do-)while-Schleife hat die for-Schleife einen Vorteil: Ihr kann die Variableninitialisierung und die Anweisung zur Veränderung einer Variable direkt übergeben werden. Das bedeutet, dass die Zuweisung eines Wertes an eine Variable (Initialisierung) und beispielsweise die Dekrementierung einer Variable bei jedem Schleifendurchlauf nicht extra vor bzw. in den Anweisungsblock geschrieben werden müssen.

for ( Initialisierung; Bedingung; Anweisung ) { 
   Anweisung1; 
   Anweisung2; 
   ... 
}

Listing 4.69    for-Schleife

Nehmen wir einmal das obige Beispiel zur Ausgabe aller durch 3 teilbaren Zahlen zwischen 1 und 29 und bauen es in eine for-Schleife ein:

for(Zahl=1; Zahl<30; Zahl++) 
     if(Zahl%3==0) 
        print Zahl

Listing 4.70    Übersichtlicherer Code dank for

Wie zu sehen ist, konnte das Programm übersichtlich in 3 Zeilen untergebracht werden. <Man könnte es auch in eine einzige Zeile schreiben, dies würde jedoch die Lesbarkeit beeinträchtigen.>

Im Abschnitt zum Thema awk-Arrays wurde bereits der in-Operator in Verbindung mit einer Schleife besprochen, mit dem die einzelnen Array-Elemente durchlaufen werden können. Daher soll an dieser Stelle lediglich auf ihn verwiesen werden.


Galileo Computing

4.4.9 Funktionen in awk  downtop

Ein wichtiges Feature einer Programmiersprache sind die sogenannten Funktionen. Wir werden Funktionen nicht nur in awk, sondern auch in der Shellskriptprogrammierung verwenden. Eine Funktion enthält null, eine oder mehrere Anweisungen und führt diese jedes Mal aus, wenn diese Funktion aufgerufen wird. Dabei können der Funktion immer wieder ein oder mehrere Parameter übergeben werden, mit denen die Funktion dann arbeitet.

Dabei unterscheidet man in awk zwischen den Funktionen, die man selbst in den Skriptcode implementiert, und den sogenannten Builtin-Funktionen. Eine Builtin-Funktion kennen Sie sogar bereits: print. Die print-Funktion gibt Text aus, wobei ihr der Text bzw. die Variablennamen übergeben werden müssen. Diese übergebenen Texte und Variablen sind dabei die im vorherigen Absatz angesprochenen Parameter.

Zunächst werden wir, um Funktionen wirklich zu verstehen, eigene Funktionen erstellen. Anschließend werden die wichtigsten Builtin-Funktionen erklärt. <Eine Liste aller von Ihrem awk-Interpreter unterstützten Funktionen finden Sie in der zugehörigen Manpage.>

Eigene Funktionen implementieren

Eine eigene Funktion muss, bevor sie angewandt werden kann, zunächst implementiert werden. Dazu wird das Schlüsselwort function verwendet. Hinter diesem Schlüsselwort folgt der Name der Funktion und die in Klammern eingeschlossene Parameterliste, wobei diese auch leer sein kann.

function Funktionsname ( ParameterA, ... ParameterN ) 
{ 
   AnweisungA 
   AnweisungB 
   ... 
   AnweisungN 
}

Listing 4.71    Rohform einer Funktion

Soll beispielsweise eine Funktion implementiert werden, die den Mittelwert von 3 Zahlen berechnet und anschließend ausgibt, könnte dies folgendermaßen aussehen:

$ awk ' 
function mittel(a, b, c) { 
   mittelwert=(a+b+c)/3 
   print mittelwert 
} 
 
BEGIN { 
   mittel(1, 3, 3) 
   mittel(395, 3918, 49019849) 
\'} 
2.33333 
1.63414e+07

Wie Sie sehen, haben wir die Funktion nicht in einen BEGIN-, Haupt- oder END- Block implementiert. Eine Funktion ist in allen Anweisungsblöcken verfügbar.

return

Jedoch können awk-Funktionen noch etwas mehr: Sie können, wie man es aus der Programmiersprache C kennt, Werte zurückgeben. Diese Wertrückgabe wird durch das Schlüsselwort return bewerkstelligt. Diesen Wert kann man dann einer Variable zuweisen oder auch an eine andere Funktion als Parameter übergeben. Zudem kann man den Wert auch in eine Bedingung einbauen.

Nehmen wir das vorherige Beispiel noch einmal, und verwenden wir nun anstelle der print-Funktion innerhalb der Funktion eine return- Anweisung. Dies ermöglicht es uns, die Funktion viel dynamischer einzusetzen. Dieses Beispiel verwendet den Rückgabewert der Funktion für eine bedingte Anweisung und übergibt den Wert einer print-Funktion.

$ awk ' 
function mittel2(a, b, c) 
{ 
   return (a+b+c)/3; 
} 
 
BEGIN { 
   w1=55 
   w2=54 
   w3=53 
 
   while(mittel2(w1, w2, w3)>50) { 
      print mittel2(w1, w2, w3); 
      w1--; 
      w2-=2; 
      w3+=0.5; 
   } 
\'} 
54 
53.1667 
52.3333 
51.5 
50.6667

Listing 4.72    return

Builtin-Funktionen

Neben den Funktionen, die Sie selbst implementieren können, stellt awk Ihnen einige vorgegebene Builtin-Funktionen zur Verfügung. Diese gliedern sich in drei Bereiche: in die mathematischen Funktionen, in die Stringfunktionen und in die sonstigen Funktionen. Informationen zu Letzteren finden Sie in der Manpage Ihres awk-Interpreters.

Mathematische Funktionen

awk stellt die in Tabelle 4.2 dargestellten mathematischen Builtin-Funktionen zur Verfügung.


Tabelle 4.2    Mathematische Builtin-Funktionen
Funktion Erklärung
atan2(y, x) Gibt den Arcustangens von x zurück.
cos(x) Gibt den Kosinus-Wert von x zurück.
exp(x) Gibt den Wert von e^x zurück.
int(x) Gibt den Ganzzahlwert (Integerwert) von x zurück. Wäre x etwa 17.341, würde int(x) den Wert 17 zurückgeben.
log(x) Gibt den natürlichen Logarithmus von x zurück.
rand() Gibt eine zufällige Zahl zwischen 0...1 zurück. Mit der Funktion srand(x) kann ein neuer Startwert für rand() gesetzt werden.
sin(x) Gibt den Sinus von x zurück.
sqrt(x) Gibt die Wurzel von x zurück.

Stringfunktionen

Kommen wir nun zu den wohl wichtigsten Funktionen in awk: den Stringfunktionen. In awk stehen Ihnen dabei folgende Funktionstypen zur Verfügung: Funktionen, die

  • Strings in Strings suchen
  • reguläre Ausdrücke suchen und ersetzen
  • die Stringlänge zurückgeben
  • Strings aufspalten
  • Text (formatiert) ausgeben
  • Kleinbuchstaben eines Strings in Großbuchstaben umwandeln
  • Großbuchstaben eines Strings in Kleinbuchstaben umwandeln

Eine Liste aller Stringfunktionen samt einer Beschreibung dieser Funktionen finden Sie in der Manpage zu awk bzw. nawk oder gawk.


Galileo Computing

4.4.10 Ein paar Worte zum Schluss  toptop

Dies war eine kleine Einführung in awk. Doch awk kann noch mehr. Es gibt Funktionalitäten, die wir nicht beschrieben haben (etwa Schlüsselwörter wie next oder exit oder Bedingungen in regulären Ausdrücken beim awk-Aufruf). Doch sollte in diesem Rahmen das Wichtigste, was man über awk wissen sollte, um es täglich zu verwenden, vermittelt worden sein. Alles Weitere finden Sie in der jeweiligen Manpage Ihres Interpreters.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  
  Zum Katalog
Zum Katalog: Linux






 Linux
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: Einstieg in Linux






 Einstieg in Linux


Zum Katalog: Debian GNU/Linux






 Debian GNU/Linux


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-
 Programmierung


Zum Katalog: Praxisbuch Netzwerk-Sicherheit






 Praxisbuch
 Netzwerk-Sicherheit


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de