Warum stimmt find manchmal mit dem Befehlszeilenpfadargument überein?

9

Unter Linux

cd /tmp
mkdir foo; cd foo

Jetzt läuft

find . -name 'foo'

gibt keine Ausgabe. Während laufen

find /tmp/foo -name 'foo'

Gibt die Ausgabe, /tmp/foodie für mich keinen Sinn ergibt. Kann jemand erklären warum?

York
quelle
2
Finden Sie Suchanfragen innerhalb des angegebenen Pfades, wie angegeben. Also, wenn Sie eingeben ./, stimmte es nicht übereinfoo
Costas
@Costas: Ich verstehe das. Was ich nicht verstehe ist, warum es einen Unterschied gemacht hat, wenn ich den absoluten Weg dazu gebe find.
York
@York In bestimmten Situationen gibt es kein Verhalten, das immer richtig ist. Stellen Sie sich einen Symlink mit dem Namen vor bar, der auf eine Datei verweist, foodie sich außerhalb des Suchpfads befindet. Soll das passen oder nicht?
Hauke ​​Laging
1
Die .und /tmp/foosind nicht gleich - es handelt sich um zwei verschiedene feste Links zu demselben Verzeichnis. find /tmp/foo/. -name 'foo'findet auch nichts.
Jimmy
@jimmij: Als ich ausgeführt habe find /tmp/foo -name 'foo', habe ich bash gebeten, im Verzeichnis /tmp/fooeine Datei zu finden , deren Name "foo" ist. Da das Verzeichnis /tmp/fooleer ist, sollte es nichts zurückgegeben haben. Ich verstehe nicht, warum es zurückkommt /tmp/foo. Auf der anderen Seite habe ich beim Ausführen find . -name 'foo'Bash gebeten, dasselbe zu tun, dh eine Datei im aktuellen Verzeichnis (die zufällig war) zu finden /tmp/foo, deren Name 'foo' ist, und sie gibt nichts zurück, was Sinn macht.
York

Antworten:

15

findDurchläuft die angegebenen Verzeichnisbäume und wertet den angegebenen Ausdruck für jede gefundene Datei aus. Die Durchquerung beginnt am angegebenen Pfad. Hier ist eine Zusammenfassung der Funktionsweise find . -name foo:

  • Erster Pfad in der Befehlszeile: .
    • Entspricht der Basisname ( .) dem Muster foo? Nein, also tu nichts.
      Es kommt also vor, dass dies /tmp/fooein anderer Name für dasselbe Verzeichnis ist. Aber findweiß das nicht (und es soll nicht versuchen, es herauszufinden).
    • Ist der Pfad ein Verzeichnis? Ja, also überquere es. Zählen Sie die Einträge in auf .und führen Sie für jeden Eintrag den Durchlaufprozess durch.
      • Das Verzeichnis ist leer: Es enthält keinen anderen Eintrag als .und .., der findnicht rekursiv durchlaufen wird. Damit ist der Job beendet.

Und find /tmp/foo:

  • Erster Pfad in der Befehlszeile: /tmp/foo
    • Entspricht der Basisname ( foo) dem Muster foo? Ja, die Bedingung stimmt also überein.
      • Mit dieser Bedingung ist keine Aktion verknüpft. Führen Sie daher die Standardaktion aus, bei der der Pfad gedruckt wird.
    • Ist der Pfad ein Verzeichnis? Ja, also überquere es. Zählen Sie die Einträge in auf /tmp/foound führen Sie für jeden Eintrag den Durchlaufprozess durch.
      • Das Verzeichnis ist leer: Es enthält keinen anderen Eintrag als .und .., der findnicht rekursiv durchlaufen wird. Damit ist der Job beendet.

Es kommt also vor, dass .und /tmp/foodasselbe Verzeichnis sind, aber das reicht nicht aus, um zu gewährleisten, dass findbeide das gleiche Verhalten haben. Der findBefehl bietet Möglichkeiten zur Unterscheidung zwischen Pfaden zu derselben Datei. Das -namePrädikat ist eines davon. find /tmp/foo -name fooEntspricht dem Startverzeichnis sowie jeder darunter liegenden Datei, die aufgerufen wird foo. find . -name .nur entspricht das Startverzeichnis ( .kann nie während eines rekursiven Traversal zu finden).

Gilles 'SO - hör auf böse zu sein'
quelle
"Es enthält keinen anderen Eintrag als. und .., der nicht rekursiv gefunden wird." Ich gehe davon aus, dass "nicht rekursiv quer" auf ".." verweist. Wenn das richtig ist, was würde dann "rekursiv transversal" im Kontext von ".." bedeuten?
Faheem Mitha
@FaheemMitha "nicht rekursiv durchlaufen" gilt für beide .und ... (Wenn es rekursiv finddurchlaufen wird ., wird es das Verzeichnis immer wieder durchlaufen und…) .und ..sind nicht nur spezielle Notationen, sondern werden als Verzeichniseinträge angezeigt.
Gilles 'SO - hör auf böse zu sein'
5

Es gibt keine Normalisierung der Befehlszeilenargumente, bevor die Tests angewendet werden. Daher unterscheiden sich die Ergebnisse je nach verwendetem Pfad (wenn Symlinks beteiligt sind):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

In "Ihrem Fall" würden beide Anrufe das gleiche Ergebnis liefern, was (verwirrender) sein könnte. Sie können verwenden, -mindepth 1wenn die Startpunkte ignoriert werden sollen (möglicherweise nicht POSIX).

