Find für Fortgeschrittene

Aus LinuxUser 11/2023

Find für Fortgeschrittene

© Sergey Lavrentev / 123RF.com

Treffsicher

Nach Dateien und deren Inhalten zu suchen, gehört bei der Arbeit am PC zum täglich Brot. Unter Linux findet man seit jeher mit GNU Find.

Apple nennt unter MacOS das zentrale Tool Finder, Microsoft liefert seine Betriebssysteme mit der Windows-Suche aus. Dass Windows nur sucht, statt wie MacOS zu finden, klingt beinahe nach einem Eingeständnis. Unter Unix erblickte 1974 das Werkzeug Find das Licht der Welt. Uns interessiert im Rahmen dieses Artikels nur dessen GNU-Implementierung, die jedes Linux-System vorinstalliert mitbringt.

Ohne Argumente liefert der Aufruf find rekursiv sämtliche Dateien, Verzeichnisse, (Sym-)Links und so weiter unterhalb des aktuellen Verzeichnisses. Dasselbe klappt aber auch mit einer Pfadangabe. Ein Find-Aufruf direkt in /var/log/ und das Kommando find /var/log/ liefern also identische Ausgaben.

Filtern nach Typen

Um die Ergebnisse weiter einzugrenzen, bringt Find zahlreiche Filtermöglichkeiten mit. Der eingebaute Schalter -type erlaubt unter anderem, nach echten Dateien (-type f), nach Verzeichnissen (-type d) oder nach Links -type l zu suchen. Daneben gibt es noch weitere Typen, über die die Manpage Auskunft gibt.

Sehen wir uns ein konkretes Beispiel an: Das Kommando aus der ersten Zeile von Listing 1 erzeugt eine einfache Textdatei mit kurzem Inhalt. Sie erhält anschließend per Touch ein anderes Modifikationsdatum (Zeile 2). Darauf kommen wir später noch einmal zurück. Als Nächstes entsteht per Ln ein sogenannter Hardlink (Zeile 3). Hardlink und Originaldatei lassen sich nicht unterscheiden. Das liegt in ihrer Natur, denn sowohl der Link als auch die Originaldatei besitzen als Adresse denselben Inode im Dateisystem. Existiert zu einer Datei ein Hardlink, gilt der Dateiinhalt erst dann als gelöscht, wenn sowohl der Hardlink als auch die Datei nicht mehr existiert.

Listing 1

Filtern nach Typen

$ echo "Ich bin eine Textdatei." > test.txt
$ touch -d '19991231' test.txt
$ ln test.txt test.lnk
$ ln -s test.txt test_symlink.lnk
$ mkdir testdir

Das erklärt, warum Sie keine Hardlinks erzeugen können, die auf Dateien auf anderen Laufwerken oder Partitionen zeigen. So etwas funktioniert nur mit symbolischen Links, kurz Symlinks (Zeile 4). Damit verweisen Sie flexibel nicht nur auf Dateien, sondern außerdem auf Verzeichnisse und andere Dateisysteme. Dahinter steckt technisch gesehen nichts anderes als eine Art Textdatei, die einen Pfad enthält. Zu guter Letzt entsteht noch ein Verzeichnis (letzte Zeile).

Listen Sie nun den Verzeichnisinhalt mit ls -li auf (Abbildung 1), sehen Sie, dass sich test.txt und test.lnk praktisch nicht auseinanderhalten lassen. In der ersten Spalte steht die Inode-Nummer, die bei Link und Datei identisch ausfällt.

Abbildung 1: Ein Aufruf von Ls mit Inode-Nummern liefert nur schwer zu unterscheidende Ergebnisse.

Abbildung 1: Ein Aufruf von Ls mit Inode-Nummern liefert nur schwer zu unterscheidende Ergebnisse.

