grep ist eines der ältesten und meistgenutzten Unix-Werkzeuge überhaupt — der Standard, wenn es darum geht, Zeilen zu finden, die ein bestimmtes Muster enthalten. Aus einer Datei, aus einem ganzen Verzeichnisbaum oder mitten aus einer Pipeline. Wer grep beherrscht, durchsucht Codebasen schneller als jede IDE, filtert Logs auf der Konsole und verkettet beliebige Kommandos zu robusten Pipelines. Der Name stammt aus dem ed-Editor: g/re/p — global / regular expression / print.
Was grep macht
grep liest Zeilen aus Dateien oder von der Standardeingabe und gibt jene aus, die ein angegebenes Pattern enthalten. Im einfachsten Fall ist das Pattern ein literaler String, im allgemeinen Fall ein regulärer Ausdruck. Per Default geht jeder Treffer als komplette Zeile auf die Standardausgabe — alles andere lässt sich über Optionen steuern.
grep ist vermutlich das meistgenutzte Filter-Tool der Unix-Welt. Es taucht in Tutorials, Skripten und Live-Debugging-Sessions gleichermassen auf. Der Grund: Die Aufgabe „zeig mir die Zeilen, in denen X vorkommt” ist allgegenwärtig — in Logs, in Konfigurationsdateien, in Quellcode, in der Ausgabe anderer Kommandos.
grep ERROR /var/log/syslogps aux | grep nginxWichtigste Optionen
grep hat viele Schalter, aber nur eine Handvoll davon braucht man im Alltag wirklich oft. Diese Tabelle deckt die wichtigsten ab — nach Häufigkeit gewichtet.
| Option | Wirkung |
|---|---|
-i | Case-insensitive Match |
-v | Negation: Zeilen ausgeben, die nicht matchen |
-n | Zeilennummer mit ausgeben |
-c | Nur die Anzahl der Treffer pro Datei |
-l | Nur Dateinamen, die mindestens einen Treffer haben |
-L | Nur Dateinamen ohne Treffer |
-r / -R | Rekursiv durchsuchen (-R folgt Symlinks) |
-w | Nur ganze Wörter matchen (Wortgrenzen) |
-x | Nur ganze Zeilen matchen |
-E | Extended Regex (ERE) statt BRE |
-F | Feste Strings, kein Regex |
-P | Perl-kompatible Regex (PCRE) |
-o | Nur den matchenden Teil ausgeben, nicht die ganze Zeile |
-A N | N Zeilen nach dem Treffer mitausgeben |
-B N | N Zeilen vor dem Treffer mitausgeben |
-C N | N Zeilen um den Treffer (Kontext) |
-q | Quiet — kein Output, nur Exit-Code |
--include='GLOB' | Nur passende Dateinamen durchsuchen |
--exclude='GLOB' | Passende Dateinamen überspringen |
--exclude-dir=DIR | Verzeichnisse vom rekursiven Abstieg ausschließen |
-h / -H | Dateinamen-Header unterdrücken / erzwingen |
-z | NUL-getrennte Records statt Newlines |
--color=auto | Treffer farbig hervorheben |
-q ist besonders nützlich in Skripten: grep -q PATTERN file && echo "gefunden" prüft auf Existenz, ohne irgendeinen Output zu produzieren. Der Exit-Code von grep ist 0 bei mindestens einem Treffer, 1 ohne Treffer und 2 bei einem echten Fehler — perfekt für if-Bedingungen.
Regex-Varianten: BRE, ERE, PCRE
grep versteht drei verschiedene Regex-Dialekte. Welcher gerade gilt, hängt von der Option ab — und das ist eine der häufigsten Fehlerquellen.
| Dialekt | Aktivierung | Besonderheiten |
|---|---|---|
| BRE (Basic) | Default, ohne Schalter | +, ?, {}, (), ` |
| ERE (Extended) | -E oder egrep | +, ?, (), ` |
| PCRE (Perl) | -P | Lookahead/Lookbehind, non-greedy *?, \d, \b |
Mit BRE schreibt man grep 'foo\+' für „foo gefolgt von einem oder mehr Zeichen”, mit ERE einfach grep -E 'foo+'. Aus diesem Grund nehmen viele Leute heute fast immer -E — die Syntax ist näher an dem, was man aus anderen Regex-Engines kennt. PCRE mit -P ist nochmal mächtiger (Lookbehind, non-greedy, Named Groups), aber nicht überall verfügbar — auf BSD-Systemen wie macOS fehlt der Schalter im Standard-grep.
Anker und Klassen funktionieren in allen Dialekten gleich:
| Konstrukt | Bedeutung |
|---|---|
^ | Zeilenanfang |
$ | Zeilenende |
\b | Wortgrenze (PCRE; in BRE/ERE meist \< und \>) |
[a-z] | Zeichenklasse |
[^abc] | Negierte Klasse |
[[:digit:]] | POSIX-Klasse für Ziffern |
[[:alpha:]] | POSIX-Klasse für Buchstaben |
[[:space:]] | POSIX-Klasse für Whitespace |
POSIX-Klassen sind locale-unabhängig und meist die beste Wahl für portable Skripte. [a-z] matcht je nach LC_COLLATE mal nur ASCII, mal auch Umlaute — [[:lower:]] ist eindeutiger.
grep -E '\.(log|tmp|bak)$' files.txtgrep -P '(?<=user=)\w+' access.logMehrere Pattern
Soll grep nach mehreren Mustern gleichzeitig suchen, gibt es drei Wege. Jeder hat seinen Platz.
grep -e ERROR -e WARN -e FATAL sysloggrep -f patterns.txt access.loggrep -E 'ERROR|WARN|FATAL' syslogIn BRE schreibt sich die Alternation \| (mit Backslash), in ERE direkt |. Wer eine wirklich große Liste von Patterns hat — etwa zehntausende IPs aus einer Sperrliste — sollte grep -F -f liste.txt nehmen: Mit -F (fixed strings) ist die Suche um Größenordnungen schneller, weil keine Regex-Engine bemüht wird.
Recursive Search mit -r
Die Killer-Anwendung von grep im Entwickleralltag: rekursive Suche durch ganze Codebasen. grep -r 'TODO' src/ findet jeden TODO-Kommentar im Verzeichnisbaum unterhalb von src/.
grep -rn 'TODO' src/-n ergänzt Zeilennummern, -r macht es rekursiv. Aber in einem realen Projekt gibt es Müll, den man nicht durchsuchen will: node_modules, .git, dist, build. Hier kommen --include und --exclude-dir ins Spiel.
| Combo | Wirkung |
|---|---|
grep -rn 'X' . | Alles rekursiv ab . |
grep -rn --include='*.ts' 'X' . | Nur TypeScript-Dateien |
grep -rn --exclude-dir=node_modules 'X' . | node_modules überspringen |
grep -rn --exclude='*.min.js' 'X' . | Minified-Code ausschließen |
grep -rln 'X' . | Nur Dateinamen mit Treffern |
grep -rwn 'foo' . | Nur ganzes Wort foo, kein foobar |
grep -rn --include='*.ts' --exclude-dir=node_modules 'useEffect' src/--include und --exclude arbeiten mit Glob-Patterns, nicht mit Regex. *.ts ist ein Glob, \.ts$ wäre Regex. Beide Optionen lassen sich beliebig oft angeben.
Context: -A, -B, -C
Einer der größten Grep-Klassiker: Bei einem Treffer auch die umliegenden Zeilen ausgeben. Das ist beim Lesen von Logs Gold wert — der Fehler steht in einer Zeile, der Kontext, warum es krachte, in den drei Zeilen davor.
| Option | Bedeutung |
|---|---|
-A N | N Zeilen nach (after) dem Treffer |
-B N | N Zeilen vor (before) dem Treffer |
-C N | N Zeilen um den Treffer (context, beidseitig) |
grep -B 3 'ERROR' sysloggrep -C 5 'connection refused' /var/log/nginx/error.logTreffer-Blöcke werden mit -- voneinander getrennt, sobald mehrere Matches im selben Output landen. Wer den Separator stört, schaltet ihn mit --no-group-separator ab.
Word-Match -w und Line-Match -x
Mit -w matcht grep nur dann, wenn das Pattern an Wortgrenzen beginnt und endet. grep -w foo findet foo in foo bar, aber nicht in foobar. Das ist genau das, was man bei Variablen- oder Funktionsnamen will.
grep -wn 'main' *.go-x geht noch weiter und matcht nur dann, wenn die gesamte Zeile dem Pattern entspricht — Teiltreffer in der Mitte der Zeile zählen nicht. Nützlich, wenn du in einer Liste prüfen willst, ob ein exakter Eintrag vorhanden ist.
grep -Fx 'admin' users.txt-F zusammen mit -x ist die robusteste Kombination für „ist dieser exakte String als ganze Zeile in der Datei?” — keine Regex-Sonderzeichen werden interpretiert.
grep vs. moderne Alternativen
Inzwischen gibt es eine ganze Reihe schnellerer und entwicklerfreundlicherer Such-Tools. Sie sind im Tagesgeschäft am eigenen Rechner oft die bessere Wahl — aber sie sind eben nicht überall installiert.
| Tool | Stärken | Schwächen |
|---|---|---|
grep | Überall vorinstalliert, POSIX-Standard | Langsam bei Codebasen, kein .gitignore |
ripgrep (rg) | Blitzschnell, .gitignore-aware, schöne Defaults | Muss installiert werden |
ag (silver searcher) | Schnell, .gitignore-aware | Vorgänger von rg, weniger gepflegt |
ack | Code-fokussiert, in Perl geschrieben | Langsamer als rg |
ripgrep ist heute der De-facto-Standard für Code-Suche am Entwickler-Rechner: schneller als grep, ignoriert per Default .gitignore-Einträge und Binärdateien, hat farbige Ausgabe out of the box. Für interaktives Arbeiten ein klarer Gewinn.
rg 'useEffect' --type tsAber: Auf einem fremden Server, in einem Container-Image oder im Recovery-Modus gibt es nur grep. Auch in CI-Skripten und portablen Shell-Tools ist grep die sichere Wahl. Deshalb gehört es weiterhin zum Pflichtrepertoire — selbst wenn du im Alltag rg bevorzugst.
Praxis-Patterns
Eine Sammlung der Einzeiler, die im Alltag immer wieder gebraucht werden.
grep -i error /var/log/syslog-i ignoriert Groß- und Kleinschreibung — nötig, weil Logs mal ERROR, mal Error, mal error schreiben. Ohne -i würden zwei von drei Schreibweisen durchrutschen.
grep '"id"' response.jsonFür saubere JSON-Verarbeitung ist jq natürlich besser — aber für ein schnelles „existiert das Feld überhaupt?” reicht grep allemal.
grep -v '^$' config.txt^$ ist der reguläre Ausdruck für eine komplett leere Zeile (Anfang direkt gefolgt von Ende), -v invertiert die Auswahl. Wer Zeilen aus Whitespace ebenfalls verwerfen will, nimmt '^[[:space:]]*$'.
grep -c 'WARN' app.log-c zählt Treffer-Zeilen, nicht Treffer — kommt WARN zweimal in einer Zeile vor, wird trotzdem nur eins gezählt. Für eine echte Vorkommens-Zählung lieber grep -o pattern file | wc -l verwenden.
grep -L 'SPDX-License-Identifier' *.c-L ist das Gegenstück zu -l: listet Dateinamen, in denen das Pattern nicht vorkommt. Ideal für Compliance-Checks oder um vergessene Marker (Lizenzheader, Copyright-Notice, Test-IDs) aufzuspüren.
ps aux | grep -v grep | grep nginxKlassisches Problem: grep nginx findet sich selbst in der Prozessliste, weil das Wort nginx im eigenen Kommando steht. Der grep -v grep-Filter davor wirft den eigenen Prozess raus. Eleganter ist pgrep nginx — aber wenn nur grep da ist, hilft dieser Trick.
grep -Ev '^(#|$)' /etc/ssh/sshd_configFiltert in einem Schritt sowohl Kommentar- als auch Leerzeilen heraus — übrig bleiben nur die effektiv aktiven Direktiven. Ideal, um in zugemüllten Configs (sshd, postfix, php.ini) den realen Zustand auf einen Blick zu sehen.
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log-o druckt nur das gematchte Teilstück, nicht die ganze Zeile — perfekt zum Extrahieren. Das Pattern ist bewusst grob: es matcht auch ungültige IPs wie 999.999.999.999. Für strikte Validierung greift man zu awk oder perl, hier reicht es zum schnellen Aufzählen.
Stolperfallen
grep | grep -v grep ist ein Klassiker — pgrep ist besser
Die Pipeline ps aux | grep nginx matcht auch das grep nginx-Kommando selbst. Der Klassiker dagegen ist ps aux | grep -v grep | grep nginx oder der Trick ps aux | grep ‘[n]ginx’ — die Klammer matcht zwar das Zeichen n, aber das Pattern selbst enthält keinen literalen nginx-String. Beides funktioniert, aber für „läuft Prozess X?” ist pgrep nginx die saubere Antwort.
Punkt matcht alles — Punkt im Suchstring escapen
In Regex bedeutet . „ein beliebiges Zeichen”. grep ‘foo.bar’ matcht also foo bar, foo-bar, fooXbar — und eben auch foo.bar. Wer wirklich einen literalen Punkt sucht, schreibt grep ‘foo.bar’ oder benutzt grep -F ‘foo.bar’. Mit -z (NUL-getrennte Records) matcht . sogar Newlines.
Spezialzeichen ohne Quotes — die Shell expandiert
grep $variable file ist gefährlich: Wenn $variable Leerzeichen oder Glob-Zeichen enthält, zerlegt die Shell das Argument oder ersetzt es durch Dateinamen. grep “*.log” . findet Treffer für das Pattern star Punkt log — sofern keine Datei *.log heißt, sonst expandiert die Shell zu Dateinamen, die grep als Pattern frisst. Faustregel: Pattern immer in einfache Quotes setzen.
BRE und ERE verwirren — Plus ohne Backslash matcht literal
In BRE (default) ist + ein literales Plus-Zeichen, kein Quantifier. grep ‘foo+’ sucht also nach dem String foo+, nicht nach „foo gefolgt von einem oder mehr Zeichen”. Wer + als Quantifier will, nimmt -E (ERE) oder schreibt es in BRE als +. Dasselbe gilt für ?, {}, () und |. Im Zweifel -E nehmen.
PCRE -P ist nicht überall verfügbar
Auf macOS und FreeBSD ist grep die BSD-Implementierung — sie hat keinen -P-Schalter. Skripte mit Lookbehind oder non-greedy Patterns brechen dort ab. Lösungen: GNU-grep installieren (brew install grep, dann ggrep -P nutzen), das Pattern in ERE umschreiben, oder auf perl -ne bzw. ripgrep ausweichen. Bei portablen Skripten lieber gleich auf POSIX-ERE bleiben.
Color-Output bricht weitere Pipe-Stages
Mit —color=auto (oder per Alias erzwungen) fügt grep ANSI-Escape-Codes in den Output ein. Beim direkten Lesen am Terminal hübsch — beim Weiterverarbeiten in einer Pipeline desaströs: grep —color=always X | sort sortiert plötzlich nach ANSI-Sequenzen, und grep —color X | grep Y findet Y oft nicht, weil die Bytes mit Farbcodes durchschossen sind. In Skripten immer —color=never verwenden.
grep -r folgt Symlinks per Default nicht — -R schon
Kleines, aber gemeines Detail: -r (Kleinbuchstabe) folgt symbolischen Links nicht, -R (Großbuchstabe) tut es. Wer ein Verzeichnis untersucht, in dem ein Symlink auf das Elternverzeichnis zeigt, wird mit -R in eine Endlosschleife laufen. Im Zweifel -r nehmen — es ist die sicherere Default-Wahl. Wenn Symlinks bewusst gefolgt werden sollen, -R.
--include und --exclude sind Globs, kein Regex
Sehr häufiger Fehler: grep -r —include=’.ts$’ X . findet nichts, weil —include Glob-Patterns erwartet. Korrekt ist —include=‘*.ts’. Auch ohne Quotes droht Ärger — die Shell expandiert *.ts sonst zu den Dateinamen im aktuellen Verzeichnis, bevor grep sie überhaupt sieht. Pattern in Quotes, immer.
Locale beeinflusst [a-z] — Umlaute können mitmatchen
Die Klasse [a-z] ist nicht so portabel, wie sie aussieht. Je nach LC_COLLATE kann sie auch Umlaute, Akzente oder gar Großbuchstaben mitmatchen — die Sortierreihenfolge bestimmt, was zwischen a und z liegt. Robust ist die POSIX-Klasse [[:lower:]] oder das explizite Forcen der C-Locale: LC_ALL=C grep ‘[a-z]’ file. Skripte, die plattformübergreifend laufen müssen, sollten POSIX-Klassen bevorzugen.
Weiterführende Ressourcen
Externe Quellen
- grep(1) – Manpage (man7.org) — Vollständige Optionsreferenz von GNU-
grep - GNU Grep Manual — Offizielles Handbuch mit Beispielen und Regex-Erklärungen
- Arch Wiki: Core utilities — Übersicht über
grepund verwandte Werkzeuge - ripgrep – GitHub-Repo — Moderne, schnellere Alternative mit
.gitignore-Support - Regular-Expressions.info — Tiefer Einstieg in Regex-Konzepte über alle Engines hinweg
Verwandte Artikel
- sed – Stream-Editor für Text — Suchen und ersetzen statt nur finden
- awk – Feldorientierte Textverarbeitung — Spalten und Logik in einem Tool
- find – Dateien rekursiv suchen — Klassischer Partner: zuerst Dateien finden, dann mit
grepdurchsuchen - cut – Spalten und Felder extrahieren — Nach
grepSpalten ausschneiden - xargs – Argumente an Kommandos übergeben — Treffer von
grep -lweiterverarbeiten