Hauke ​​Laging
quelle
-mindepth 1funktioniert. Ich verstehe jedoch immer noch nicht warum find . -name 'foo'und find /tmp/foo -name 'foo'verhalte mich anders.
York
2

(gnu) find zeigt alle Übereinstimmungen an, die innerhalb des Pfads gefunden wurden, der für den Befehl bereitgestellt wurde, da der Vergleich mit den Befehlszeilenargumenten beginnt und von dort aus tiefer in die Verzeichnisstruktur absteigt (daher werden -maxdepth 0die Tests nur auf die Basisebene oder die Befehlszeilenargumente beschränkt, während -mindepth 1Überspringt die Befehlszeilenargumente wie man finderläutert. Dies ist der Grund, warum find /tmp/foo -name 'foo'eine Übereinstimmung erzielt wird, selbst wenn das Verzeichnis selbst leer ist.

find . -name 'foo'Auf der anderen Seite wird kein Ergebnis .erzielt, da (Punkt) eine spezielle Datei ist, die wie ein Hardlink zu demselben Inode wirkt wie /tmp/foo- es ist wie ein separater (wenn auch spezieller) Dateiname und kein symbolischer Link oder Ausdruck, dem man unterliegt Pfadnamenerweiterung durch die Shell. Daher zeigt der erste Test, der von find auf die Befehlszeilenargumente im angegebenen Beispiel angewendet wird, keine Übereinstimmungen an, da er .tatsächlich nicht mit dem in definierten Namensmuster übereinstimmt -name 'foo'. Dies gilt auch nicht, /tmp/foo/.da ein Test für ein -nameMuster nur für den Basisnamen des Pfads durchgeführt wird (siehe man find), was auch hier der Fall ist ..

Obwohl dieses Verhalten aus Benutzersicht möglicherweise nicht zu erwarten ist oder intuitiv erscheint (und ja, es hat mich zunächst auch verwirrt), stellt es keinen Fehler dar, sondern entspricht der Logik und Funktionalität, die auf den Man- und Infoseiten für ( gnu) finden.

Shevek
quelle
0

Ich habe versucht, Gilles 'Antwort zu kommentieren, aber es war zu lang, um in einen Kommentar zu passen. Aus diesem Grund stelle ich es als Antwort auf meine eigene Frage.

Gilles 'Erklärung (und auch die von Shevek) ist klar und sinnvoll. Der entscheidende Punkt hierbei ist, dass nicht nur findversucht wird, die Dateinamen für die Dateien innerhalb der angegebenen Pfade (rekursiv) abzugleichen, sondern auch versucht wird, den Basisnamen der angegebenen Pfade selbst abzugleichen.

Gibt es andererseits einen Beweis dafür, dass dies der Weg findist, der funktionieren soll, anstatt ein Fehler zu sein? Meiner Meinung nach wäre es besser, wenn die Ergebnisse dadurch nicht inkonsistent werden find .und find ABSOLUTE-PATHweil Inkonsistenzen immer verwirrend sind und Entwickler viel Zeit damit verschwenden könnten, herauszufinden, "was falsch war". In meinem Fall habe ich ein Skript geschrieben und der Pfad wurde einer Variablen entnommen. Damit mein Skript richtig funktioniert, kann ich mir vorstellen, zu schreiben find $path/. -name 'pattern'.

Schließlich denke ich, dass Sie konsistente Ergebnisse finderzielen können, indem Sie das immer .durch das aktuelle Verzeichnis ersetzen, bevor Sie fortfahren.

York
quelle
-1

fooIn dem Verzeichnis, in dem Sie die relative Suche gestartet haben, ist kein Objekt benannt .

Sie gehen zu Recht davon aus, dass dies gfindfehlerhaft ist, /tmp/foowenn der absolute Name als Startverzeichnis verwendet wird.

Gfindhat verschiedene Abweichungen vom Standard, anscheinend haben Sie einen anderen gefunden. Wenn Sie eine standardkonformere Lösung wünschen, empfehle ich, sfinddass dies Teil der ist schilytools.

-namegilt für Verzeichnissuchergebnisse. Keiner der beiden von Ihnen genannten Fälle gibt einen Verzeichniseintrag foovon einer readdir()Operation zurück.

schily
quelle
Ich vermute, das ist auch ein Fehler. Hier ist die Ausgabe von find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Freie Software Foundation, Inc. Lizenz GPLv3 +: GNU GPL Version 3 oder höher < gnu.org/licenses/gpl.html >
York
Nun, ich habe Ihre Beispielbefehle mit find(POSIX-zertifiziert) überprüft gfindund sfind; Das Problem besteht nur, wenn Sie verwenden gfind. Wird unter Linux gfindnicht unter dem nativen Namen, sondern unter dem Namen installiert find.
schily
3
Die find /tmp/foo -name fooAusgabe ist korrekt /tmp/foo. (Cc @York) Dies ist das Verhalten von OpenBSD find, GNU find, BusyBox find, Solaris findund anderen POSIX-kompatiblen Dateien find(„Der Primärwert muss als wahr bewertet werden, wenn der Basisname des untersuchten Dateinamens mit dem Muster (…) übereinstimmt “ - wenn der Dateiname ist eine, die als Pfadoperand übergeben wird, es ist kein readdirAufruf beteiligt).
Gilles 'SO - hör auf böse zu sein'