Gawk/Regular Expressions

Aus Foxwiki

Reguläre Ausdrücke in Mustern

Ein Muster ist die maßgebliche Methode, um den Programmfluss von Awk zu steuern
  • Nur wenn der Inhalt des aktuell bearbeiteten Datensatzes mit dem angegebenen Muster übereinstimmt, wird der zugehörige Kommandoblock ausgeführt.
  • Um dieses Schema flexibel zu gestalten, verhelfen reguläre Ausdrücke zur Formulierung der Muster.
  • Anstelle starrer Vergleichsmuster treten Platzhalter, sodass von der Eingabe quasi nur noch eine »Ähnlichkeit« mit dem Muster gefordert wird, um die Kommandofolge anzuwenden.

Zulässig sind die erweiterten Reguläre Ausdrücke wie sie von egrep verwendet werden:

c matches the non-metacharacter c.
\c matches the literal character c.
. matches any character including newline.
^ matches the beginning of a string.
$ matches the end of a string.
[abc. . .] character list, matches any of the characters abc. . ..
[^abc. . .] negated character list, matches any character except abc. . ..
r1|r2 alternation: matches either r1 or r2.
r1r2 concatenation: matches r1, and then r2.
r+ matches one or more r 's.
r* matches zero or more r 's.
r ? matches zero or one r 's.
(r) grouping: matches r.
r{n}r{n,}r{n,m} One or two numbers inside braces denote an interval expression.
  • If there is one number in the braces, the preceding regular expression r is repeated n times.
  • If there are two numbers separated by a comma, r is repeated n to m times.
  • If there is one number followed by a comma, then r is repeated at least n times.
