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

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

Spacer
Linux von Johannes Plötner, Steffen Wendzel
Das umfassende Handbuch
Buch: Linux

Linux
Rheinwerk Computing
1282 S., 5., aktualisierte Auflage 2012, geb., mit 2 DVDs
49,90 Euro, ISBN 978-3-8362-1822-1
Pfeil 8 Reguläre Ausdrücke
Pfeil 8.1 Der Aufbau regulärer Ausdrücke
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.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 Variable
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

Rheinwerk Computing - Zum Seitenanfang

8.4 awkZur nächsten Ü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) programmiert haben: Alfred V. Aho, Peter J. Weinberger und Brian W. Kernighan. Diese drei haben übrigens ebenfalls ein Buch ([AhWeKe88A]) über awk geschrieben. Doch mittlerweile gibt es neuere Bücher zu dieser Sprache, etwa [Herold03A] oder eben das vorliegende. 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.

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.


Rheinwerk Computing - Zum Seitenanfang

8.4.1 Nutzen und InterpreterZur nächsten ÜberschriftZur vorigen Überschrift

Die Programmiersprache awk dient, ähnlich wie sed zum Auseinandernehmen und Verarbeiten von Streams. Jedoch bietet sie 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 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 stark an die Programmiersprache C angelehnt, zudem ist sie ä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. [Fn. Allerdings sind interpretierte Sprachen dafür 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.]


Rheinwerk Computing - Zum Seitenanfang

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

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

Listing 8.27 awk-Aufrufschema

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

Ausdruck

Der erste Parameter ist ein regulärer Ausdruck. Dieser 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 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 die 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. Auch ihn müssen Sie 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.

Rheinwerk Computing - Zum Seitenanfang

8.4.3 Erste GehversucheZur 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 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:

Listing 8.28 awk gibt eine Datei aus.

$ 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:
...

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

Listing 8.29 awk filtert nach »n«.

$ 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:

Wie Sie sehen, kann awk (wie bereits sed) auf diese Weise die (Grund-)Aufgaben des Programms grep übernehmen.

Anweisungen

Lässt man den regulären Ausdruck weg und verwendet an seiner Stelle eine einfache Anweisung, kann man ebenfalls den Dateiinhalt ausgeben. Da 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.

Listing 8.30 awk mit Anweisungen

$ 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:
...

Nun beides zusammen

Nun sind wir so weit, dass wir beide Parameter, nämlich regulären Ausdruck und 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.

Listing 8.31 Regulärer Ausdruck und eine Anweisung

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

Rheinwerk Computing - Zum Seitenanfang

8.4.4 Der AnweisungsblockZur nächsten ÜberschriftZur vorigen Überschrift

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

Anweisungen separieren

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

Listing 8.32 Anweisungen

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

Eine weitere Möglichkeit

Neben der Trennung 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 von ihnen. Allerdings ist die Verwendung von geschweiften Klammern außerhalb von bedingten Anweisungen schlechter (weil unübersichtlicher) Programmierstil und wird daher nur der Vollständigkeit halber erwähnt.

Listing 8.33 Anweisungen mit {}

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

BEGIN und END

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

Hingegen wird der Anweisungsblock 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, für den Fall, dass 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:

Listing 8.34 Der Aufbau eines awk-Skripts

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

{
Anweisung1;
...
AnweisungN;
}

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

[zB]Nehmen wir einmal an, eine Datei mit drei Zeilen solle verarbeitet, und vor und nach der Verarbeitung solle 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.

Listing 8.35 END und BEGIN angewandt

$ awk ' BEGIN {
print "Es folgt der Dateiinhalt:"
}

{

print
}

