Zum Inhalt springen
Kontakt

Sebastian Nawrot
Dorneystr. 45
44149 Dortmund

Linux & Shell

Regex Tutorial: Reguläre Ausdrücke verstehen & anwenden

Regex von Grund auf: Metazeichen, Quantoren, Gruppen und Lookaheads – mit Praxisbeispielen für grep und sed sowie einem Cheat Sheet zum Nachschlagen.

Sebastian Nawrot
12 Min. Lesezeit
#Regex#Reguläre Ausdrücke#Bash#grep#sed#Linux
Regex Tutorial: Reguläre Ausdrücke verstehen & anwenden

Reguläre Ausdrücke — kurz Regex — sind eine Minisprache zum Beschreiben von Textmustern. Mit ihnen durchsuchst du Log-Dateien nach Fehlermeldungen, prüfst ob eine E-Mail-Adresse gültig aussieht, extrahierst Datumsspalten aus CSV-Exporten oder ersetzt Massen-weise Zeichenfolgen in Tausenden von Dateien. Einmal verstanden, sparen sie täglich Zeit in der Shell, im Code und im Editor.

Dieses Tutorial führt dich von den Grundlagen bis zu Lookaheads, erklärt den wichtigen Unterschied zwischen POSIX-Regex und PCRE und zeigt, wann du welche Form in grep oder sed einsetzt. Begleitend gibt es ein Regex-Cheat-Sheet zum Lesezeichen-Setzen — und falls du Muster sofort ausprobieren willst, öffne parallel den Regex-Tester in der Codeschnipsel-Toolbox.

Eine Abgrenzung vorab, denn sie ist die Quelle vieler stiller Bugs: Regex ist nicht dasselbe wie Globbing. Globbing expandiert Dateinamen auf Shell-Ebene, bevor ein Befehl überhaupt läuft (*.txt); Regex matcht Textinhalt innerhalb von Tools wie grep oder sed. Gleich aussehende Zeichen wie * bedeuten in beiden Welten etwas völlig anderes.

Literale und Metazeichen

Das einfachste Regex-Muster ist ein wörtlicher String: fehler matcht genau das Wort „fehler". Wirklich mächtig werden reguläre Ausdrücke durch Metazeichen — Zeichen, die eine besondere Bedeutung tragen statt sich selbst zu vertreten.

MetazeichenBedeutung
.Beliebiges einzelnes Zeichen (außer Zeilenumbruch)
\Escape-Zeichen: macht das nächste Zeichen zum Literal
^Anfang der Zeile (als Anker)
$Ende der Zeile (als Anker)
[…]Zeichenklasse: eines der aufgelisteten Zeichen
(…)Gruppe: fasst Teilmuster zusammen
{…}Quantor: legt Wiederholungsanzahl fest
?Null oder ein Mal (Quantor)
*Null oder mehr Mal (Quantor)
+Ein oder mehr Mal (Quantor)
|Alternation (oder): in ERE |, in BRE als \\| geschrieben

