Dieser Beitrag stammt ursprünglich von www.linuxfocus.org


Guido Socher
Über den Author
Guido ist ein Linux-Fan, weil es ein freies Betriebssystem ist, und es einfach Spaß macht, mit Leuten der Linux Gemeinde aus aller Welt zusammenzuarbeiten. Seine Freizeit verbringt er mit seiner Freundin, hört BBC World Service Radio, macht Fahrradtouren und arbeitet mit Linux.

E-Mail an den Author 

Inhalt:
Einleitung
Ein kleines Beispiel
Syntax Regeln
Regular Expressions zum Bearbeiten von Texten

Suchen nach Textmustern mit Regular Expression
Unix Basics

[Ilustration]

Zusammenfassung: Regular Expressions werden zum Kontext abhängigen Suchen und Editieren von Texten benutzt. Sie werden in guten Editoren, Text-Parsern und verschiedenen Programmiersprachen benutzt.


Worst seing with Explorer. Try Netscape instead. 

Einleitung

Regular Expressions kommen in vielen Editoren wie vi, emacs in Programmen wie grep, egrep und in Programmiersprachen wie awk, perl und sed zur Anwendung.

Mit Regular Expressions kann kontextabhängig nach Textmustern gesucht werden. Eine Regular Expression ist deshalb nicht ein einfacher Suchstring, sondern die formale Beschreibung eines Textmusters.

Als ich das erste Mal sah, wie man mit Hilfe von Regular Expressions Texte editieren kann, war ich einfach begeistert. Änderungen an einem Text, für die ich Stunden gebraucht hätte, waren in wenigen Sekunden erledigt. Was ich jedoch auf dem Bildschirm sah, erschien mir wie eine zufällige Ansammlung von Punkten, Strichen und anderen Zeichen. Trotzdem, eines war mir klar: Ich wollte die Sprache der Regular Expressions lernen und war schon bald sehr erstaunt, wie einfach sie zu verstehen ist. Sie folgt wenigen einfachen Syntaxregeln.

Obwohl Regular Expressions in der UNIX Welt so weit verbreitet sind, gibt es nicht die eine Regular Expressions Sprache. Es sind vielmehr eine Anzahl verschiedener Dialekte, die man hier vorfindet. Es gibt zum Beispiel zwei Vertreter des bekannten grep Programmes, egrep und grep. Perl kann man wohl in diesem Zusammenhang als die Sprache mit der am weitesten entwickelten Regular Expressions Syntax bezeichnen. Zum Glück folgen alle diese Dialekte denselben einfachen Prinzipien und sobald man diese verstanden hat, ist der Rest sehr einfach.

In diesem Artikel möchte ich mich auf die Grundlagen beschränken, die Details kann man dann in den man-pages nachlesen.

Ein kleines Beispiel

Angenommen wir hätten folgende Telefonliste einer Firma:

Phone Name  ID
     ...
     ...
3412    Bob 123
3834  Jonny 333
1248   Kate 634
1423   Tony 567
2567  Peter 435
3567  Alice 535
1548  Kerry 534
     ...

Es handelt sich um eine Firma mit 500 Leuten und die Daten sind einfach in einem normalen ASCII File gespeichert. Einträge von Personen, die mit einer 1 starten arbeiten in Gebäude 1. Wer arbeitet in Gebäude 1?

Eine Regular Expression kann das
beantworten:

grep '^1' phonelist.txt
oder
egrep '^1' phonelist.txt
oder
perl -ne 'print if (/^1/)' phonelist.txt

In Worten bedeutet das: Suche nach allen Zeilen, die mit einer 1 anfangen. Das Zeichen "^" sorgt dafür, daß nur nach Einsen am Anfang der Zeilen gesucht wird.

Syntax Regeln

Ein-Zeichen Muster

