find

Software für Unix zum Auffinden von Dateien und Verzeichnissen innerhalb der Verzeichnisstruktur

find (/usr/bin/find) ist ein Programm zur Durchmusterung von Filesystemen. Es ist ein grundlegender Bestandteil der Softwareausstattung von UNIX-Systemen und als solcher durch den POSIX-Standard (beziehungsweise den identischen Standards IEEE Std 1003 und ISO/IEC 9945)[1] definiert, als mandatory (erforderlicher Bestandteil) gekennzeichnet und somit Teil der Single UNIX Specification. Auch UNIX-ähnliche Betriebssysteme verfügen in der Regel über diesen Befehl.

Unter Microsoft Windows gibt es ebenfalls den Befehl find, dieser durchsucht jedoch den Inhalt von Dateien und hat eher (entfernte) Ähnlichkeit mit dem Kommando grep.

Geschichte

Bearbeiten

Das erste find erschien in Fifth Edition Unix (ca. Juni 1974) als Teil des PWB/UNIX-Projekts (Programmer’s Workbench), der in Research Unix[2] übernommen wurde. Es wurde von Richard C. (Dick) Haight, einem Mitglied der PWB/UNIX-Entwickler, geschrieben, ebenso wie cpio und expr.[3] find und cpio wurden für den gemeinsamen Einsatz entworfen.

Arbeitsweise

Bearbeiten

Als typisches Kommandozeilen-Utility ist das Verhalten finds durch die Sektion 12.2 des Standards (Utility Syntax Guidelines) geregelt.[4] Ausgehend von einem anzugebenden Basisverzeichnis durchschreitet find die Filesystemstruktur rekursiv und erzeugt eine Liste von Filesystem-Inhalten (Files, Directories etc..), die (wenigstens in den meisten Fällen) auf <stdout> ausgegeben wird. Diese Liste kann durch einen oder mehrere Operanden nach verschiedenen Gesichtspunkten fortschreitend eingeschränkt werden:

find /some/dir -print                     # jeden Eintrag in /some/dir anzeigen
find /some/dir -type f -print             # angezeigte Einträge auf (reguläre) Dateien beschränken
find /some/dir -type f -name 'x*' -print  # angezeigte Einträge auf Dateien deren Namen mit „x“ beginnt, einschränken

Es ist möglich, mehrere solche Operanden durch logische Operationen (UND, ODER, NICHT) zu verknüpfen wie auch durch Klammern die Präzedenz der Operanden zu beeinflussen. So können auch komplexe Filtertypen realisiert werden.

Allen Operanden ist gemeinsam, dass sie als Ergebnis ein logisches TRUE oder FALSE zurückgeben, aufgrund dessen die gerade untersuchte Entität an der weiteren Verarbeitung teilnimmt (bis sie – üblicherweise – am Schluss ausgegeben wird) oder von derselben ausgeschlossen wird.

Es ist möglich, das Ausgabeformat auf den reinen Dateinamen zu beschränken (-print) oder auch eine Liste von Attributen in Tabellenform (-ls, was eine Ausgabe ähnlich der des Kommandos ls -ails bewirkt) auszugeben.

Eine besondere Stellung unter den Operanden nimmt -exec ein. Es erwartet als Argument ein (einfaches oder auch zusammengesetztes) Kommando, in welchem der Name der gerade untersuchten Entität durch das Symbol {} vertreten wird. Damit ist es möglich, für eine eben erzeugte Liste von Filesystem-Einträgen eine durch ein Kommando repräsentierte Aktion durchzuführen. Das Beispiel sucht in allen Dateien *.c in dem Directory /some/dir nach Aufrufen oder dem Code der Funktion myfunc() (die Datei /dev/null wird angegeben, damit grep die Dateinamen der Fundstellen mit anzeigt):

find /some/dir -type f -name '*\.c' -exec grep 'myfunc(' /dev/null {} \;

Darüber hinaus gibt -exec als Rückgabewert den Return Code des aufgerufenen Kommandos zurück, sodass es ebenfalls für Filterungszwecke genutzt werden kann. Falls im obigen Beispiel nur die Dateinamen der Dateien, in denen die Funktion verwendet wird, gesucht werden, so kann dies durch:

find /some/dir -type f -name '*\.c' -exec grep -q 'myfunc(' {} \; -print

geschehen.

Zeitmessung und -vergleiche

Bearbeiten

