# FAQ Tips > Hier Suchen und Finden, Links, Tutorials >  RegEx Tutorial Teil1 mit Beispielen in bash, awk, sed und grep

## BetterWorld

*Einleitung*
Ein *Reg*ular*Exp*ression ist im wesentlichen ein Filter, den man auf Strings anwenden kann.
"Strings" sind hier Zeichenketten beliebigen Inhalts; also auch Non-printable-characters, Whitespaces, Tabulatoren usw. mit eingeschlossen.

Sehr viele Programmiersprachen stellen solche *RegEx*-engines zur Verfügung. Die einen mächtiger mit vielen Erweiterungen, andere nur geringfügig. Alle haben ihre Eigenheiten. 

Die Programmiersprache *perl*  macht von *RegEx*es regen Gebrauch und hatte als eine der ersten Sprachen eine sehr mächtige *RegEx*-Engine eingebaut. Noch heute ist  *PCRE* (== *P*erl*C*ompatible*R*egular*E*xpression ein gängiges Akronym. In jeder Sprache, die *PCRE* kennt, kann man dieses mächtige Werkzeug, so man es einmal gelernt hat, also sofort einsetzen.
Was für die allemeisten Zwecke ausreicht.

Die *GNU*-Systemprogramme *awk, sed* und *grep* und die *bash* selbst verwenden ebenfalls *PCRE*. Und sogar in meinem Lieblingseditor *vi* ( oder besser *vim*) kann man damit zum Beispiel auch Suchen&Ersetzen. Oder mit *less* irgendetwas Suchen. Selbst *man* und *info* beherrschen schlichte *RegEx*es. (Ja, selbst da muss man nicht öde scrollen, sondern kann mittels Suche springen.)

In diesem Minitutorial stelle ich die Basics vor und gebe Beispiele mit Lösungen für alle vier Programme.
Natürlich wird man später für den jeweiligen Zweck das _"richtige"_ Tool verwenden, wenn die zu erledigenden Aufgaben komplexer werden.

*einfache RegExes*
Wir verwenden oft schon reguläre Ausdrücke (==*RegEx*) ohne es wirklich zu merken.

```
#Schon ein einfaches
ls z*

# ist letztlich eine RegEx-Suche nach Dateien, deren Namen mit "z" beginnt
# und dem dann beliebige Buchstaben folgen und davon beliebig viele.

# Auch "greppen" wir oft Zeilen aus einer Datei oder aus der Ausgabe eines Befehles
grep Dummy  irgendeineDatei
```

Hier lassen wir *grep* nach einer Zeile in der Datei *irgendeineDatei* suchen, die den RegEx *Dummy* enthält.
*grep* ist übrigens ein Akronym für *g*lobal*r*egular*e*xpression*p*rint
(Manche sagen *get* statt *global*) 
*Dummy* ist also ein "wortwörtlicher RegEx", oder mathematisch formuliert: Jedes Zeichen ist ein RegEx, der nur auf sich selbst "matched". (Sofern das Zeichen keine Sonderbedeutung hat; dann wäre es mit einem davorstehenden Backslash erst zur normalen Bedeutung zu escapen.)*Merke*
*Das Globbing der bash ist eine Sonderform der RegExes*(*Globbing* nennt man das Expandieren von **?[]* zu Dateinamen, was die *bash* beim parsen der eingegebenen Kommandozeile macht.)

*Wie man RegExes liest*
Der RegEx *Dummy* ist also ein Filter, der im Wesentlichen sagt: Ein großes "D", gefolgt von einem kleinen "u", gefolgt von einem "m", gefolgt von einem "m", gefolgt von einem "y". Wir schreiben *gefolgt von einem "m"* zweimal hintereinander, nicht *gefolgt von 2 "m"*.  
Die RegEx-Engine liest (bei Sprachen mit *L*eft*T*o*R*ight Leserichtung) den RegEx von links nach rechts und hört auf, sobald ein vollständiger Match erreicht wurde.
Wir werden später auf diesen Punkt noch ausführlicher zurückkommen.
(Und klar ist die Leserichtung umgekehrt im Hebräischen oder Arabischen Sprachen)

*Mengenangaben*
Das gibt es natürlich auch: ein *m{2}* sagt *genau zwei "m"*. 
*m{4,7}* meint *genau vier bis sieben "m"*.
*m{,8}* meint *null bis 8 "m"*. Und noch *m{3,}* meint letztlich *mehr als 3 "m"*
(Korrekt wäre übrigens *m{0,8}*, aber viele RegExDialekte erlauben das Auslassen.)

Diesen Mengenangaben ist eines allen gemein:
*Sie beziehen sich auf das direkt VORHERGEHENDE Was-auch-immer*:
Der RegEx  *abcd{2}*  matched *abcdd*, aber niemals *abcdabcd*
("match"  ist der "Fachbegriff" für zutreffen)

Und natürlich gibt es auch die Mengenangaben eins, zwei, viele.
Ein *a** meint Null bis unendlich viele *a*s.
*a+* meint beliebig viele *a*s, aber mindestens eines.

Dem aufmerksamen Leser sollte nun schon die Frage auf der Zunge liegen, dass das ja dem *ls z** widerspricht.
Richtig. Wenn wir das Globbing der *bash* verwenden, meint der *** beliebig viele beliebige Zeichen und nicht, wie bei einem RegEx das von Null bis unendlich oft vorkommende Zeichen vor dem Stern.

Die Mengenoperatoren *?*+{}* heißen in der Fachsprache "Quantifikatoren".

*Joker für beliebige Zeichen*
Ein Regex der dem Globbing der *bash* entspricht, wäre *.**
Der *.* meint ein beliebiges Zeichen, und der *** ermöglicht die beliebige Wiederholung und die Auslassung (Null mal vorkommend) des beliebigen Zeichens.
Wollen wir nach einem Punkt selbst suche, so müssen wir ihn durch das Voranstellung eines Backslashes escapen, wie üblich unter den Unices.

Das optionale Vorkommen (oder in anderen Worten: das Null oder einmalige Vorkommen) lässt sich mit *?* in einem RegEx ausdrücken. Abweichend vom *bash* Globbing, wo es ein einziges beliebiges Zeichen meint.
*a?* meint also, dass an dieser Stelle ein *a* stehen kann, oder auch nicht.

*Zeichenklassen*
Oft will man nach einer Menge von Zeichen suchen, die an einer Stelle stehen. Wollen wir z.B. alle Strings finden, die mit *bild* beginnen, dem ein *A* oder ein *B* folgt, so schreiben wir als RegEx einfach *bild[AB]* 
*[AB]* meint hier *genau ein Zeichen* aus der Menge, die die Zeichen *A* und *B* enthält, also *genau ein A* oder *genau ein B*. Wir wissen schon, dass wir beliebig Quantifikatoren anhängen können.
Natürlich muss man die Menge von *a* bis *f* nicht *[abcdef]* schreiben, sondern kann sie mit dem nur innerhalb einer Zeichenklasse gültigen Bereichsoperator *-* abkürzen zu* [a-f]*
*[a-f]* meint also *genau ein a* oder *b* oder .... oder *f*
Das ganze geht natürlich auch mit Zahlen.
*[0-4]* meint entweder eine *0* oder eine *1* oder ... oder eine *4*.*Merke*
*Zahlen haben in RegExes KEINERLEI numerische Bedeutung.
Sie sind lediglich Zeichen, die nach der LOCALE sortiert werden können.
Wir reden immer nur von Zeichen, nie von Zahlen.*
Zeichenklassen können auch negiert werden.
*[^A]* meint JEDES Zeichen außer *A*
Eine Klasse wie *[^23]* meint JEDES Zeichen *außer dem Zeichen 2* oder *dem Zeichen 3*
Das kann also ein *a* oder jedes andere Zeichen sein, nur eben keine *2* und keine *3*
Diese Negierung muss aber direkt nach der öffnenden Klammer stehen. Steht es nicht am Anfang der Zeichenklasse, so ist es lediglich das Zeichen *^* selbst.
*[A^B]* meint also entweder ein *A* oder ein *B* oder ein *^*. Keine Verneinung. Das steht kein *nicht B*. 

Damit können wir jetzt einen RegEx für hexadezimale Zahlen so schreiben:
*[a-zA-Z0-9]* eine hexadezimale Ziffer
*[a-zA-Z0-9]{3}* eine 3-stellige hexadezimale Ziffer
*[^a-zA-Z0-9]* alles außer einer hexadezimale Ziffer
*[^a-zA-Z0-9]{3}* drei Zeichen die kein Zeichen der hexadezimalen Ziffern enthalten

Und natürlich lässt sich auch das *[a-zA-Z0-9]* abkürzen zu*[[:xdigit:]]*

Eine Liste dieser "Abkürzungen":
*[: alpha  :]  nur Buchstaben
[:alnum:]  alle Buchstaben und Ziffern
[:blank:]  alle Leerzeichen (also auch Tabulatoren )
[:cntrl:]  alle Kontrolzeichen z.B. 
[:digit:]  alle Ziffern
[:graph:]  graphische Sonderzeichen
[:lower:]  Kleinbuchstaben
[:print:]  Drucksteuerzeichen
[:punct:]  Interpunktionszeichen, also Punkt, Komma usw.
[:space:]  Leerzeichen (ohne Tabulatoren )
[:upper:]  Großbuchstaben
[:xdigit:]  hexadezimales Zeichen*

Auch diese Abkürzungen  lassen sich mit *^* verneinen.
*[^[:digit:]]* bezeichnet alles, außer einer Ziffer.
Beachtet, dass *[:digit:]* der Name einer Menge ist. Die äußeren *[]* bezeichnen eine Zeichenklasse.
Da diese "Klassennamen" aber nur innerhalb einer Zeichenklasse Sinn machen, treten sie halt auch nur innerhalb einer Zeichenklasse auf. Die Negation *^* muss also nach der ersten *[* stehen!

Diese Abkürzungen haben noch einen Vorteil. Wir müssen uns nicht darum kümmern, ob in Japan ein Komma ein Satztrenner ist. All solche Eigenschaften werden automatisch berücksichtigt. Und alle Zeichen werden der jeweiligen Landessprache (besser der LOCALE nach) sortiert. Bevor es sie gab, musste man für jedes andere Land selbst mühsam Zeichenklassen definieren. 

*Anfang, Ende und Anker*
Wenn das *^* nicht innerhalb von *[]* steht, ist es keine Negation eines Zeichens, sondern meint den Anfang des Strings.  
*^[^a]* sagt also, dass am Anfang des Strings/der Zeile kein *a* stehen darf.

Und das *$* Zeichen meint immer das Ende eines Strings/einer Zeile.
Ein *^BEGIN.*END$* meint also einen String, der am Anfang die Zeichenfolge *BEGIN*hat.
Dann dürften beliebig viele beliebige Zeichen folgen, aber der String muss auf *END* enden.

Die Zeichen *^$* _verankern_ also unseren RegEx am Anfang und Ende eines Strings.
Dieses Konzept ist sehr wichtig. Wenn die RegExes komplizierter werden, sind sie unerlässlich.
Wir werden darauf noch häufiger zu sprechen kommen.

*Ein paar praktische Beispiele*
Der Befehl *date* kennt über 40 Formatdescriptoren. Die kann man sich nicht merken. (Also ich jedenfalls nicht). Ich habe mir dafür einen schlichten Alias definiert:

```
alias datef="man date | grep -E '^[[:blank:]]+%'"
```

Mit *alias mycommand="command parameters"* definiere ich einen Alias.
Das Kommando selbst ruft die Manpage von *date* auf und leitet die Ausgabe mit der Pipe *|* zum Befehl *grep* bei dem ich mit *-E*xtended RegExes einschalte und lasse dann nach dem RegEx *^[[:blank:]]+%* suchen. 
Der RegEx liest sich: An Anfang der Zeile *^* muss ein Zeichen *[]* der Zeichenklasse *[:blank:]* mindestens einmal *+* stehen und dann muss ein *%* Zeichen kommen.
Genau die Beschreibungen aller gültigen *date* Formatzeichen.

Mit *awk* wird das zu 

```
man date | awk ' /^[[:blank:]]+%/ {print $0}'
```

Und mit *sed* zu

```
man date | sed -rn '/^[[:blank:]]+%/p'
```

Alles ziemlich gleich. Die Besonderheiten von *sed* und *awk* werde ich in den nächsten Teilen beschreiben.
Aber man sieht klar, dass die eigentlichen RegExes doch ziemlich gleich sind.

Und das Ganze lässt sich auch innerhalb von *man* selbst machen.
Gebt dazu einfach *man 1 date*  ein (die 1 gibt den Abschnitt der Man- Kataloge direkt mit an und vermeided so die Abfrage, welches Man-Abschnitt man lesen will; *man p date* würde die POSIX Manpage zu *date* direkt anzeigen).
Damit landet ihr innerhalb der Manpage.

Tippt nun einfach */* (das Kommando für "Suche") und unseren RegEx dahinter, gefolgt von Enter.
*/^[[:blank:]]+%* landet also ebenfalls unter Verwendung von RegExes bei den Formatbezeichnern.

Es sind aber schon ein paar Gemeinsamkeiten zu erkennen. Die RegExe sind alle gleich.
Und die Suche beginnt immer mit */* und endet oft auf */*

Häufig will man Dateien und Verzeichnisse mit Datumsangaben verwalten. Wir machen das mal schnell.
Erstellt euch ein Verzeichnis und wechselt dorthinein. Und dann folgt dem hier:


```
# wir erstellen uns eine Verzeichnisstruktur
# dazu verwenden wir die "Brace-Expansion" der bash
mkdir -p ./bilder{A..B}/bilder_20{13..15}/monat_{01..12}

#damit haben wir Verzeichnisse wie z.B.
# ./bilderA/bilder_2013/monat_11
# und erzeugen dort ein paar Dateien 
find -type d | grep monat | while read dir; do 
    touch $dir/bild_{00..23}Uhr_{00..59}.jpg
done

# und lassen uns nun spezifische Bilder anzeigen
ls ./bilder[AB]/bilder_201[35]/monat_04/bild_18Uhr_3[4-9]*

# zum Schluss löschen wir den Käse wieder
rm -rf bilder*
```

Die *bash* kennt beim Globbing nur ganz einfach RegExes. Die *{}* sind dort keine Quantoren, sondern reine Bereichsangaben. Diese Brace-Expansion ist nicht auf Zahlen beschränkt sind, es geht auch *echo {a..d}*.

Damit endet der erste Teil. Im zweiten wird es spezifischer und damit wirklich nützlicher.
Zum Üben versucht folgende Dinge:
Fischt aus der Ausgabe von *man man* die Zeilen heraus, die beschreiben, welcher Manpage Abschnitt/Katalog für welche Themen Hilfe gibt.
Eine paar Zeilen davon lauten:


```
       0   Dateiheader (gewöhnlich in /usr/include)
       1   Ausführbare Programme oder Shell-Befehle
       2   Systemaufrufe (Kernel-Funktionen)
```

Fehler, Kritik, Vorschläge und Hinweise gerne per PM (und den Hinweis, ob ich das hier veröffentlichen darf)
Ich werde im nächsten Teil die Regexes vertiefen und mich auf *sed* und *awk* konzentrieren.
Ist anderes gewünscht, einfach per PM mitteilen.

_Dieses Minitutorial steht unter der Lizenz CC BY-NC-SA kann also nichtkommerziell weitergegeben werden unter den gleichen Bedingungen bei Nennung meiner Email karl.thomas.schmidt@googelmail.com_

----------