Das Grundelement einer Regular Expression ist das Ein-Zeichen Muster. Die mit den Ein-Zeichen Mustern ist nur erfolgreich, wenn genau dieses eine Zeichen im Text zu finden ist. Ein Beispiel für ein Ein-Zeichen Muster ist die 1 im Beispiel von oben.

Ein anderes Beispiel für ein Ein-Zeichen
Muster ist:
egrep 'Kerry' phonelist.txt

Dieses Muster besteht nur aus Ein-Zeichen Mustern (den Buchstaben K,e usw.)

Mehrere Zeichen kann man in einer Menge zusammenfassen. Die Menge wird durch eckige Klammern angezeigt. Die gesamte Menge ist als ganzes auch ein Ein-Zeichen Muster. Eine Suche mit solch einer Menge ist erfolgreich, wenn genau eines der Zeichen aus der Menge im Textstring vorhanden ist. Ein Beispiel:

[abc]    Ein Ein-Zeichen Muster mit dem man nach
         genau einem der Zeichen a, b oder c
         sucht.
[ab0-9]  Ein Ein-Zeichen Muster mit dem man nach
         a oder b oder einer Zahl aus dem ASCII
         Bereich von Null bin Neun sucht
[a-zA-Z0-9\-] Das sucht nach Klein- oder Groß-
              buchstaben oder einer Zahl oder dem
              Minuszeichen.

Angewendet auf die Telefonliste:

egrep '^1[348]' phonelist.txt

Dieser Ausdruck sucht nach Zeilen, die mit 13, 14 oder 18 anfangen.

Die meisten ASCII Zeichen führen zur erfolgreichen Suche, wenn genau das Zeichen selbst im Text steht, aber es gibt auch Regular Expression Zeichen, die eine spezielle Bedeutung haben. Die rechteckigen Klammern starten z.B die Definition einer Menge. In einer Menge ist "-" ein spezielles Regular Expression Zeichen und gibt einen Bereich im ASCII Zeichensatz an. Um die normale Bedeutung eines solchen speziellen Zeichens zu bekommen, stellt man einfach einen Backslash "\" vor. In einigen Dialekten der Regular Expression Sprache haben wiederum der Backslash in Verbindung mit einem anderem Zeichen eine spezielle Bedeutung. In diesem Fall erhält man die normale Bedeutung in dem man den Backslash entfernt.

Der Punkt ist auch ein wichtiges Regular Expression Zeichen. Die Suche ist erfolgreich, wenn das verglichene Zeichen ein anderes Zeichen als das ASCII new-line Zeichen (new-line = neue Zeile) ist. Beispiel:

grep '^.2' phonelist.txt

Dieser Ausdruck sucht nach Zeilen mit einer 2 in der zweiten Position und irgend einem Zeichen an erster Stelle.

Mengen kann man invertieren indem man die Definition mit "[^" an Stelle von "[" startet. Nun bedeutet das "^" nicht mehr Anfang der Zeile sondern, "[" und "^" zusammen bezeichnen die inverse Menge.

[0-9]    Ein Ein-Zeichen Muster, die Suche ist
         erfogreich, wenn das Zeichen im Text
         eine Zahl ist.
[^0-9]   Alles was keine Zahl ist.
[^abc]   Alles was kein a, b
         oder c ist.
 .       Alles bis auf das new-line Zeichen.
         Identisch mit [^\n]. Das \n ist
         das new-line Zeichen.

Um nach allen Zeilen zu suchen, die NICHT mit einer
Eins anfangen schreibet man:
egrep '^[^1]' phonelist.txt

Anchors (Anker)

Vorher haben wir gesehen, daß "^" genau dem Anfang einer Zeile entspricht. Anchors sind spezielle Regular Expression Zeichen, die nicht ein Zeichen, sondern eine Position bezeichnen.

^  Der Zeilenanfang
$  Das Zeilenende

Um nach Leuten mit einer company-ID von 567 in unserer Liste zu suchen, benutzt man einfach den Ausdruck:

egrep '567$' phonelist.txt

