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

Inhaltsverzeichnis
Vorwort
1 Einleitung
2 Die Installation
3 Erste Schritte
4 Linux als Workstation für Einsteiger
5 Der Kernel
6 Die Grundlagen aus Anwendersicht
7 Die Shell
8 Reguläre Ausdrücke
9 Konsolentools
10 Die Editoren
11 Shellskriptprogrammierung mit der bash
12 Die C-Shell
13 Benutzerverwaltung
14 Grundlegende Verwaltungsaufgaben
15 Netzwerkgrundlagen
16 Anwendersoftware für das Netzwerk
17 Netzwerkdienste
18 Mailserver unter Linux
19 LAMP
20 DNS-Server
21 Secure Shell
22 Die grafische Oberfläche
23 Window-Manager und Desktops
24 X11-Programme
25 Multimedia und Spiele
26 Prozesse und IPC
27 Bootstrap und Shutdown
28 Dateisysteme
29 Virtualisierung und Emulatoren
30 Softwareentwicklung
31 Crashkurs in C und Perl
32 Einführung in die Sicherheit
33 Netzwerksicherheit überwachen
A Lösungen zu den einzelnen Aufgaben
B Kommandoreferenz
C X11-InputDevices
D MBR
E Die Buch-DVDs
F Glossar
G Literatur
Stichwort

Download:
- ZIP, ca. 15,7 MB
Buch bestellen
Ihre Meinung?

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

Linux
geb., mit 2 DVDs
1302 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1704-0
Pfeil 8 Reguläre Ausdrücke
  Pfeil 8.1 Aufbau von regulären Ausdrücken
  Pfeil 8.2 Der Stream-Editor sed
    Pfeil 8.2.1 Was bringt mir sed?
    Pfeil 8.2.2 Erste Schritte mit sed
    Pfeil 8.2.3 sed-Befehle
    Pfeil 8.2.4 Nach Zeilen filtern
    Pfeil 8.2.5 Wiederholungen in regulären Ausdrücken
  Pfeil 8.3 grep
    Pfeil 8.3.1 grep -E und egrep
    Pfeil 8.3.2 Geschwindigkeitsvergleich
    Pfeil 8.3.3 Exkurs: PDF-Files mit grep durchsuchen
  Pfeil 8.4 awk
    Pfeil 8.4.1 Nutzen und Interpreter
    Pfeil 8.4.2 Der Aufruf des Interpreters awk
    Pfeil 8.4.3 Erste Gehversuche
    Pfeil 8.4.4 Der Anweisungsblock
    Pfeil 8.4.5 Variablen
    Pfeil 8.4.6 Arrays
    Pfeil 8.4.7 Bedingte Anweisungen
    Pfeil 8.4.8 Schleifen
    Pfeil 8.4.9 Funktionen in awk
    Pfeil 8.4.10 Ein paar Worte zum Schluss
  Pfeil 8.5 Zusammenfassung
  Pfeil 8.6 Aufgaben


Galileo Computing - Zum Seitenanfang

8.4 awk  Zur nächsten ÜberschriftZur vorigen Überschrift

Bei awk handelt es sich um eine Programmiersprache, für deren Anwendung sich die Autoren, die sie beschreiben wollen, immer interessante Anwendungsbeispiele ausdenken. In [Herold03A] werden beispielsweise Lebensmitteltabellen und Bundesliga-Ergebnisse ausgewertet, in [VoReJo97A] begnügt man sich mit einer Nummernliste und in unserem Buch »Einstieg in Linux« ([WendPloe08A]) 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 ebenfalls ein Buch ([AhWeKe88A]) über awk. Doch mittlerweile gibt es neuere Bücher zu dieser Sprache, etwa [Herold03A] oder eben das vorliegende Buch. Nach der gründlichen Lektüre dieses Unterkapitels werden Sie für den täglichen Umgang mit awk alles Wichtige wissen. Ein vollständiges Buch zur awk-Programmiersprache ersetzt es jedoch nicht.

Abbildung Blowfish

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 - Zum Seitenanfang

8.4.1 Nutzen und Interpreter  Zur nächsten ÜberschriftZur vorigen Überschrift

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 einen Interpreter, der die Anweisungen im Code einliest und ausführt. Dieser Interpreter heißt wie die Sprache selbst 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 Ausnahme stellt Common-Lisp dar, das auch ohne Bytecode-Übersetzung relativ schnell läuft.]