Sobald du eines dieser Zeichen wörtlich matchen willst, stellst du einen Backslash davor: \. matcht einen echten Punkt, \( eine echte öffnende Klammer. Ein literales Pipe-Zeichen schreibst du in ERE als \\\|.

Zeichenklassen

Zeichenklassen in eckigen Klammern matchen genau ein Zeichen aus einer definierten Menge.

[abc] matcht ein a, b oder c. Mit einem Bindestrich lässt sich ein Bereich angeben: [a-z] matcht jeden Kleinbuchstaben, [0-9] jede Ziffer, [A-Za-z0-9] jeden alphanumerischen Charakter. Ein ^ direkt nach der öffnenden Klammer negiert die Klasse: [^0-9] matcht alles außer Ziffern.

Daneben gibt es Abkürzungen — aber hier ist Vorsicht geboten, weil ihr Verhalten vom verwendeten Regex-Engine abhängt:

AbkürzungPCRE-BedeutungPOSIX-Äquivalent in grep -E
\dZiffer (0–9)[0-9] oder [[:digit:]]
\DKeine Ziffer[^0-9]
\wWort-Zeichen (Buchstabe, Ziffer, _)[[:alnum:]_]
\WKein Wort-Zeichen[^[:alnum:]_]
\sLeerzeichen, Tab, Zeilenumbruch[[:space:]]
\SKein Whitespace[^[:space:]]

\d, \w und \s sind Perl-Klassen (PCRE). In grep -E (Extended Regular Expressions, ERE) stehen sie nicht zur Verfügung — du bekommst keine Fehlermeldung, aber das Match-Verhalten ist undefiniert oder falsch. Für ERE-konforme Shell-Arbeit nutze die POSIX-Klassen in der rechten Spalte. Mit grep -P aktivierst du explizit den PCRE-Engine, der \d versteht.

Quantoren

Quantoren legen fest, wie oft das vorherige Element auftreten darf oder muss.

QuantorBedeutung
*0 oder mehr Mal
+1 oder mehr Mal
?0 oder 1 Mal (optional)
{n}Genau n Mal
{n,}Mindestens n Mal
{n,m}Zwischen n und m Mal

Standardmäßig sind Quantoren greedy (gierig): Sie matchen so viel wie möglich. Aus dem String <b>fett</b> matcht <.*> den gesamten String von <b> bis </b>, nicht nur das erste Tag. Das ist oft unerwünscht.

Mit einem angehängten ? wird ein Quantor lazy (genügsam) und matcht so wenig wie möglich: <.*?> matcht jetzt <b> und dann getrennt </b>. Laziness ist ein PCRE-Feature und gibt es auch in Python, JavaScript und den meisten modernen Engines — in POSIX-ERE (also grep -E ohne -P) steht *? nicht zur Verfügung. In der Shell brauchst du dafür das -P-Flag:

grep -oP '<.*?>' datei.html  # -P nötig, da Laziness ein PCRE-Feature ist

Anker

Anker matchen keine Zeichen, sondern Positionen im Text.

^ steht für den Zeilenanfang: ^Fehler matcht nur Zeilen, die mit dem Wort „Fehler" beginnen. $ steht für das Zeilenende: \.log$ matcht Zeilen, die auf .log enden.

\b ist eine Wortgrenze — die Position zwischen einem Wort-Zeichen und einem Nicht-Wort-Zeichen: \broot\b matcht das Wort „root", aber nicht „rootkit" oder „groot". \b ist ein PCRE-Feature; in ERE ist es nicht portabel verfügbar. In grep -E hilft ersatzweise die längere Konstruktion (^|[^[:alnum:]_])root([^[:alnum:]_]|$), oder du greifst zu grep -P.

Gruppen & Alternativen

Runde Klammern (…) erfüllen zwei Funktionen: Sie fassen Teile eines Musters zu einer Einheit zusammen, auf die Quantoren angewendet werden können, und sie speichern den gematchten Text als Rückreferenz.

(ha){3} matcht genau „hahaha". Den gematchten Inhalt einer Gruppe kannst du in sed als \1, \2 usw. wiederverwenden:

# Datum von JJJJ-MM nach MM.JJJJ umformen
sed -E 's/([0-9]{4})-([0-9]{2})/\2.\1/' datei.txt

Hier speichert Gruppe 1 das Jahr, Gruppe 2 den Monat. Im Ersetzungsteil kehrt \2.\1 die Reihenfolge um.

Der Alternations-Operator | entspricht einem logischen Oder: (Fehler|Warning|Critical) matcht eine der drei Zeichenfolgen. In ERE (grep -E) schreibst du den Operator als nacktes |. In BRE (Basic Regular Expressions) müssen sowohl die Klammern als auch das Pipe-Zeichen escaped werden: \(Fehler\|Warning\).

grep '\(Fehler\|Warning\)' syslog  # BRE

Lookahead & Lookbehind

Lookaheads und Lookbehinds sind sogenannte Zero-Width-Assertions — sie stellen eine Bedingung an das, was vor oder nach dem Hauptmuster steht, ohne diesen Kontext selbst zu matchen oder zu verbrauchen. Sie sind ein PCRE-Feature und brauchen deshalb in grep das -P-Flag.

AssertionSyntaxBedeutung
Lookahead positiv(?=…)Danach muss stehen …
Lookahead negativ(?!…)Danach darf nicht stehen …
Lookbehind positiv(?<=…)Davor muss stehen …
Lookbehind negativ(?<!…)Davor darf nicht stehen …

Praktisches Beispiel: Du willst alle Portnummern aus einer Nginx-Config extrahieren, aber nur die, die vor : in einer listen-Direktive stehen:

grep -oP '(?<=listen )\d+' nginx.conf

Der Lookbehind (?<=listen ) stellt sicher, dass nur Zahlen nach dem Wort „listen " gematcht werden — das Wort selbst erscheint nicht im Treffer.

Ein negativer Lookahead: Alle Zeilen aus einer .env-Datei finden, die keinen #-Kommentar enthalten:

grep -P '^(?!#)' .env

Ohne -P würden diese Muster nicht funktionieren — grep -E ignoriert oder falsch-interpretiert die Lookahead-Syntax.

Falls du diese Muster gerade aufbaust: Der Regex-Tester in der Codeschnipsel-Toolbox lässt dich Lookaheads interaktiv ausprobieren, bevor du sie in ein Skript packst.

Regex in der Shell: grep & sed

Das ist der Teil, der im Alltag am meisten zählt. grep und sed sind die beiden Tools, mit denen Regex in der Shell lebendig wird.

BRE vs. ERE ist der erste Unterschied, den du kennen musst: grep verwendet standardmäßig Basic Regular Expressions (BRE). In BRE müssen Klammern, Klammern für Quantoren und das Pipe-Zeichen escaped werden: \(, \{, \|. Mit dem Flag -E wechselst du zu Extended Regular Expressions (ERE), wo diese Zeichen ohne Backslash arbeiten — was Muster deutlich lesbarer macht. sed verhält sich wie grep: ohne -E ist es BRE, mit -E ERE.

# BRE: Klammern müssen escaped werden
grep '\(Fehler\|Warning\)' syslog

# ERE: sauberer, ohne Escaping der Struktur
grep -E '(Fehler|Warning)' syslog

Jetzt konkrete Anwendungsfälle:

# deutsche Postleitzahlen validieren (genau 5 Ziffern, ganze Zeile)
grep -E '^[0-9]{5}$' plz.txt

# E-Mail-Adressen aus einer Datei extrahieren (-o gibt nur den Match zurück)
grep -oE '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}' mail.txt

# IP-Adressen aus einem Nginx-Access-Log filtern
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort -u

# Zeilen mit HTTP-Fehlercode 4xx oder 5xx aus dem Log
grep -E ' [45][0-9]{2} ' access.log

# Log-Zeilen mit Fehler-Stufen, egal ob groß oder klein geschrieben
grep -iE 'error|warning|critical' syslog  # -i ignoriert Groß-/Kleinschreibung

# Datumsformat JJJJ-MM-DD zu DD.MM.JJJJ umschreiben
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/g' datei.txt

# alle Kommentarzeilen entfernen (Zeilen, die mit # beginnen)
sed -E '/^[[:space:]]*#/d' config.ini

# Leerzeilen am Anfang und Ende trimmen
sed -E '/^[[:space:]]*$/d' datei.txt

Einige Hinweise zu diesen Beispielen: Beim PLZ-Muster sind die Anker ^ und $ entscheidend. Ohne sie matcht [0-9]{5} fünf Ziffern an beliebiger Stelle einer Zeile — also auch innerhalb von PLZ: 12345-6789 oder einer langen Bestellnummer. Erst ^…$ erzwingt, dass die ganze Zeile aus genau fünf Ziffern besteht. Das E-Mail-Muster ist eine praktikable Näherung, keine RFC-konforme Validierung — eine solche ist in Regex kaum darstellbar. Die IP-Adressen matcht das Muster syntaktisch, nicht semantisch (255.255.255.255 matcht, 999.999.999.999 leider auch). Für semantisch korrekte IP-Validierung braucht es entweder einen langen Alternations-Baum oder eine Skriptsprache mit Netz-Libraries.

Wer sed auf macOS nutzt: BSD sed und GNU sed unterscheiden sich in einigen Flags. sed -E funktioniert auf beiden; -i (in-place) braucht auf macOS ein Suffix-Argument: sed -i '' 's/…/…/' datei.txt.

Häufige Sonderzeichen escapen

Wenn du nach einem Literal-Metazeichen suchen willst, muss es escaped werden. Hier die häufigsten Fälle:

SonderzeichenEscapedMatcht
Punkt\.Einen echten .
Sternchen\*Einen echten *
Plus\+Einen echten +
Fragezeichen\?Einen echten ?
Klammern\( \)Echte Klammern in ERE — Achtung: in BRE ist \(…\) der Gruppierungs-Operator, ( ) sind dort literal
Eckige Klammern\[Eine echte [
Geschw. Klammern\{Eine echte {
Pipe\\|Ein echtes | (in ERE; in BRE ist | bereits literal)
Backslash\\Einen echten \
Dach\^Einen echten ^
Dollar\$Einen echten $

Ein typischer Stolperer: grep '1.0' matcht auch „1x0" oder „100", weil . ein Metazeichen ist. Gemeint war wahrscheinlich grep '1\.0'. Dieser Fehler ist in Log-Analyse-Skripten häufig und liefert stille Falsch-Treffer.

Regex testen

Bevor ein Muster in ein Skript wandert, lohnt es sich, es interaktiv zu testen. Der Regex-Tester in der Codeschnipsel-Toolbox zeigt dir sofort, was dein Muster matcht, wie Gruppen aufgeteilt werden und ob das Verhalten in ERE und PCRE übereinstimmt — das spart Debugging-Zeit erheblich.

Für die Kommandozeile gibt es außerdem grep --color=always, das Matches direkt im Terminal einfärbt — nützlich, um beim Entwickeln eines Musters schnell zu sehen, was getroffen wird:

grep --color=always -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' access.log | head -20

Und grep -c zählt matching Zeilen statt sie auszugeben — schnell um zu prüfen, ob ein Muster überhaupt etwas findet:

grep -cE '^[0-9]{5}$' plz.txt

Häufige Fehler & Regex-Fallen

Zwei Fehlerquellen kosten in der Praxis am meisten Zeit. Erstens der nicht-escapte Punkt: . matcht jedes Zeichen, nicht nur den literalen Punkt. Ein Muster wie 192.168.0.1 matcht also auch 192x168y0z1 — wer eine IP literal meint, escapt jeden Punkt zu \.. Verwandt ist der zu gierige .*: Es frisst standardmäßig so viel wie möglich, weshalb <.*> über mehrere Tags hinweg matcht statt nur über eins.

Zweitens catastrophic backtracking: Verschachtelte Quantoren wie (a+)+ zwingen die Regex-Engine bei langen, nicht-matchenden Eingaben in eine exponentielle Anzahl von Rückverfolgungs-Versuchen. Lässt du grep -P '(a+)+$' auf eine Zeile aus vielen a ohne abschließendes passendes Zeichen los (printf 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!' | grep -P '(a+)+$'), kann der Prozess sekundenlang bis quasi-endlos hängen. Solche Muster lassen sich fast immer entschärfen — hier reicht ein simples a+$.

Cheat Sheet

Ein kompakter Überblick aller wichtigen Regex-Elemente — zum Bookmarken.

Metazeichen

ZeichenBedeutung
.Beliebiges Zeichen (außer \n)
^Zeilenanfang
$Zeilenende
|Alternation (oder) — in ERE |, in BRE \\|
\Escape-Zeichen

Zeichenklassen

MusterBedeutung
[abc]a, b oder c
[a-z]Kleinbuchstabe
[0-9]Ziffer (ERE-portabel)
[^abc]Nicht a, b oder c
[[:digit:]]Ziffer (POSIX-Klasse)
[[:alpha:]]Buchstabe (POSIX-Klasse)
[[:space:]]Whitespace (POSIX-Klasse)
\d \w \sZiffer / Wortzeichen / Whitespace (nur PCRE / grep -P)

Quantoren

QuantorBedeutung
*0 oder mehr (greedy)
+1 oder mehr (greedy)
?0 oder 1 (optional)
{n}Genau n Mal
{n,m}n bis m Mal
*? +?Lazy-Varianten (nur PCRE)

Anker

AnkerBedeutung
^Zeilenanfang
$Zeilenende
\bWortgrenze (PCRE / grep -P)

Gruppen & Lookaheads

SyntaxBedeutung
(…)Capture Group
\1 \2Rückreferenz (in Ersetzungen)
(?=…)Positiver Lookahead (PCRE)
(?!…)Negativer Lookahead (PCRE)
(?<=…)Positiver Lookbehind (PCRE)
(?<!…)Negativer Lookbehind (PCRE)

Wichtige Flags

FlagWirkung
grep -EERE aktivieren (kein Escaping von + ? { } ( ) |)
grep -PPCRE aktivieren (\d \w \b Lookaheads)
grep -oNur den Match ausgeben, nicht die ganze Zeile
grep -iGroß-/Kleinschreibung ignorieren
sed -EERE in sed aktivieren

Fazit

Regex ist kein monolithisches Konzept — es gibt POSIX BRE, POSIX ERE und PCRE, und sie unterscheiden sich in Details, die in der Shell-Praxis zählen: welche Metazeichen escaped werden müssen, ob \d verfügbar ist, ob Lookaheads funktionieren. Den Überblick zu behalten ist die halbe Miete. Die andere Hälfte ist Übung an echten Daten.

Ein nützlicher erster Schritt: Nimm eine deiner Log-Dateien oder CSV-Exporte und probiere die Muster aus diesem Artikel im Regex-Tester in der Codeschnipsel-Toolbox aus — dort siehst du sofort, ob das Muster das tut, was du denkst.

Den Rest des Shell-Scripting-Fundamentals findest du im Shell-Scripting-Hub — von Shellarten über Globbing bis zu weiteren Praxis-Tutorials.

Ähnliche Artikel

Linux & Shell

Bash Globbing & Wildcards: Dateimuster in der Shell

7 Min. Lesezeit
Linux & Shell

Shell-Arten im Vergleich: Bash, Zsh, Fish & Co.

9 Min. Lesezeit
Webentwicklung & Technik

Apache HTTP Server 2026: Der Klassiker im modernen Einsatz

10 Min. Lesezeit

Möchtest du auch so einen Blog?

Ich entwickle moderne, SEO-optimierte Websites und Blogs mit Next.js, React und Tailwind CSS.