\y matches the empty string at either the beginning or the end of a word.
\B matches the empty string within a word.
\< matches the empty string at the beginning of a word.
\> matches the empty string at the end of a word.
\s matches any whitespace character.
\S matches any nonwhitespace character.
\w matches any word-constituent character (letter, digit, or underscore).
\W matches any character that is not word-constituent.
\` matches the empty string at the beginning of a buffer (string).
\' matches the empty string at the end of a buffer.

Beispiel 1

Um alle Leerzeilen der Datei /etc/inittab auszugeben, hilft:

$ awk '/^$/ {print "Zeile" FNR "ist leer"}' /etc/fstab
Zeile9ist leer
Zeile11ist leer
Zeile13ist leer
Zeile17ist leer

Die interne Variable FNR enthält die Nummer der aktuell bearbeiteten Zeile.

Beispiel 2

Steht eine gültige Zahl (Integer) in der Eingabe?

$ awk '/^digit:+$/ {print "Ja!"}'
123457900
Ja!
12A
[Ctrl]-[D]

Die enthaltene Anwendung von Zeichenklassen ([:digit:]) wird später behandelt.

Metazeichen / regulärer Ausdruck

Verwechseln Sie die regulären Ausdrücke nicht mit den Metazeichen der Shells! Zwar ist das Funktionsprinzip identisch, selten aber die konkrete Semantik der Zeichen (bspw. * oder ?): Liste alle mit "a" beginnenden Dateien (Shell):

$ ls -1 a*
a.out
ascii2short.c
awk.txt

Suche in der Ausgabe von "ls" nach mit "a" beginnenden Dateien (awk)

$ ls | awk '/^a+/ {print}'
a.out
ascii2short.c
awk.txt
  • Das Beispiel verdeutlicht die unterschiedliche Syntax zwischen Shell und awk bei der Realisierung derselben Aufgabe.

Übung

  • Ersetzen Sie einmal das awk-Muster durch »/a*/« und vergleichen die Resultate.
  • Was ist da passiert?

Muster und Zeichenketten

Bislang arbeitete die Mustererkennung stets über die gesamte Eingabezeile.

"Zeichenkette ~ /Muster/"

… dehnt sich dieses Schema auf beliebige Zeichenketten aus.

So extrahiert nachfolgendes Awk-Programm alle Benutzernamen aus der Datei /etc/passwd, deren Heimatverzeichnisse unterhalb von /home liegen:

$ awk -F ':' '$6 ~ /^\/home/ {print $1}' /etc/passwd
tux
user
  • Die Zeichenkette ist hier das 6.Feld ($6) der Passwortdatei (Heimatverzeichnis); die Syntax des Musters sollte sich anhand der einleitenden Tabelle der regulären Ausdrücke erklären.
  • Felder und deren Indizierung sind Gegenstand des folgenden Abschnitts.

Flexible Muster

  • Der Zeichenkettenvergleich ist auch hilfreich, wenn das zu betrachtende Muster flexibel gestaltet werden soll.
  • Die Zeichenkette ist dann die aktuell bearbeitete Zeile ($0, wird später behandelt); als Muster dient der Inhalt einer Variablen (muster im Beispiel):
$ cat sinnlos.awk
#!/usr/bin/awk -f

$0 ~ muster { print $0; }

Die Variable muster wird dann als Kommandozeilenargument geeignet belegt:

$ ./sinnlos.awk muster='.+' <Datei>

Zeichenklassen

  • Der Begriff »Wort« ließe sich einfach als eine Folge von Buchstaben umschreiben; eine »ganze Zahl« demzufolge als Ansammlung von Ziffern.
  • Ein simples awk-Programm, das Zeichenketten aus der Eingabe in die Kategorien »Wort« oder »Zahl« eingliedert, könnte wie folgt verfasst sein:
  1. Testet die Eingabe auf Wort, Zahl oder Sonstiges.
/[0-9]+/ { print "Eine Zahl" }
/[A-Za-z]+/ { print "Ein Wort" }
  • Auf den ersten Blick ist kein Fallstrick zu erkennen, aber* Wer garantiert, dass ein Skript tatsächlich nur in Umgebungen eingesetzt wird, die denselben Zeichensatz verwenden wie der Skriptentwickler?
  • Nicht in jedem Zeichensatz bilden Ziffern bzw. 
  • Buchstaben eine zusammenhängende Folge.
  • Und Angaben »[von-bis]« beruhen genau auf jenem Prinzip.
  • Um Skripte portabel (POSIX-Standard) zu halten - und mitunter auch kürzer - sollte daher Zeichenklassen der Vorzug vor Bereichsangaben gegeben werden.
  • Es ist dann Aufgabe der konkreten Awk-Implementierung, eine Zeichenklasse auf den verwendeten Zeichensatz abzubilden.

Unser Beispiel mit Zeichenklassen schreibt sich dann so:

# Testet die Eingabe auf Wort, Zahl oder Sonstiges.
/digit:+/  { print "Eine Zahl" }
/alpha:+/  { print "Ein Wort" }

Vorausgesetzt Ihr System wurde sauber konfiguriert (Belegung der Shellvariablen $LANG), sollten nun auch die deutschen Umlaute korrekt erfasst werden:

$ echo Überprüfung | awk '/alpha:+/ {print "Ein Wort";}' 
Ein Wort

Weitere Zeichenklassen

[:alnum:] Alphanumerische Zeichen
[:alpha:] Alphabetische Zeichen.Um zu testen, ob in einer Variablen eine gültige Zahl (ganzzahlig) gespeichert ist, bietet sich folgendes Konstrukt an:

echo "0815" | awk '/^digit:+$/ {print "eine Zahl"}'

Eine Zahl
[:blank:] Leerzeichen und Tabulatoren
[:cntrl:] Steuerzeichen
[:digit:] Nummerische Zeichen
[:graph:] Druck- und sichtbare Zeichen (Ein Leerzeichen ist druckbar aber nicht sichtbar, wogegen ein »a« beides ist)
[:lower:] Kleingeschriebene alphabetische Zeichen
[:print:] Druckbare Zeichen (also keine Steuerzeichen)
[:punct:] Punktierungszeichen/Punctuation characters (Zeichen die keine Buchstaben, Zahlen, Steuerzeichen oder Leerzeichen sind) (".", "," ":")
[:space:] Druck- aber keine sichtbaren Zeichen (Leerzeichen, Tabulatoren, Zeichenende ..)
[:upper:] Großbuchstaben
[:xdigit:] Hexadezimale Zeichen