In Worten: Suche nach zeilen, die mit 567 enden.

Multipliers

Multiplier Zeichen geben an, wie oft ein Ein-Zeichen Muster im Text vorkommen soll:

Beschreibung grep egrep perl vi vim vile elvis emacs
nie oder oft * * * * * * * *
mindestens ein mal \{1,\} + +   \+ \+ \+ +
vielleicht ein mal \? ? ?   \= \? \= ?
n bis m mal \{n,m\}   {n,m}       \{n,m\}  

Anmerkung: Bei den verschiedenen VI Typen sollten die Option magic gesetzt sein.

Ein Beispiel aus der Telefonliste:

....
1248   Kate 634
....
1548  Kerry 534
....

Um nach einer Zeile zu suchen die sich aus einer 1, einigen Zahlen, einigen Leerzeichen und dem Buchstaben K zusammensetzt, tippt man:

grep '^1[0-9]\{1,\} \{1,\}K' phonelist.txt
oder man benutzt * und wiederholt [0-9] und
das Leerzeichen:
grep '^1[0-9][0-9]*  *K' phonelist.txt
oder
egrep '^1[0-9]+ +K' phonelist.txt
oder
perl -ne 'print if (/^1[0-9]+ +K/)' phonelist.txt

Die Multiplier Zeichenfolge bestimmt, wie oft das vorhergehende Ein-Zeichen Muster im Text vorkommen soll. "23*4" bedeutet z.B. nicht "2, eine 3, vielleicht irgend etwas und dann eine 4" (das wäre "23.*4")! Vielmehr bedeutet "23*4" daß nach "einer 2, vielleicht einigen Dreien und einer 4" gesucht werden soll.

Es ist wichtig zu wissen, daß diese Multiplier gierig sind. Das bedeutet, daß das erste Multiplier Suchmuster sich so weit wie möglich nach rechts ausdehnt.

Dieser Sachverhalt macht keinen großen Unterschied für grep, ist aber beim Editieren von Texten wichtig.

Der Ausdruck ^1.*4
würde sich über ganze Zeile

1548  Kerry 534

von Anfang Ende ausdehnen.
Der Ausdruck dehnt sich nicht nur
auf den kleinsten Bereich, also 154,
aus, sondern so weit es geht.

Klammern als Speicher

Das Klammern als Speicherkonstrukt beeinflußt nicht die Art oder die Zeichen nach denen gesucht wird. Es sorgt nur dafür, daß ein Textstring abgespeichert und später über Variablen darauf zugegriffen werden.

Das erste Klammern als Speicherkonstrukt wird über Variable 1 angesprochen, das zweite über Variable 2 und so weiter.

Programm Name Syntax Syntax der Variablen
grep \(\) \1
egrep () \1
perl () \1 or ${1}
vi,vim,vile,elvis \(\) \1
emacs \(\) \1

Ein Beispiel:

Der Ausdruck [a-z][a-z] sucht
nach Kleinbuchstaben.

Nun kann man diese Variablen benutzen, um nach Textmustern wie 'otto' zu suchen:

egrep '([a-z])([a-z])\2\1'

Die Variable \1 enthält den Buchstaben o
und \2 den Buchstaben t

Der Ausdruck würde auch auf
anna passen, aber nicht auf yxyx.

Klammern als Speicherkonstrukte werden normalerweise nicht zum Suchen von Namen wie otto und anna benutzt, sondern zum Editieren sowie zum Suchen und Ersetzen.

Regular Expressions zum Bearbeiten von Texten

Zum Editieren von Texten braucht man einen Editor wie vi oder emacs oder Perl All diese Programme beherrschen Regular Expressions.

In emacs benutzt man M-x query-replace-regexp oder replace-regexp. M-x query-replace-regexp läßt sich jedes Suchen und Ersetzen bestätigen. Am besten legt man sich query-replace-regexp oder replace-regexp auf eine Funktionstaste.