Nach diesen Vorarbeiten geht es an erste Experimente mit Find. Suchen Sie im aktuellen Verzeichnis (./) nach Dateien, nach Links und nach Verzeichnissen (Abbildung 2). Schon die Suche nach Dateien birgt eine Überraschung: Find betrachtet Hardlinks als Dateien. Damit ist klar, dass in unserem Beispiel eine Suche nach Links lediglich ein einziges Ergebnis liefern kann. Auch die Suche nach Verzeichnissen ergibt Unerwartetes: Find findet nicht nur das Verzeichnis testdir/, sondern ebenso das aktuelle Verzeichnis ./.

Abbildung 2: Die Suche nach Dateien, Links und Verzeichnissen erbringt teils überraschende Ergebnisse.

Abbildung 2: Die Suche nach Dateien, Links und Verzeichnissen erbringt teils überraschende Ergebnisse.

Filtern nach Zeit

Jede Datei unter Linux trägt drei Zeitstempel: die Modification Time MTIME, die Access Time ATIME und die Change oder Creation Time CTIME.

MTIME gibt an, wann der Dateiinhalt zuletzt geändert wurde. Im Fall einer Log-Datei kann man davon ausgehen, dass sie sich recht häufig ändert. Möchten Sie wissen, wann eine Datei zuletzt gelesen wurde, ist ATIME Ihr Freund. Interessieren Sie sich dafür, wann die Metadaten einer Datei zuletzt modifiziert wurden, also Besitzer, Gruppe oder Berechtigungen, dann kommt CTIME ins Spiel.

Find gibt Ihnen mit den Schaltern -mtime, -atime und -ctime die korrespondierenden Filtermöglichkeiten an die Hand. Alle drei erwarten einen ganzzahligen Wert für das Alter in Tagen. Über ein vorgestelltes Pluszeichen geben Sie das Mindestalter an, über ein Minuszeichen das Höchstalter. Beim Filtern nach Zeiten beachtet Find nur ganze Perioden von 24 Stunden, angebrochene Tage ignoriert das Werkzeug. Abbildung 3 zeigt einige Varianten.

Abbildung 3: Dateien, die innerhalb des letzten Monats geändert wurden (oben), auf die innerhalb der letzten 24 Stunden zugegriffen wurde (Mitte) und deren Metadaten vor mindestens einer Woche angepasst wurden (unten).

Abbildung 3: Dateien, die innerhalb des letzten Monats geändert wurden (oben), auf die innerhalb der letzten 24 Stunden zugegriffen wurde (Mitte) und deren Metadaten vor mindestens einer Woche angepasst wurden (unten).

Finden und verarbeiten

Jeder Benutzer steht gelegentlich vor der Aufgabe, Eigentümer, Gruppen und deren Berechtigungen in ganzen Verzeichnisbäumen korrigieren zu müssen. Freilich könnten Sie das einfach per Chown und Chmod rekursiv korrigieren, etwa durch chmod -R 750 Verzeichnis. Das würde dann sicherstellen, dass Eigentümer und Gruppe entsprechende Rechte auf den Verzeichnisbaum besitzen. Allerdings hätten Sie dann auch jede enthaltene Datei als ausführbar für Eigentümer und Gruppe markiert (7=rwx, 5=r-x, 0=---). Das ist nur sehr selten sinnvoll. Um hier zwischen Verzeichnissen und Dateien zu unterscheiden, machen Sie sich die Stärken von Find zunutze: Mit der Option -exec führen Sie auf jeden Treffer ein bestimmtes Kommando aus.

Zuerst korrigieren Sie wie in Listing 2 gezeigt die Berechtigungen der Verzeichnisse (-type d), anschließend die der Dateien (-type f). Den auszuführenden Befehl (hier chmod) müssen Sie dabei terminieren, entweder durch ein Pluszeichen oder durch ein Semikolon. Bei Letzterem kommt Ihnen allerdings die Shell in die Quere. Für sie hat das Semikolon eine besondere Bedeutung: Es separiert Kommandos voneinander. Daher verschluckt die Bash den Strichpunkt, der so niemals Find erreicht. Um das zu verhindern, müssen Sie das Semikolon entweder per Backslash escapen oder in einfache Hochkommas stellen. Geschweifte Klammern sorgen dafür, dass Find die Fundstellen inklusive Pfad an das externe Kommando übergibt.

Listing 2