END {

print "Das Ende der Datei ist erreicht."
print "Tschüss!"
\
' /tmp/Inputdatei}

Wenn die Datei /tmp/Inputdatei den Inhalt

Listing 8.36 /tmp/Inputdatei

zeile1
zeile2
zeile3

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

Listing 8.37 Die Ausgabe des awk-Aufrufs

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

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 Skripte ü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 Code mit Kommentaren versehen hat.

Listing 8.38 Ein simpler Beispielkommentar

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

Lange Zeilen

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

Listing 8.39 Anwenden des Backslashs

      # 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:"

Rheinwerk Computing - Zum Seitenanfang

8.4.5 VariableZur nächsten ÜberschriftZur vorigen Überschrift

Eine Variable ist ein transparentes Mittel, um auf einen Speicherbereich zuzugreifen. Dabei gibt man einer Variablen einen Namen. Über diesen kann man immer wieder auf den Speicherbereich zugreifen und die darin enthaltenen Daten manipulieren. Dabei 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 Variable 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.

Variable deklarieren und initialisieren

Eine Variable wird deklariert, indem man ihren Namen im awk-Skript verwendet. 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:

Listing 8.40 Variable deklarieren und initialisieren

BEGIN {
# Der Name einer Katze sei Felix,
KatzenName="Felix"

# ihr Wohnort sei Ettenbeuren.
Wohnort="12345 Ettenbeuren"

# Die Hausnummer hingegen sei 123.
Hausnummer=123
}

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.

Listing 8.41 Erneute Wertzuweisung durch =

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

Zahlenwerte manipulieren

Um zunächst einmal grundlegende Rechenoperationen durchführen zu können, muss man Zahlenwerte manipulieren können. Zum Rechnen sind nicht unbedingt Variable 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:

Listing 8.42 Eine Beispielberechnung

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

Nehmen wir des Weiteren an, jeder dieser Werte sei dynamisch und müsse eventuell ständig angepasst werden. Dan wäre es doch viel einfacher, wenn man den einzelnen Werten sinnvolle Variablennamen gäbe, was im Code beispielsweise folgendermaßen implementiert werden könnte:

Listing 8.43 Berechnung mit Variablen

{
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 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; 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 Zirkumflex (^) 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 ergäbe beispielsweise den Wert »1«.

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

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 Variablen einen neuen Wert zuweisen. So kann man den Code-Ausschnitt

Listing 8.44 Lange Form

var = var + 1

auch in der kurzen Form

Listing 8.45 Kurze Form

var += 1

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 (Inkrement) bzw. veringert (Dekrement). Das Inkrement wird durch das doppelte Plus-Zeichen, das Dekrement 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

Listing 8.46 Dekrementierungsbeispiel

Personen = Personen – 1;

kann durch die Dekrementierung verkürzt als

Listing 8.47 Dekrementierung

Personen--;

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

Vorher oder nacher?

Bei In- und Dekrement 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:

Listing 8.48 Vorher oder nacher?

BEGIN {
Personen = 2;

# gibt '2' aus:
print Personen;


# gibt '3' aus:
print ++Personen;

# gibt auch '3' aus:
print Personen--;

# gibt '1' aus:
print --Personen;
}

Interpreter-Variablen

awk kennt neben den Variablen, die Sie selbst erstellen können, auch sogenannte Builtin-Variablen. Diese haben vordefinierte Namen, die alle in Großbuchstaben geschrieben sind. Sie werden genauso verwendet wie herkömmliche Variable. 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:

Listing 8.49 $n anwenden

$ 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
  • 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 und hat ist standardmäßig den Wert »%.6g« gesetzt, was 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:

Listing 8.50 Verändern von CONVFMT

$ awk 'BEGIN {
print "1 / 3 = " 1/3
CONVFMT="%.15g";
print "1 / 3 = " 1/3
\
'}
1 / 3 = 0.333333
1 / 3 = 0.333333333333333
  • ENVIRON (environment)
    ENVIRON ist ein Array, in dem die Umgebungsvariablen der Shell enthalten sind. Die Index-Elemente enthalten dabei den Wert der jeweiligen Variablen.

Listing 8.51 ENVIRON nutzen

$ awk 'BEGIN {
print "Terminal: " ENVIRON["TERM"];
print "Mailqueue: " ENVIRON["MQUEUE"];
\
'}
Terminal: xterm-color
Mailqueue: /var/spool/mqueue
  • 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.

Listing 8.52 Die Variable FNR

$ awk '{ print FNR \' /etc/passwd}
1
2
3
4
5
...
  • 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 werden hierfür die Tab- und Newline-Zeichen verwendet. Diese Variable kann übrigens auch durch den Parameter -Fx beim awk-Aufruf gesetzt werden, wobei »x« das Separierungszeichen ist.

Listing 8.53 Aufteilen der Datei passwd

$ 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
...
  • 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.

Listing 8.54 NF in der Datei /etc/services

$ awk '{ print NF \' /etc/services}
2
4
3
2
  • NR (number of records)
    Die Variable NR enthält die aktuelle Anzahl der bereits verarbeiteten Eingabe- zeilen.

Listing 8.55 Die Variable NR funktioniert auch über mehrere Dateien.

$ 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
  • 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:

Listing 8.56 CONVFMT im Vergleich zu OFMT

$ awk 'BEGIN {
CONVFMT="%.14g"
X=1.1234567890123456
print X
\
'}
1.12346
$ awk 'BEGIN {
OFMT="%.14g"
X=1.1234567890123456
print X
\
'}
1.1234567890123
  • 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)
    Der Wert dieser Variablen 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.

Rheinwerk Computing - Zum Seitenanfang

8.4.6 ArraysZur nächsten ÜberschriftZur vorigen Überschrift

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

[zB]Nehmen wir einmal an, es gebe drei Katzen. Allen dreien 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 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.

Listing 8.57 Arrays in awk

$ 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.

Assoziative Arrays

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

Listing 8.58 Assoziative Arrays in awk

$ 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.

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. [Fn. Schleifen werden in Abschnitt 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.

Listing 8.59 in-Operator und for-Schleife

$ 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.

delete

Wenn nun eine dieser Katzen verstürbe (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.

Listing 8.60 in-Operator und for-Schleife

$ 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.

Rheinwerk Computing - Zum Seitenanfang

8.4.7 Bedingte AnweisungenZur 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 Art von bedingten Anweisungen ist die if-Anweisung. Sie wird in der folgenden Form in einem awk-Skript genutzt:

Listing 8.61 Verwendung von if in einem awk-Skript

if ( Bedingung ) {
Anweisung1;
Anweisung2;
}

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:

Listing 8.62 print

if(1)
print $3
Aufbau einer Bedingung

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

Listing 8.63 wahr und falsch

wahr=1;
falsch=0;

if(wahr)
print "Dieser Text wird ausgegeben."

if(falsch)
print "Dieser Text wird nicht ausgegeben."

Vergleichsoperatoren

Damit eine Bedingung jedoch sinnvoll zum Einsatz kommen kann, muss die Möglichkeit bestehen, auch Werte zu vergleichen. Dies kann mit den Operatoren Größer-Gleich (>=), Kleiner-Gleich (<=), Größer (>), Kleiner (<), Gleich (==) und Ungleich (!=) bewerkstelligt werden.

Listing 8.64 Vergleichsoperatoren

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!"

!

Das Ausrufezeichen dient zum Negieren einer Bedingung. Möchten Sie beispielsweise darauf prüfen, dass die Bedingung a==1 gerade nicht erfüllt ist, so müsste die Schreibweise

Listing 8.65 !a==1

if(!a==1)

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

Listing 8.66 !a

if(!a) {
...
}

|| und &&

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

Listing 8.67 && und ||

$ cat /tmp/myfile
root 0 /root
swendzel 1000 /home/swendzel
$ awk '{
# ODER: Mindestens eine Teilbedingung
# MUSS erfüllt sein.
if(0 || 1)
print $0
\
' /tmp/myfile}
root 0 /root
swendzel 1000 /home/swendzel
$ awk '{
# Hier ist die erste Bedingung falsch, weshalb die Gesamt-
# bedingung nicht erfüllt ist, da bei einer UND-Verknüpfung
# alle Teilbedingungen erfüllt sein müssen.
if(0 && 1)
print $0
\
' /tmp/myfile}
$

Klammern

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

Listing 8.68 Klammerung in Bedingungen

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

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.

Listing 8.69 Eine if-else-Anweisung

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

Rheinwerk Computing - Zum Seitenanfang

8.4.8 SchleifenZur 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 ihr 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 lange ausgeführt, wie die gegebene Bedingung erfüllt ist.

Listing 8.70 Die while-Schleife

while ( Bedingung ) {
Anweisung1;
Anweisung2;
}

[zB]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.

Listing 8.71 Ein Beispiel für eine while-Schleife

$ awk 'BEGIN {
Kundennummer=1;

while(Kundennummer <= 10000) {

print "Kunde: " Kundennummer
Kundennummer++
}
\
'}
Kunde: 1
Kunde: 2
Kunde: 3
...

[»]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.

[zB]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.

Listing 8.72 Verschachtelung von Schleifen

$ awk 'BEGIN {
Zahl=1;

while(Zahl<30) {

if(Zahl%3==0)
print Zahl
Zahl++
}
\
'}
3
6
9
12
15
18
21
24
27

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«.

Listing 8.73 Endlosschleife

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

break und continue

Um die Verwendung von Schleifen zu vereinfachen, kann man das Verhalten des Skripts bezüglich der Schleife innerhalb ihres Anweisungsblocks 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 ihr behandelt. Um die obige Endlosschleife beispielsweise nur einmal zu durchlaufen, könnte man hinter die print-Anweisung eine break-Anweisung setzen:

Listing 8.74 Die Anweisung break

while(1) {
print "Dieser Text wird unendlich oft ausgegeben."
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.

Listing 8.75 Die Anweisung continue

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

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.

Listing 8.76 do-while

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

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

Listing 8.77 do-while in der Praxis

$ 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 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.

Listing 8.78 for-Schleife

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

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:

Listing 8.79 Übersichtlicherer Code dank for

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

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

[»]In Abschnitt 8.4.6, »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.

Rheinwerk Computing - Zum Seitenanfang

8.4.9 Funktionen in awkZur nächsten ÜberschriftZur vorigen Überschrift

Ein wichtiges Feature einer Programmiersprache sind die so genannten 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 sie aufgerufen wird. Dabei können ihr immer wieder ein oder mehrere Parameter übergeben werden, mit denen sie dann arbeitet. Dabei unterscheidet man in awk zwischen den Funktionen, die man selbst im Skriptcode implementiert, und den sogenannten Builtin-Funktionen. Eine davon 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. [Fn. 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. Dahinter folgt der Name der Funktion und die in Klammern eingeschlossene Parameterliste, die auch leer sein kann.

Listing 8.80 Rohform einer Funktion

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

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

Listing 8.81 Berechnung des Mittelwerts

$ 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 z. B. aus der Programmiersprache C kennt, Werte zurückgeben. Diese Rü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 eine return-Anweisung. Dies ermöglicht es uns, die Funktion viel dynamischer einzusetzen. Das Beispiel verwendet den Rückgabewert der Funktion für eine bedingte Anweisung und übergibt den Wert an print.

Listing 8.82 return

$ 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

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: mathematische, String- und sonstige Funktionen. Informationen zu letzteren 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 $e^x$ 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 pseudozufä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 zu den wohl wichtigsten Funktionen in awk: den Stringfunktionen. In awk stehen Ihnen davon folgende Typen 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 Beschreibungen finden Sie in der Manpage zu awk beziehungsweise zu nawk oder gawk.


Rheinwerk Computing - Zum Seitenanfang

8.4.10 Ein paar Worte zum SchlussZur vorigen Überschrift

Dies war eine kleine Einführung in die Skriptsprache awk. Doch awk kann noch mehr – es gibt Funktionalität, 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.

>> Zum Feedback-Formular
<< zurück
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Linux Handbuch






 Linux Handbuch


Zum Katalog: Linux Server






 Linux Server


Zum Katalog: Raspberry Pi






 Raspberry Pi


Zum Katalog: Ubuntu 14.04 LTS






 Ubuntu 14.04 LTS


Zum Katalog: Roboter bauen mit Arduino






 Roboter bauen
 mit Arduino


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2012
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.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de