Galileo Computing - Zum Seitenanfang

8.4.2 Der Aufruf des Interpreters awk  Zur nächsten ÜberschriftZur vorigen Überschrift

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

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

Listing 8.28  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

Der Parameter 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 - Zum Seitenanfang

8.4.3 Erste Gehversuche  Zur nächsten ÜberschriftZur vorigen Überschrift

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 Zeile ausgegeben wird, 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 8.29  awk gibt eine Datei aus.

Möchten wir nun alle Zeilen herausfiltern, in denen ein »n« enthalten ist, so gelingt 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 8.30  awk filtert nach »n«.


Wie Sie sehen, kann awk (wie bereits sed) 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 (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 8.31  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 8.32  Regulärer Ausdruck und eine Anweisung


Galileo Computing - Zum Seitenanfang

8.4.4 Der Anweisungsblock  Zur nächsten ÜberschriftZur vorigen Überschrift

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 8.33  Anweisungen

Eine weitere Möglichkeit

Neben der Separierung durch ein Semikolon ist es auch möglich, Anweisungen durch geschweifte Klammern zu separieren. Dies wird bei bedingten Anweisungen verwendet, funktioniert aber auch außerhalb solcher Anweisungen. 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 8.34  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 einmalig ausgeführt, was allerdings nicht immer ausreicht.

Hingegen wird der Hauptanweisungsblock 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 ergibt sich aus einer 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 8.35  Der Aufbau eines awk-Skripts


Nehmen wir einmal an, eine Datei soll mit drei Zeilen verarbeitet werden, und vor und nach der Verarbeitung soll 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 gesetzt haben.


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

Listing 8.36  END und BEGIN angewandt

Wenn die Datei /tmp/Inputdatei den Inhalt

zeile1
zeile2
zeile3

Listing 8.37  /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 8.38  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 8.39  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 8.40  Anwenden des Backslashs


Galileo Computing - Zum Seitenanfang

8.4.5 Variablen  Zur nächsten ÜberschriftZur vorigen Überschrift

Eine Variable ist ein transparentes Mittel, um auf eine Adresse im Speicherbereich zuzugreifen. Dabei gibt man einer Variablen 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 handeln – oder auch um eine Kombination aus Zahlen, großen und kleinen Buchstaben sowie Sonderzeichen, etwa einem $. In awk wird dabei zwischen einer Variablen, die nur aus Zahlen besteht (mit denen sich rechnen beziehungsweise vergleichen lässt), und einem sogenannten String unterschieden, dem man neben Zahlen eben auch Buchstaben und Sonderzeichen zuweisen kann.

Der Name einer Variablen sollte dabei nur aus Ziffern, Buchstaben und Unterstrichen bestehen. Dabei ist jedoch zu beachten, dass das erste Zeichen im Namen keine Ziffer sein darf. 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 zu diesem Zeitpunkt noch nicht 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 Variablen zugewiesen wird, müssen Sie sich übrigens nicht kümmern, diese Zuweisung erfolgt nämlich völlig transparent.

Die Initialisierung einer Variablen, 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 8.41  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 8.42  Erneute Wertzuweisung durch =

Zahlenwerte manipulieren

Um zunächst einmal grundlegende Rechenoperationen durchführen zu können, muss man Zahlenwerte manipulieren 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 )
}

Listing 8.43  Eine Beispielberechnung

Nehmen wir des Weiteren 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
}

Listing 8.44  Berechnung mit Variablen

Doch welche Möglichkeiten stehen Ihnen 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, 28 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 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 Variablen derselben Variable einen neuen Wert zuweisen. So kann man den Code-Ausschnitt

var = var + 1

Listing 8.45  Lange Form

auch in der kurzen Form

var += 1

Listing 8.46  Kurze Form

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

++ --

Doch awk kann noch mehr: nämlich in- und dekrementieren. Dabei wird der Zahlenwert einer Variablen 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 8.47  Dekrementierungsbeispiel

kann durch die Dekrementierung verkürzt als

Personen--;

Listing 8.48  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), so wird ihr Wert vor der Durchführung einer Anweisung verändert. Steht der Operator hingegen hinter einer Variablen (das ist die Post-Variante), so 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 8.49  Vorher oder nacher?