Externe Kommandos

$ find . -type d -exec chmod 750 {} \;
$ find . -type f -exec chmod 640 {} +

Auf dieselbe Weise suchen Sie rekursiv nach Inhalten von Dateien. Zwar beherrscht Grep ebenfalls Rekursion, es vermag jedoch nicht weiter zu filtern. Es würde deshalb daran scheitern, ausschließlich Textdateien mit Größen unter 1 MByte, deren letzte Änderung drei Tage zurückliegt, nach Text zu durchsuchen. Mit Find ist das recht simpel (Listing 3).

Listing 3

Find-Grep-Kombi

$ find . -type f -size -1M \
  -mtime +3 -name "*.txt" \
  -exec grep SUCHTEXT {} \;

Logische Verknüpfungen

Schon die bislang vorgestellten, grundlegenden Find-Optionen befähigen Sie, recht präzise die richtigen Dateien, Verzeichnisse und so weiter zu finden. Das volle Potenzial von Find ist damit aber noch längst nicht ausgeschöpft: Sie können darüber hinaus unerwünschte Suchorte ausschließen und komplexere Suchen über logische Verknüpfungen gestalten.

Wollen Sie Dateien finden, die nicht nur einem bestimmten Namensschema genügen, sondern auch eine vorgegebene Größe und ein maximales Alter aufweisen, benötigen Sie logische Verknüpfungen, etwa das logische Und. Find ermöglicht solche Verknüpfungen mit den Schaltern -and und -or respektive deren Kurzformen -a und -o). Implizit wertet Find eine Aneinanderreihung von Ausdrücken stets als UND-Verknüpfung. Suchen Sie im aktuellen Verzeichnis eine echte Datei (also keinen Link oder dergleichen) namens test.txt, die mindestens fünf Tage alt und weniger als 3 MByte groß ist, erreichen Sie das mit einem der drei völlig gleichwertigen Suchausdrücke aus Listing 4.

Listing 4

UND

$ find . -type f \
  -mtime +5 \
  -size -3M \
  -name test.txt
$ find . -type f \
  -a -mtime +5 \
  -a -size -3M \
  -a -name test.txt
$ find . -type f \
  -and -mtime +5 \
  -and -size -3M \
  -and -name test.txt

Eine ODER-Verknüpfung macht das Filtern schon viel interessanter. Angenommen, Sie möchten nicht nur Dateien finden, sondern auch Links. Für uns Menschen erscheint es logisch, dass die Konjunktion UND hier nichts anderes bedeutet als eine Vereinigungsmenge: “Finde Objekte vom Typ Datei und finde Objekte vom Typ Link. Liste alle auf.” Logisch korrekt formuliert hieße es aber: “Finde Objekte vom Typ Datei oder Link. Liste sie auf.”

Punkt vor Strich

Nicht formale Sprachen neigen im Gegensatz zu formalen Sprachen dazu, das logische Oder als exklusiv anzusehen: “Möchtest du nach links oder nach rechts abbiegen?” Suchen Sie dagegen nach Dateien, die älter als fünf Tage sind oder (-o) eine Größe von mindestens 1 MByte haben, erhalten Sie auch Treffer für Files, für die beides gilt.

Das logische Oder weist zudem eine niedrigere Präzedenz auf als das logische Und. Das verhält sich ähnlich wie bei der Punkt-vor-Strich-Rechenregel: Das UND bindet stärker. Hierzu liefere ich später noch ein paar Beispiele. Sind Sie beispielsweise an Links interessiert, oder aber an Objekten mit einer Größe von mindestens 3 MByte (Abbildung 4), nutzen Sie -or (Listing 5, erste Zeile).

Abbildung 4: Eine ODER-Verknüpfung.

Abbildung 4: Eine ODER-Verknüpfung.

Listing 5

ODER

$ find . -type l -or -size +3M
$ find . -type f \( -size +3M -or -size -10M \) -iname *.txt

