Der Befehl find ist das Schweizer Taschenmesser für die rekursive Suche im Dateisystem. Er durchwandert Verzeichnisbäume und filtert Einträge nach beliebigen Kriterien — Name, Typ, Größe, Zeitstempel, Berechtigungen, Eigentümer — und kann auf jeden Treffer eine Aktion anwenden: ausgeben, löschen oder ein beliebiges Kommando ausführen. Wer find beherrscht, ersetzt Dutzende kleiner Skripte durch einen Einzeiler.
Was find macht
find durchsucht ein oder mehrere Verzeichnisse rekursiv und gibt jene Einträge aus, die alle angegebenen Kriterien erfüllen. Im Unterschied zu ls, das nur den Inhalt einer Ebene zeigt, geht find beliebig tief — und im Unterschied zu locate, das eine vorgebaute Datenbank abfragt, läuft find immer live über das Dateisystem und sieht den aktuellen Stand.
Gesucht wird nicht nur nach Dateinamen: find versteht Dateityp (regulär, Verzeichnis, Symlink, Gerät), Größe, Modifikations-, Zugriffs- und Statuszeit, Berechtigungs-Bits, Eigentümer und Gruppe, Inode-Nummer und einiges mehr. Auf jeden Treffer lassen sich Aktionen anwenden — vom einfachen -print über -delete bis hin zu beliebigen externen Kommandos via -exec.
find . -name '*.md'Syntax-Anatomie
Die Aufrufstruktur von find ist einfacher, als sie auf den ersten Blick wirkt:
find [Pfade] [Tests] [Aktionen]Alle drei Teile sind optional — ein nacktes find ohne Argumente listet rekursiv alles ab dem aktuellen Verzeichnis. Werden keine Pfade angegeben, gilt .; wird keine Aktion angegeben, gilt -print. Die Reihenfolge ist allerdings entscheidend: Pfade stehen immer vor den Tests, und Tests werden in der Reihenfolge ausgewertet, in der sie auf der Kommandozeile stehen.
find /var/log -name 'syslog*' -print/var/log ist der Pfad, -name 'syslog*' der Test, -print die Aktion. Die Reihenfolge der Tests beeinflusst die Performance: günstige Tests wie -type zuerst, teurere wie -name danach. find wertet die Ausdruckskette nämlich linkshändig aus — sobald ein AND-Glied falsch ist, brechen weitere Tests ab.
Wichtigste Tests
Tests sind die Filter, mit denen du den Suchraum einschränkst. Sie geben pro Eintrag wahr oder falsch zurück und werden mit logischen Operatoren verknüpft. Diese Tabelle deckt die im Alltag wichtigsten ab.
| Test | Wirkung | Beispiel |
|---|---|---|
-name MUSTER | Basename matcht Glob, case-sensitive | -name '*.log' |
-iname MUSTER | Wie -name, aber case-insensitive | -iname 'readme*' |
-type X | Dateityp: f/d/l/c/b/s/p | -type d für Verzeichnisse |
-size N | Größe in Blöcken (c=Bytes, k/M/G) | -size +10M, -size -1k, -size 100c |
-mtime N | Mod-Zeit in 24h-Blöcken | -mtime -7 (jünger als 7 Tage) |
-mmin N | Mod-Zeit in Minuten | -mmin -60 (letzte Stunde) |
-atime N / -amin N | Zugriffszeit | -atime +30 (älter als 30 Tage) |
-ctime N / -cmin N | Statusänderungszeit | -ctime -1 |
-newer DATEI | Neuer als DATEI | -newer /tmp/marker |
-perm MODE | Berechtigungs-Match | -perm -4000 (SUID gesetzt) |
-user NAME | Eigentümer | -user michael |
-group NAME | Gruppe | -group wheel |
-empty | Leere Datei oder leeres Verzeichnis | -empty |
-maxdepth N | Maximale Tiefe (0 = nur Startpfad) | -maxdepth 2 |
-mindepth N | Minimale Tiefe | -mindepth 1 (Startpfad ausschließen) |
-path MUSTER | Vollpfad matcht Glob | -path '*/node_modules/*' |
-regex MUSTER | Vollpfad matcht Regex | -regex '.*\.[ch]' |
Bei den Zeit-Tests bedeutet +N „älter als”, -N „jünger als” und N ohne Vorzeichen exakt im N-ten Block. Bei -size analog: +10M heißt „größer als 10 MB”, -1k „kleiner als 1 KB”, 100c „genau 100 Bytes”. Das c-Suffix steht dabei für „characters” (Bytes), nicht für ein anderes Größenpräfix.
Logische Operatoren
Mehrere Tests werden standardmäßig per AND verknüpft — find . -type f -name '*.log' findet reguläre Dateien, deren Name auf .log endet. Für komplexere Bedingungen gibt es explizite Operatoren:
| Operator | Bedeutung | Beispiel |
|---|---|---|
-a / (Leerzeichen) | AND (Default) | -type f -name '*.tmp' |
-o | OR | -name '*.log' -o -name '*.tmp' |
! / -not | Negation | ! -name '*.bak' |
\( \) | Gruppierung (in Shell quoten) | \( -name a -o -name b \) |
Klammern müssen vor der Shell maskiert werden — entweder als \( und \) oder in einfachen Quotes '('. Sonst interpretiert die Shell sie als Subshell-Syntax. Wichtig: AND bindet stärker als OR. Wer -type f -name a -o -name b schreibt, bekommt alle Dateien b zurück, nicht nur reguläre — gewollt ist meist \( -name a -o -name b \) -type f.
find . \( -name '*.log' -o -name '*.tmp' \) -type ffind . -type f ! -name '*.bak'Aktionen
Aktionen sind das, was find mit jedem Treffer tun soll. Ohne explizite Aktion gilt -print. Die Aktion entscheidet auch über den Rückgabewert des Ausdrucks.
| Aktion | Wirkung |
|---|---|
-print | Pfad mit Newline ausgeben (Default) |
-print0 | Pfad mit NUL-Byte trennen — sicher für xargs -0 |
-delete | Treffer löschen (Verzeichnisse müssen leer sein) |
-exec CMD {} \; | CMD einmal pro Treffer ausführen |
-exec CMD {} + | CMD mit gebündelten Treffern ausführen (schneller) |
-execdir CMD {} \; | Wie -exec, aber im Verzeichnis des Treffers |
-prune | Aktuellen Pfad nicht weiter absteigen |
-ls | ls -dils-ähnliche Ausgabe |
Der Platzhalter {} wird durch den aktuellen Pfad ersetzt. Das abschließende \; (oder ';') terminiert das -exec-Kommando — es muss von der Shell maskiert werden. Die Variante + statt \; ist fast immer schneller: find ... -exec rm {} + ruft rm einmal mit allen Treffern als Argumenten auf, während \; für jeden Treffer einen eigenen Prozess startet.
find . -type f -name '*.sh' -exec chmod +x {} \;find . -type f -name '*.tmp' -exec rm {} +find und xargs
Die klassische Pipeline für „finde X und tu Y damit” geht über xargs. Der Vorteil: xargs parallelisiert (-P), bündelt Argumente effizient und kann mit Argument-Position arbeiten. Die korrekte Form nutzt immer -print0 und -0:
find . -name '*.tmp' -print0 | xargs -0 rmWarum nicht ohne -print0/-0? Weil Dateinamen Leerzeichen, Tabulatoren oder gar Newlines enthalten dürfen. xargs ohne -0 zerlegt die Eingabe an Whitespace und macht aus mein dokument.tmp zwei Argumente — rm versucht dann, mein und dokument.tmp zu löschen, beide nicht existent. Mit -print0/-0 werden Pfade per NUL-Byte getrennt, dem einzigen Zeichen, das in Dateinamen verboten ist.
find . -name '*.png' -print0 | xargs -0 -I FILE cp FILE backup/-I FILE ersetzt das Token FILE im Kommando durch jedes einzelne Argument — nützlich, wenn der Treffer nicht ganz ans Ende der Argumentliste gehört. Für reine Bündelung ohne Position-Magic reicht xargs -0 oder die -exec ... +-Form direkt in find.
Praxis-Patterns
Eine Sammlung der Einzeiler, die im Alltag immer wieder gebraucht werden. Jeder davon ist eine Vorlage — anpassen und anwenden.
find / -type f -size +1G 2>/dev/null2>/dev/null schluckt die „Permission denied”-Meldungen, die unprivilegierte Suchen unter / produzieren. Auf Servern mit voller Platte ist das der erste Reflex.
Praktisch nach einem Merge oder Crash, um zu sehen, was sich frisch verändert hat. -mtime -7 bedeutet „mtime jünger als 7 mal 24 Stunden” — Stolperfalle: das ist nicht „seit dem 7. Kalendertag rückwärts”, sondern rollierend in 24-Stunden-Blöcken (siehe Stolperfallen weiter unten).
find . -type f -mtime -7find . -type d -empty-empty matcht sowohl leere Dateien als auch leere Verzeichnisse — mit -type d schränkst du gezielt auf verwaiste Ordner ein. Klassischer Cleanup-Schritt nach Refactorings, in denen Verzeichnisse leer zurückbleiben und nicht mehr gebraucht werden.
find / -perm -4000 -type f 2>/dev/null-perm -4000 matcht alle Dateien, bei denen das SUID-Bit gesetzt ist — egal welche anderen Bits. Das Minus vor dem Modus heißt „mindestens diese Bits”. Solche Dateien laufen mit den Rechten des Eigentümers und sind ein klassisches Audit-Ziel.
Klassischer Cron-Job zum Aufräumen alter Logfiles: +30 heißt „älter als 30 mal 24 Stunden”. -delete muss am Ende stehen, sonst werden auch geprunte Verzeichnisse mit gelöscht — und vor dem ersten Einsatz sicherheitshalber ohne -delete testen, um die Trefferliste zu prüfen.
find /var/log -type f -name '*.log' -mtime +30 -deletefind . -type f -name '*.sh' -exec chmod 755 {} +Nach dem Auschecken eines Repos auf einem fremden System fehlen den Skripten oft die x-Bits. Das + am Ende sammelt alle Treffer und ruft chmod mit gebündelten Argumenten auf — deutlich schneller als \;, das pro Datei einen eigenen Prozess startet.
find . \( -name .git -o -name node_modules \) -prune -o -type f -print-prune muss vor der eigentlichen Suchbedingung stehen und mit -o verbunden werden — sonst betritt find den Ordner trotzdem. Das Pattern liest sich als: „wenn der Eintrag .git oder node_modules heißt, beschneide; ansonsten gib reguläre Dateien aus”.
fd als moderne Alternative
Wer find zu sperrig findet, schaut sich fd an: ein in Rust geschriebener Such-Befehl mit sinnvollen Defaults. fd PATTERN durchsucht das aktuelle Verzeichnis nach Treffern, respektiert .gitignore, blendet Dotfiles aus und nutzt Regex statt Globs.
fd '\.log$'fd ist deutlich schneller als find, hat farbige Ausgabe und parallelisiert von sich aus. Für interaktive Suchen am eigenen Rechner ist es ein klarer Gewinn. Aber: fd ist nicht überall installiert. Auf einem fremden Server, in Container-Images oder in portablen Skripten gibt es nur find — und genau deshalb gehört find ins Standard-Repertoire jedes Shell-Nutzers.
Stolperfallen
xargs ohne -0 zerlegt Dateinamen mit Whitespace
Die Pipeline find . -name ‘*.log’ | xargs rm funktioniert nur, solange kein Treffer Leerzeichen, Tabulatoren oder Newlines im Namen enthält. Sobald eine Datei fehler protokoll.log heißt, zerlegt xargs sie in zwei Argumente — und rm meldet „No such file or directory” für beide. Korrekt ist immer find … -print0 | xargs -0 … oder die -exec … +-Form direkt in find.
-exec mit \; startet einen Prozess pro Datei
find . -name ‘*.txt’ -exec wc -l {} ; ruft wc für jede einzelne Datei neu auf — bei 10000 Treffern sind das 10000 Forks. Die Variante mit + statt ; bündelt die Argumente und ruft wc nur ein paar Mal auf, was Größenordnungen schneller ist. Faustregel: + nutzen, wann immer das Kommando mehrere Argumente verträgt; ; nur, wenn das Kommando exakt ein Argument erwartet oder pro Datei einen eigenen Aufruf braucht.
-mtime rechnet in 24h-Blöcken, nicht in Kalendertagen
-mtime -1 bedeutet nicht „heute geändert”, sondern „in den letzten 24 Stunden geändert”. Eine Datei, die gestern Abend um 22:00 angefasst wurde, ist heute Morgen um 09:00 also „1 Tag alt” und wird von -mtime -1 nicht mehr getroffen. Für minutengenaue Suchen gibt es -mmin, für Kalendertag-Logik braucht es Tricks mit -newer und einer Marker-Datei: touch -d ‘today 00:00’ /tmp/marker; find . -newer /tmp/marker.
-name matcht nur den Basename, nicht den Pfad
find . -name ‘src/*.ts’ liefert keine Treffer — -name matcht ausschließlich den letzten Pfadbestandteil, nicht das Verzeichnis davor. Wer den Vollpfad filtern will, nimmt -path: find . -path ’*/src/*.ts’. Bei -path matcht der Stern allerdings auch Slashes — ein wichtiger Unterschied zu Shell-Globs.
Reihenfolge der Tests beeinflusst Performance
find wertet Ausdrücke linkshändig aus und kürzt AND-Verkettungen ab, sobald ein Glied falsch ist. Steht der teure Test (-name mit komplexem Glob, -regex) vorne, läuft er für jeden einzelnen Eintrag. Steht der billige Test (-type f) vorne, fallen Verzeichnisse sofort raus und der teure Test läuft nur auf reguläre Dateien. Faustregel: -type, -maxdepth und -prune immer nach vorne.
-prune muss vor anderen Tests stehen
-prune wirkt nur, wenn es ausgewertet wird, bevor find in das Verzeichnis hinabsteigt. Schreibt man find . -name ‘*.js’ -o -name node_modules -prune, hat find den Ordner längst betreten. Die robuste Form ist find . -name node_modules -prune -o -name ‘*.js’ -print: Erst die Prune-Bedingung, dann der eigentliche Filter, und das abschließende -print ist Pflicht — ohne explizites -print liefert die Konstruktion auch das geprunte Verzeichnis selbst zurück.
Symlinks werden per Default nicht gefolgt
Ohne weitere Flags folgt find symbolischen Links nicht — sie werden als Treffer ausgegeben (mit -type l), aber ihr Ziel wird nicht abgestiegen. Mit -L (vor den Pfadargumenten!) folgt find jedem Link. Vorsicht bei zirkulären Links: ohne -L kein Problem, mit -L erkennt GNU-find Schleifen automatisch, andere Implementierungen aber nicht.
macOS hat BSD-find, nicht GNU-find
Auf macOS und FreeBSD ist find eine andere Implementierung. Einige Optionen funktionieren anders oder fehlen ganz: -printf gibt es nicht, -regex nutzt eine andere Regex-Syntax, -mount heißt anderswo -xdev. Wer Skripte zwischen Linux und macOS portieren will, beschränkt sich auf POSIX-Optionen oder installiert GNU-findutils (brew install findutils liefert gfind).
Weiterführende Ressourcen
Externe Quellen
- find(1) – Manpage (man7.org) — Vollständige Optionsreferenz von GNU-
find - GNU Findutils Manual — Ausführliches Handbuch mit Beispielen, inklusive
xargsundlocate - Arch Wiki: Core utilities — Kurze Notizen zu
findund verwandten Werkzeugen - fd – GitHub-Repo — Moderne, schnellere Alternative zu
find - IBM Developer: Linux find command — Tutorial mit vielen Praxis-Beispielen
Verwandte Artikel
- ls – Verzeichnisinhalte auflisten — Einzeilige Übersicht statt rekursiver Suche
- locate – Dateien per Index finden — Schneller Datenbank-Lookup als Ergänzung zu
find - xargs – Argumente an Kommandos übergeben — Klassischer Partner für
find -print0 - Berechtigungen — Modus-Bits für
-permverstehen - Verzeichnisstruktur (FHS) — Wo welche Dateien liegen, bevor du sie suchst