Interpreter-Variablen

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örigen Manpage. Es folgt eine Liste der von jedem Interpreter unterstützten Interpreter-Variablen und ihrer Funktion:

  • $0
    In dieser Variablen steht der komplette Inhalt einer eingelesenen Zeile.
  • $n (Hier 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 8.50  $n anwenden

  • ARGC (argument count)
    ARGC
    enthält die Anzahl der an 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 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 8.51  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 8.52  ENVIRON nutzen

  • FILENAME
    In dieser Interpreter-Variablen 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 Variablen ist die aktuelle Anzahl der bisher verarbeiteten Eingabezeilen (= records) gespeichert.
$ awk '{ print FNR \' /etc/passwd}
1
2
3
4
5
...

Listing 8.53  Die Variable FNR

  • FS (field separator)
    Diese Variable ist von großer Bedeutung, denn 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 8.54  Aufteilen der Datei passwd

  • NF (number of fields)
    In dieser Variablen, 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 8.55  NF in der Datei /etc/services

  • NR (number of records)
    Die Variable NR enthält die aktuelle Anzahl der bereits verarbeiteten Eingabe- zeilen.
$ 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 8.56  Die Variable NR funktioniert auch über mehrere Dateien.

  • OFMT (output format)
    Diese Variable hat eine ähnliche Wirkung wie CONVFMT. Hierbei wird jedoch nicht die Umwandlung von Zahlen in Strings geregelt, 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 8.57  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 Variablen 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 Variablen 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 Variablen nicht verändert werden.

Galileo Computing - Zum Seitenanfang

8.4.6 Arrays  Zur nächsten ÜberschriftZur vorigen Überschrift

Neben normalen Variablen gibt es in awk auch noch die 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 drei 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 den einzelnen Variablen des Arrays) weisen wir jeweils einen Wert – das Alter der Katze – zu. Unser Array hat dabei wie eine Variable einen einfachen Namen: MyArray.