Im vi benutzt man :%s/ / /gc. Das % bezeichnet die Ex-region 'ganze datei' und kann natürlich durch jede andere Ex-Region ersetzt werden. Im Editor vim kann man zum Beispiel mit shift-v und Cursor-up/down eine Region einfach markieren. Leider würde eine Einführung in vim den Rahmen diese Artikel sprengen. / / /gc ist interaktiv und fragt nach einer Bestätigung. s/ / /g ist nicht interaktiv. In perl benutzt man:

perl -pe 's/ / /g' 

Nun einige Beispiele. In der Firma müssen die Telefonnummern geändert werden. Alle Telefonnummern, die mit einer 1 anfangen, sollen nun eine 2 nach der zweiten Ziffer bekommen. Aus 1423 wird damit 14223.

Phone Name  ID
     ...
3412    Bob 123
3834  Jonny 333
1248   Kate 634
1423   Tony 567
2567  Peter 435
3567  Alice 535
1548  Kerry 534
     ...

und so einfach geht das:

vi:    s/^\(1.\)/\12/g
emacs: ^\(1.\)   replaced by  \12
perl:  perl -pe 's/^(1.)/${1}2/g' phonelist.txt
Nun sieht die Telefonliste so aus:

Phone Name  ID
     ...
3412    Bob 123
3834  Jonny 333
12248   Kate 634
14223   Tony 567
2567  Peter 435
3567  Alice 535
15248  Kerry 534
     ...

Perl kennt nicht nur die Variablen \1 \9. \12 würde deshalb auf Variable 12 verweisen. Um das Problem zu lösen benutzt man einfach ${1}.

Nur stehen die Spalten jetzt nicht mehr schön untereinander. Wie kann man das lösen? Man könnte z.B einfach testen, ob an der 5ten Position ein Leerzeichen steht.

vi:     s/^\(....\) /\1  /g
emacs:  '^\(....\) '  replaced by  '\1  '
perl:   perl -pe 's/^(....) /${1}  /g' phonelist.txt

Jetzt hat man:
Phone Name  ID
      ...
3412     Bob 123
3834   Jonny 333
12248   Kate 634
14223   Tony 567
2567   Peter 435
3567   Alice 535
15248  Karry 534
      ...

Ein Kollege hat die phonelist.txt editiert und dabei nicht aufgepaßt. Am Anfang einiger Zeilen stehen nun einige Leerzeichen. Wie kann man das lösen ?

Phone Name  ID
      ...
3412     Bob 123
     3834   Jonny 333
12248   Kate 634
14223   Tony 567
 2567   Peter 435
3567   Alice 535
  15248  Kerry 534
      ...

Das sollte die Sache korrigieren:
vi:     s/^  *//  (Vor dem Stern sind zwei Leerzeichen)
emacs:  '^ +'  replaced by: Nichts
perl:   perl -pe 's/^ +//' phonelist.txt

Du schreibst gerade an einem Programm benutzt die Variablen temp und temporary. Nun möchtest Du temp durch die Variable counter ersetzen. Würde man einfaches Suchen und Ersetzen benutzen, so würde aus temporary, counterorary. Das willst Du vermeiden.

Regular Expressions machen das ganz einfach. Man ersetzt temp([^o]) mit counter\1. Das heißt, temp und nicht "o" wird ersetzt. (Eine andere Alternative wären Boundaries, die hier aber nicht besprochen wurden.)

Ich hoffe, daß dieser Artikel Dein Interesse geweckt hat. Nun solltest Du einfach mal in die man-pages Deines Lieblingseditors sehen.

Es gibt außerdem noch mehr spezielle Regular Expressions Zeichen wie z.B Alterations, eine Art Oder, und natürlich die oben erwähnten Boundaries.

Viel Spaß.
  Guido

Webpages mantained by Miguel Ángel Sepúlveda 
© Guido Socher 1998 
LinuxFocus 1998