Verschiedene Operanden (-atime, -ctime, -mtime) ermöglichen das Vergleichen von Zeitstempeln von Dateien, wobei die Vergleichszeiträume in Tagen angegeben werden. Tatsächlich werden diese Zeiträume allerdings als Ergebnis einer Integer-Division der Zeitdifferenz in Sekunden durch 86400 interpretiert. Der Standard führt in seinen Erläuterungen als (hier vervollständigtes) Beispiel

find /some/dir -atime 2

an[1], das alle Dateien (oder sonstigen Filesystem-Entitäten) findet, deren Zeitpunkt des letzten Zugriffs zwischen 48 und 72 Stunden vor Ausführung des Befehls liegt.

Hinweise und Einschränkungen

Bearbeiten

Numerische Argumente

Bearbeiten

Verschiedene Operanden (zum Beispiel -size oder -atime) erwarten numerische Argumente. Bei solchen gilt, dass die Angabe n (für numerische Werte n) immer genau n bedeutet, -n hingegen kleiner als n und +n bedeutet größer als n.

Vorsicht bei -exec

Bearbeiten

Da oftmals beim Aufruf von find nicht feststeht, wie viele Treffer schließlich im Ergebnis-Set vorliegen, kann die Verwendung von -exec zu einer unverhältnismäßigen Belastung des Systems führen, da ja für jeden Treffer ein (durch vergleichsweise hohen Ressourcenverbrauch gekennzeichneter) fork()-Systemcall durchzuführen ist.

Execplus

Bearbeiten

Mit dem POSIX-Standard konforme Implementationen kennen deshalb das den externen Aufruf abschließende Plus (+). Hier wird das externe Kommando mit einer Liste von Treffern gleichzeitig aufgerufen, sodass die erwähnte Belastung des Systems durch viele fork()-s verringert wird. Gleichzeitig aber wird der Rückgabewert des Kommandos für eine einzelne Datei unbestimmt, sodass -exec nicht mehr weiter als filternder Operand wirken kann.

Weitere Probleme bei der Verwendung von -exec können sich bei komplexen Kommandos ergeben: es ist zwar grundsätzlich möglich, verschachtelte Kommandos zu verwenden, aber letztlich ist find keine Shell und deshalb in der Interpretation solcher Schachtelungen beschränkt. Insbesondere Shell-übliche Konstruktionen wie etwa die logische Verknüpfung cmd1 && cmd2 schlagen innerhalb von Kommandos in -exec fehl.

{} und -exec

Bearbeiten

Das Argument {} kann lediglich ein einziges Mal in einem -exec-Operanden verwendet werden, nicht öfter. Darüber hinaus muss es als einzelnes (das heißt alleinstehendes) Argument verwendet werden. Die folgenden Konstruktionen sind deshalb alle ungültig:

find /some/where -type f -exec mv {} {}.old \;
find /some/where -type f -exec gzip {} >/packed/files/{} \;

Für solche Zwecke sollte ein Script geschrieben werden, dem als Parameter der Dateiname übergeben wird und das als Argument in -exec eingesetzt wird. Etwa für das erste Beispiel:

$ echo 'mv $1 ${1}.old' > /tmp/mymove.sh 
$ chmod u+x /tmp/mymove.sh

$ find /some/where -type f -exec /tmp/mymove.sh {} \;

Schutz vor unendlichem Regress

Bearbeiten

In der Praxis kann es durchaus vorkommen, dass in Filesystemen (durch Hardlinks oder Softlinks) Endlosreferenzen aufgebaut werden. Der POSIX-Standard schreibt deshalb vor, dass konforme Versionen diesen Umstand zu erkennen und die Rekursion abzubrechen haben.[1]

Nonstandard-Varianten

Bearbeiten

Das GNU-Projekt verfügt über einen Nachbau des Kommandos als Teil des findutils-Pakets. Es unterscheidet sich vom POSIX-konformen Original in einigen Punkten.

Bearbeiten

Einzelnachweise

Bearbeiten
  1. a b c find (Opengroup Base Specifications Issue 6). Abgerufen am 8. März 2018 (englisch).
  2. Die Bezeichnung wurde allerdings erst ab 1978 für diese Entwicklungslinie gebräuchlich, um sie von PWB/UNIX und MERT abzugrenzen, siehe Bell System Technical Journal Vol. 57, No 6, Pt. 2 Jul/Aug 1978
  3. M. Douglas McIlroy: A Research Unix reader: annotated excerpts from the Programmer's Manual, 1971–1986. (pdf) Abgerufen am 8. März 2018 (englisch).
  4. The Open Group Base Specifications Issue 7, 2018 edition, Kap. 12. Utility Conventions. Abgerufen am 15. Mai 2019 (englisch).