Selbstverständlich können darüber hinaus mehrere ODER-Verknüpfungen vorkommen (Abbildung 5). Spannend wird es, sobald Sie UND- und ODER-Verknüpfungen mischen – etwa, wenn Sie nach Textdateien suchen, die zwischen 3 und 10 MByte groß sind (Abbildung 6). Hier müssen Sie die unterschiedlichen Präzedenzen von UND und ODER beachten und gegebenenfalls Klammern setzen, genau wie beim Rechnen (Listing 5, zweite Zeile).

Abbildung 5: Mehrere ODER-Verknüpfungen.

Abbildung 5: Mehrere ODER-Verknüpfungen.


Abbildung 6: UND und ODER gemischt.

Abbildung 6: UND und ODER gemischt.

Die Bash kennt wie die meisten Shells die Klammer als Sprachelement. Daher müssen Sie Klammern, die sich an Find richten, ihre besondere Bedeutung für die Shell nehmen, sie also escapen. Dazu stellen Sie ihnen einen Backslash voran.

Pruning

Mit -prune lassen Sie Verzeichnisse und Dateien aus. Der Gebrauch des Parameters wirkt auf den ersten Blick etwas verwirrend, klärt sich aber, sobald Sie die allgemeine Formulierung aus der ersten Zeile von Listing 6 verinnerlichen.

Listing 6

Pruning

$ find Ort(e) Prune-Bedingung -prune -o Bedingungen Aktionen
$ find . -name ignoreme -prune -o -print
$ find . -print -name ignoreme -prune
$ sudo find \( -path /proc -o -path /sys -o -path /lost\+found -o -path /mnt \) -prune -o -name '.vimrc' -print

Hinter Prune steckt anders als bei -name oder -size kein Test, sondern wie bei -print eine Aktion. Das bedeutet, dass sich durch -prune unter Umständen die Liste der auszuwertenden Objekte ändert. Dennoch liefert Prune an die nachfolgenden Tests stets true. Deshalb ist es nahezu immer sinnvoll, nach -prune das logische Oder -or anzuwenden.

Die Manpage von Find formuliert die Vorgehensweise etwa so: Die Idee besteht hier darin, dass der Ausdruck vor -prune sich auf die zu kürzenden Elemente bezieht. Die Aktion -prune selbst gibt jedoch stets true zurück. Deswegen stellt das folgende -o sicher, dass die Shell die rechte Seite nur für die nicht gekürzten Verzeichnisse auswertet. Der Inhalt der gekürzten Verzeichnisse wird nicht einmal besucht und spielt deswegen keinerlei Rolle.

Zur Illustration liefert Listing 6 einige Beispiele. Das Kommando aus Zeile 2 gibt alle Dateinamen im aktuellen Verzeichnis aus, überspringt aber das Verzeichnis ignoreme/ und alle darin enthaltenen Dateien. Dagegen liefert der Befehl aus Zeile 3 alle Dateien im aktuellen Verzeichnis, inklusive des Verzeichnis ignoreme/, nicht aber die Dateien unterhalb von ignoreme/.

Manchmal suchen Sie eine ganz bestimmte Datei, deren Namen Sie genau kennen, zum Beispiel die Konfigurationsdatei.vimrc von Vim. Nun haben Sie unpraktischerweise vergessen, wo genau im Dateisystem sie liegt. Einige Orte kommen dabei jedoch per se nicht infrage, etwa entfernte Dateisysteme (NFS, CIFS, WebDAV) oder Systemverzeichnisse wie /proc und /sys. Hier spielen Sie die volle Stärke von -prune aus, indem Sie all diese Verzeichnisse so ausschließen, wie das die letzte Zeile von Listing 6 zeigt.

Fazit

Mit Find haben Sie unter unixoiden Betriebssystemen ein Werkzeug zur Hand, das seinen Namen wirklich verdient. Die hier vorgestellten Möglichkeiten versetzen Sie in die Lage, sehr genau die Dateien zu finden, die Sie benötigen. Die Manpage beschreibt etliche weitere Funktionen, die bestimmte Spezialfälle abdecken. Dementsprechend lohnt es sich auf jeden Fall, hin und wieder einen Blick ins Handbuch zu werfen. (jlu)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 11/2023 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben