find ist eines der althergebrachten UNIX®-Werkzeuge. Seine Aufgabe ist es, rekursiv ein oder mehrere Verzeichnisse nach Dateien zu durchsuchen, die ein gegebenes Muster aufweisen. Einerseits ist das Werkzeug sehr hilfreich, andererseits ist seine Syntax nicht einfach und es braucht einige Übung bevor man es richtig anwenden kann. Die allgemeine Syntax lautet:
find [Verzeichnis] [Option(en)] [Test] [Aktion] |
Ohne Angabe eines Verzeichnisses wird das aktuelle Verzeichnis durchsucht. Ohne spezielle Suchkriterien wird das Ergebnis wie „vorhanden“ gewertet und daher alle vorhandenen Dateien aufgelistet. Die möglichen Optionen, Tests und Aktionen sind derart zahlreich, dass wir hier nur einige wenige behandeln. Lassen Sie uns mit den Optionen beginnen:
-xdev: keine Suche in Verzeichnissen, die auf anderen Dateisystemen liegen.
-mindepth <n>: Beginn der Suche erst n Stufen unterhalb des angegebenen Verzeichnisses.
-maxdepth <n>: Suche nach Dateien in maximal n Stufen unterhalb des angegebenen Verzeichnisses.
-follow: Verfolgen von symbolischen Links falls diese auf Verzeichnisse zeigen. Standardmäßig folgt find den Links nicht.
-daystart: bei zeitbezogenen Tests (siehe unten) wird der aktuelle Tag als Zeitmarke benutzt anstelle des standardmäßigen Zeitstempels, der 24 Stunden zur aktuellen Zeit zurück liegt.
Ein Test kann einer oder mehrere elementare Eigenschaften umfassen. Einige dieser Eigenschaften sind:
-type <file_type>: Suche nach einem angegebenen Dateityp. Der Dateityp kann einer der folgenden sein: f (normale Datei), d (Verzeichnis), l (symbolischer Link), s (Socket), b (blockorientierte Datei), c (zeichenorientierte Datei) oder p (eine „named Pipe“).
-name <pattern>: findet Dateien, die dem gegebenen Muster entsprechen. Mit dieser Option wird das Muster wie ein Muster für Shell Namenserweiterung angesehen (siehe Kapitel „Platzhalter in der Shell“).
-iname <pattern>: gleiche Wirkung wie -name, allerdings ohne auf Groß-/Kleinschreibung zu achten.
-atime <n>, -amin <n>: findet Dateien, die zuletzt vor n Tagen (-atime) oder vor n Minuten (-amin) geöffnet wurden. Sie können auch die Optionen <+n> oder <-n> angeben, wobei die Suche nach Dateien ausgeführt wird, die vor höchstens oder mindestens n Tagen/Minuten geöffnet wurden.
-anewer <a_file>: sucht nach Dateien, die später als die Datei a_file geöffnet wurden.
-ctime <n>, -cmin <n>, -cnewer <file>: gleiche Wirkung wie bei den Optionen -atime, -amin und -anewer, bezieht sich aber auf den Zeitpunkt der letzten Änderung des Inhaltes der Datei.
-regex <pattern>: gleiche Wirkung wie die Option -name. Allerdings wird pattern als regulärer Ausdruck behandelt.
-iregex <pattern>: wie -regex, allerdings ohne Beachtung von Groß-/Kleinschreibung.
Es gibt zahlreiche andere Tests, die Sie in find(1) finden können. Zusammengesetzte Tests können Sie in dieser Art anwenden:
<c1> -a <c2>: ist wahr, wenn sowohl c1 als auch c2 zutreffen; -a ist sowieso vorgegeben, also können Sie <c1> <c2> <c3> schreiben, wenn c1, c2 und c3 zutreffen sollen.
<c1> -o <c2>: ist wahr, wenn entweder c1 oder c2 zutreffen oder beide. Beachten Sie, dass -o eine niedrigere Priorität als -a besitzt. Daher müssen Sie bei der Suche nach Dateien, die entweder auf c1 oder c2 und auf c3 passen, eine Klammerkonstruktion verwenden: ( <c1> -o <c2> ) -a <c3>. Klammern als Teil eines Ausdrucks müssen maskiert werden, da sie sonst von der shell interpretiert werden!
-not <c1>: kehrt Bedingungen um, also ist -not <c1> wahr, wenn c1 nicht zutrifft.
Schließlich können Sie für jede gefundene Datei auch eine Aktion festlegen. Die meist genutzten Aktionen sind:
-print: gibt einfach den Namen der Datei auf der Standardausgabe an. Das ist die Standard-Aktion.
-ls: gibt auf der Standardausgabe das Äquivalent des Befehls ls -ilds für jede gefundene Datei aus.
-exec <command_line>: wendet die Befehlszeile command_line auf jede gefundene Datei an. Die Befehlszeile command_line muss mit dem Zeichen ; enden, das allerdings wieder maskiert werden muss um nicht von der shell interpretiert zu werden. Der Dateiname wird dabei von {} eingefasst. Siehe Beispiele.
-ok <command>: identisch mit -exec. Allerdings wird bei jeder Datei eine Bestätigung verlangt.
Am besten kann man all diese Optionen und Parameter mit einigen Beispielen darstellen. Nehmen wir an, Sie wollen alle Unterverzeichnisse im Verzeichnis /usr/share auflisten. Die Eingabe dazu lautet:
# find /usr/share -type d |
Angenommen, Sie betreiben einen HTTP Server und alle Ihre HTML-Dateien liegen im Verzeichnis /var/www/html, das auch das aktuelle Verzeichnis ist. Jetzt wollen Sie alle Dateien finden, die seit einem Monat nicht verändert wurden. Da die Seiten von verschiedenen Autoren stammen gibt es sowohl Dateien mit der Endung html als auch solche mit der Endung htm. Sie wollen all diese Dateien in das Verzeichnis /var/www/obsolete verlinken. Dazu geben Sie ein[17]:
# find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 -exec ln {} /var/www/obsolete \; |
Dies ist ein recht komplexes Beispiel, das wir jetzt genauer erklären werden. Die Tests lauten wie Folgt:
\( -name "*.htm" -o -name "*.html" \) -a -ctime -30 |
wodurch das erreicht wird, was wir wollen: es findet alle Dateien, die auf .htm oder .html enden („\( -name "*.htm" -o -name "*.html" \) “) und (-a) die innerhalb der letzten 30 Tage nicht verändert wurden (-ctime -30). Beachten Sie die Klammern: sie müssen hier angewendet werden, da -a eine höhere Priorität hat. Ohne Klammern würden alle Dateien aufgelistet, die mit .htm enden und dazu alle Dateien, die mit .html enden und in den letzten 30 Tagen nicht geändert wurden. Das ist aber nicht genau das, was wir wollten. Beachten Sie auch, dass die Klammern maskiert wurden: Wenn wir ( .. ) anstelle von \( .. \) schreiben würden, hätte die shell die Klammern interpretiert und versucht, den Befehl -name "*.htm" -o -name "*.html" in einer Sub-Shell auszuführen... Weitere mögliche Arten der Maskierung bestehen in der Einfassung mit Anführungszeichen, jedoch ist hier ein Backslash vorzuziehen, da nur ein einzelnes Zeichen maskiert werden muss.
Schließlich fehlt noch der für jede Datei auszuführende Befehl:
-exec ln {} /var/www/obsolete \; |
Hier muss das Zeichen ; vor der shell maskiert werden, da die Shell das Zeichen anderenfalls als Befehlstrenner interpretieren und find das Fehlen eines Argumentes für -exec melden würde.
Ein letztes Beispiel: Sie haben ein ausgedehntes Verzeichnis (/shared/images) mit allen möglichen Arten von Bilddateien. Sie benutzen regelmäßig den Befehl touch um den Zeitstempel der Datei stamp in diesem Verzeichnis als Zeitreferenz zu aktualisieren. Sie wollen nun alle JPEG-Bilder finden, die neuer als die Datei stamp sind. Da Ihre Bilder aus verschiedenen Quellen stammen, besitzen diese Bilder die Endungen jpg, jpeg, JPG oder JPEG. Natürlich wollen Sie auch keine Suche im alten Verzeichnis durchführen. Die Dateiliste soll per Mail an Sie geschickt werden (Ihr Kennzeichen ist birgit):
# find /shared/images -cnewer \ /shared/images/stamp \ -a -iregex ".*\.jpe?g" \ -a -not -regex ".*/old/.*" \ | mail birgit -s "New images" |
Natürlich ist es nicht gerade effizient, diese Befehlszeile jedes Mal neu eingeben zu müssen. Außerdem soll der Befehl regelmäßig ausgeführt werden. Hier ist eine einfache Lösung zur regelmäßigen Ausführung des Befehls:
[17] Beachten Sie, dass sowohl /var/www als auch /var/www/obsolete im gleichen Dateisystem liegen müssen!