$ awk 'BEGIN
{
# Ein Array mit drei Elementen anlegen
MyArray[1]=3;
MyArray[2]=8;
MyArray[3]=3;
  # Die Array-Elemente ausgeben
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 8.58  Arrays in awk

Assoziative Arrays

Da awk auch sogenannte assoziative Arrays unterstützt, muss nicht durch Indexnummern auf die Elemente zugegriffen werden – man kann die Elemente vielmehr benennen. Wir bezeichnen diese Elemente im Folgenden jeweils mit 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 8.59  Assoziative Arrays in awk


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


in

Gehen wir nun noch einen Schritt weiter, indem wir den Operator in verwenden. Diesen bauen wir in eine sogenannte for-Schleife ein. [Schleifen werden in Abschnitt 8.4.8 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 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 Variablen 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 8.60  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 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 8.61  in-Operator und for-Schleife


Galileo Computing - Zum Seitenanfang

8.4.7 Bedingte Anweisungen  Zur nächsten ÜberschriftZur vorigen Überschrift

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 – ich finde ihn gut. Eine bedingte Anweisung besteht aus zwei Komponenten: der Bedingung selbst – sie ist eine Wertabfrage, etwa von einer Variablen – und den Anweisungen oder 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 8.62  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 vollkommen korrekt:

if(1)
print $3

Listing 8.63  print

Aufbau einer Bedingung

Um nun eine if-Anweisung in das Skript einzubauen, müssen Sie zunächst 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 folgt/folgen.

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

Listing 8.64  wahr und falsch

Vergleichsoperatoren

Damit eine Bedingung jedoch sinnvoll zum Einsatz kommen kann, muss die Möglichkeit bestehen, auch Variablenwerte 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 8.65  Vergleichsoperatoren

!

Das Ausrufezeichen dient zur Negierung einer Bedingung. Möchten Sie beispielsweise erreichen, dass die Bedingung a==1 gerade nicht erfüllt ist, so müsste die Schreibweise

if(!a==1)

Listing 8.66  !a==1

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

if(!a) {
...
}

Listing 8.67  !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 dann 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 8.68  && 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 den Wert »2« hat:

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

Listing 8.69  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 Formulieren 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 8.70  Eine if-else-Anweisung


Galileo Computing - Zum Seitenanfang

8.4.8 Schleifen  Zur nächsten ÜberschriftZur vorigen Überschrift

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 8.71  Die while-Schleife


In der Regel verwendet man Schleifen in Verbindung mit einer Variablen. 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 Variablen zuweisen wollen. Mit einer Schleife lassen sich diese Aktionen 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 8.72  Ein Beispiel für eine while-Schleife


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 9,491,849-mal oder keinmal 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 ineinander zu schachteln. Dabei ist nichts weiter zu beachten, als dass man den Code möglichst übersichtlich schreiben sollte.


Zur Vertiefung des bisher Gelernten folgt ein Beispiel: Es sollen alle Zahlen ausgegeben werden, die größer als eins und kleiner als 30 sind und die durch drei 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 Variablen 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 8.73  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 8.74  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
    Die Anweisung 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 8.75  Die Anweisung break

  • continue
    Die Anweisung continue hingegen bricht nur die aktuelle Abarbeitung des Anweisungsblocks einer Schleife ab. Um die obige Endlosschleife 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 8.76  Die Anweisung 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 findet also unbedingt statt, für alle weiteren muss die Bedingung jedoch erfüllt sein.

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

Listing 8.77  do-while

Würden wir das obige Beispiel also in eine do-while-Schleife übertragen, so sähe dies folgendermaßen aus:

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

Listing 8.78  do-while in der Praxis

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 Variablen direkt übergeben werden. Das bedeutet, dass die Zuweisung eines Wertes an eine Variable (Initialisierung) und beispielsweise die Dekrementierung einer Variablen bei jedem Schleifendurchlauf nicht extra vor oder in den Anweisungsblock geschrieben werden müssen.

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

Listing 8.79  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 8.80  Übersichtlicherer Code dank for

Wie zu sehen ist, konnte das Programm übersichtlich in drei Zeilen untergebracht werden. [Man könnte es theoretisch 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 noch einmal auf diesen Operator verwiesen werden.



Galileo Computing - Zum Seitenanfang

8.4.9 Funktionen in awk  Zur nächsten ÜberschriftZur vorigen Überschrift

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 keine, 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 sie 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: Die print-Funktion gibt Text aus, wobei ihr der Text bzw. die Variablennamen übergeben werden müssen. Diese übergebenen Texte und Variablen sind die im vorherigen Absatz angesprochenen Parameter.

Um Funktionen wirklich zu verstehen, wollen wir zunächst 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. Zur Implementierung wird das Schlüsselwort function verwendet. Hinter diesem Schlüsselwort folgt der Name der Funktion und die in Klammern eingeschlossene Parameterliste, die auch leer sein kann.

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

Listing 8.81  Rohform einer Funktion

Soll beispielsweise eine Funktion implementiert werden, die den Mittelwert von drei 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

Listing 8.82  Berechnung des Mittelwerts

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 Variablen zuweisen oder auch an eine andere Funktion als Parameter übergeben. Zudem kann man den Wert auch in eine Bedingung einbauen.

Nehmen wir uns das vorherige Beispiel noch einmal vor, 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 8.83  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: die mathematischen Funktionen, die Stringfunktionen und die sonstigen Funktionen. Informationen zu den sonstigen Funktionen finden Sie in der Manpage Ihres awk-Interpreters.

Mathematische Funktionen

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


Tabelle 8.2  Mathematische Builtin-Funktionen

Funktion Erklärung

atan2(y, x)

Gibt den Arcus-Tangens von y/x zurück.

cos(x)

Gibt den Kosinus-Wert von x zurück.

exp(x)

Gibt den Wert von ex zurück.

int(x)

Gibt den Ganzzahlwert (Integerwert) von x zurück. Wäre x beispielsweise 17.341, so 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« und »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.


String-Funktionen

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 String-Lä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 String-Funktionen samt einer Beschreibung dieser Funktionen finden Sie in der Manpage zu awk beziehungsweise zu nawk oder gawk.


Galileo Computing - Zum Seitenanfang

8.4.10 Ein paar Worte zum Schluss  topZur vorigen Überschrift

Dies war eine kleine Einführung in die Skriptsprache 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). Das Wichtigste, was Sie über awk wissen sollten, um es täglich zu verwenden, haben Sie aber in diesem Kapitel schon gelernt. 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, Ausgabe 2011






Linux, Ausgabe 2011
Jetzt bestellen


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

 Buchempfehlungen
Zum Katalog: Linux-Server






 Linux-Server


Zum Katalog: Linux Hochverfügbarkeit






 Linux Hoch-
 verfügbarkeit


Zum Katalog: LPIC-1






 LPIC-1


Zum Katalog: Debian GNU/Linux






 Debian GNU/Linux


Zum Katalog: openSUSE 11.2






 openSUSE 11.2


Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2011
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