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.

Bash Alle .md-Dateien im aktuellen Baum
find . -name '*.md'

Syntax-Anatomie

Die Aufrufstruktur von find ist einfacher, als sie auf den ersten Blick wirkt:

Bash Grundsyntax
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.

Bash Vollständige Form mit allen drei Teilen
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.

TestWirkungBeispiel
-name MUSTERBasename matcht Glob, case-sensitive-name '*.log'
-iname MUSTERWie -name, aber case-insensitive-iname 'readme*'
-type XDateityp: f/d/l/c/b/s/p-type d für Verzeichnisse
-size NGröße in Blöcken (c=Bytes, k/M/G)-size +10M, -size -1k, -size 100c
-mtime NMod-Zeit in 24h-Blöcken-mtime -7 (jünger als 7 Tage)
-mmin NMod-Zeit in Minuten-mmin -60 (letzte Stunde)
-atime N / -amin NZugriffszeit-atime +30 (älter als 30 Tage)
-ctime N / -cmin NStatusänderungszeit-ctime -1
-newer DATEINeuer als DATEI-newer /tmp/marker
-perm MODEBerechtigungs-Match-perm -4000 (SUID gesetzt)
-user NAMEEigentümer-user michael
-group NAMEGruppe-group wheel
-emptyLeere Datei oder leeres Verzeichnis-empty
-maxdepth NMaximale Tiefe (0 = nur Startpfad)-maxdepth 2
-mindepth NMinimale Tiefe-mindepth 1 (Startpfad ausschließen)
-path MUSTERVollpfad matcht Glob-path '*/node_modules/*'
-regex MUSTERVollpfad 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:

OperatorBedeutungBeispiel
-a / (Leerzeichen)AND (Default)-type f -name '*.tmp'
-oOR-name '*.log' -o -name '*.tmp'
! / -notNegation! -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.

Bash Mehrere Endungen per OR
find . \( -name '*.log' -o -name '*.tmp' \) -type f
Bash Alles außer Backups
find . -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.

AktionWirkung
-printPfad mit Newline ausgeben (Default)
-print0Pfad mit NUL-Byte trennen — sicher für xargs -0
-deleteTreffer 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
-pruneAktuellen Pfad nicht weiter absteigen
-lsls -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.

Bash Pro Treffer ein chmod-Aufruf
find . -type f -name '*.sh' -exec chmod +x {} \;
Bash Alle .tmp-Dateien gebündelt löschen
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:

Bash Korrekte Pipeline mit NUL-Trennung
find . -name '*.tmp' -print0 | xargs -0 rm

Warum 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.

Bash Argument-Position mit -I steuern
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.

Bash Große Dateien finden (über 1 GB)
find / -type f -size +1G 2>/dev/null

2>/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).

Bash In den letzten 7 Tagen geändert
find . -type f -mtime -7
Bash Leere Verzeichnisse aufspüren
find . -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.

Bash SUID-Binaries — Sicherheits-Audit
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.

Bash Logs älter als 30 Tage löschen
find /var/log -type f -name '*.log' -mtime +30 -delete
Bash In Skript: alle .sh ausführbar machen
find . -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.

Bash .git und node_modules ausschließen
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.

Bash Mit fd: alle .log-Dateien
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

/ Weiter

Zurück zu Dateisystem

Zur Übersicht