Warum ist der Befehl "find | grep 'Dateiname' "so viel langsamer als" 'Dateiname finden' "?


10

Ich habe beide Befehle ausprobiert und der Befehl find | grep 'filename' ist viele Male langsamer als der einfache find 'filename' Befehl.

Was wäre eine richtige Erklärung für dieses Verhalten?


2
Sie listen jede Datei mit find auf und übergeben die Daten dann an grep, um sie zu verarbeiten. Wenn find alleine verwendet wird, fehlt Ihnen der Schritt, jede aufgelistete Datei an grep zu übergeben, um die Ausgabe zu analysieren. Dies wird daher schneller sein.
Raman Sailopal

In welchem ​​Sinne langsamer? Dauert das Ausführen der Befehle anders lange?
Kusalananda

1
Ich kann das nicht lokal reproduzieren. Wenn überhaupt, time find "$HOME" -name '.profile'meldet sich eine längere Zeit als time find "$HOME" | grep -F '.profile'. (17s gegen 12s).
Kusalananda

2
@ JenniferAnderson Ich habe beide wiederholt ausgeführt. Die 17 und 12 Sekunden sind Durchschnittswerte. Und ja, die grepVariation stimmt überall im findErgebnis überein , wohingegen die Übereinstimmung mit find -namenur genau übereinstimmen würde (in diesem Fall).
Kusalananda

2
Ja, find filename wäre schnell . Ich nahm irgendwie an, dass dies ein Tippfehler war und dass das OP bedeutete find -name filename. Mit find filenamenur filenamewürde untersucht (und sonst nichts).
Kusalananda

Antworten:


11

(Ich gehe hier von GNU findaus)

Mit just

find filename

wäre schnell, weil es nur zurückkehren würde filename, oder die Namen darin, filenamewenn es ein Verzeichnis ist, oder ein Fehler, wenn dieser Name nicht im aktuellen Verzeichnis vorhanden wäre. Es ist eine sehr schnelle Operation, ähnlich wie ls filename(aber rekursiv, wenn filenamees sich um ein Verzeichnis handelt).

Im Gegensatz,

find | grep filename

würde es ermöglichen find, eine Liste aller Namen aus dem aktuellen Verzeichnis und darunter zu generieren , die grepdann filtern würde. Dies wäre offensichtlich eine viel langsamere Operation.

Ich gehe davon aus, dass das, was eigentlich beabsichtigt war, war

find . -type f -name 'filename'

Dies würde filenameals Name einer regulären Datei irgendwo im aktuellen Verzeichnis oder darunter suchen .

Dies ist so schnell (oder vergleichsweise schnell) wie find | grep filename, aber die grepLösung würde mit filenamedem vollständigen Pfad jedes gefundenen Namens übereinstimmen , ähnlich wie dies der Fall -path '*filename*'wäre find.


Die Verwirrung rührt von einem Missverständnis darüber her, wie es findfunktioniert.

Das Dienstprogramm verwendet eine Reihe von Pfaden und gibt alle Namen unter diesen Pfaden zurück.

Sie können dann die zurückgegebenen Namen mithilfe verschiedener Tests einschränken, die sich auf den Dateinamen, den Pfad, den Zeitstempel, die Dateigröße, den Dateityp usw. auswirken können.

Wenn du sagst

find a b c

Sie fragen findjeden Namen unter den drei Wege zur Liste a, bund c. Wenn dies zufällig Namen von regulären Dateien im aktuellen Verzeichnis sind, werden diese zurückgegeben. Wenn einer von ihnen zufällig der Name eines Verzeichnisses ist, wird er zusammen mit allen weiteren Namen in diesem Verzeichnis zurückgegeben.

Wenn ich es tue

find . -type f -name 'filename'

Dadurch wird eine Liste aller Namen im aktuellen Verzeichnis ( .) und darunter generiert . Dann beschränkt es die Namen auf die von regulären Dateien, dh nicht auf Verzeichnisse usw., mit -type f. Dann gibt es eine weitere Einschränkung für Namen, die mit filenameusing übereinstimmen -name 'filename'. Die Zeichenfolge filenamekann ein Dateinamen-Globbing-Muster sein, z. B. *.txt(denken Sie daran, es zu zitieren!).

Beispiel:

Folgendes scheint die .profilein meinem Home-Verzeichnis aufgerufene Datei zu "finden" :

$ pwd
/home/kk
$ find .profile
.profile

Tatsächlich werden jedoch nur alle Namen im Pfad zurückgegeben .profile(es gibt nur einen Namen, und das ist von dieser Datei).

Dann steige ich cdeine Ebene höher und versuche es erneut:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

Der findBefehl kann jetzt keinen aufgerufenen Pfad finden .profile.

Wenn ich es jedoch dazu bringe, das aktuelle Verzeichnis zu betrachten und dann die zurückgegebenen Namen auf nur zu beschränken.profile , findet es es auch von dort:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenamewürde nur zurückkehren, filenamewenn filenamees nicht vom Typ Verzeichnis war (oder vom Typ Verzeichnis war, aber selbst keinen Eintrag hatte)
Stéphane Chazelas

2

Nicht-technische Erklärung: Die Suche nach Jack in einer Menschenmenge ist schneller als die Suche nach allen in einer Menschenmenge und die Beseitigung aller außer Jack.


Das Problem ist, dass das OP erwartet, dass Jack die einzige Person in der Menge ist. Wenn ja, haben sie Glück. find jacklistet auf, jackob es sich um eine aufgerufene Datei handelt jack, oder alle Namen im Verzeichnis, wenn es sich um ein Verzeichnis handelt. Es ist ein Missverständnis darüber, wie es findfunktioniert.
Kusalananda

1

Ich habe das Problem noch nicht verstanden, kann aber weitere Erkenntnisse liefern.

Wie bei Kusalananda ist der find | grepAnruf auf meinem System deutlich schneller, was wenig Sinn macht. Zuerst nahm ich eine Art Pufferproblem an; Das Schreiben in die Konsole verlangsamt die Zeit bis zum nächsten Systemaufruf zum Lesen des nächsten Dateinamens. Das Schreiben in eine Pipe ist sehr schnell: etwa 40 MB / s, selbst für 32-Byte-Schreibvorgänge (auf meinem eher langsamen System; 300 MB / s für eine Blockgröße von 1 MB). Daher habe ich angenommen, dass findbeim Schreiben in eine Pipe (oder Datei) schneller aus dem Dateisystem gelesen werden kann, sodass die beiden Vorgänge zum Lesen von Dateipfaden und zum Schreiben in die Konsole parallel ausgeführt werden können (was findals einzelner Thread-Prozess nicht alleine möglich ist.

Es ist find‚s Fehler

Vergleich der beiden Anrufe

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

und

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

zeigt, dass findetwas unglaublich Dummes tut (was auch immer das sein mag). Es stellt sich einfach als ziemlich inkompetent bei der Ausführung heraus -name '*.txt'.

Kann vom Input / Output-Verhältnis abhängen

Sie könnten denken, dass find -namegewinnt, wenn es sehr wenig zu schreiben gibt. Aber es wird nur peinlicher find. Es verliert auch dann, wenn für 200K-Dateien (13M Pipe-Daten) überhaupt nichts zu schreiben ist für grep:

time find /usr -name lwevhewoivhol

findkann so schnell wie sein grepobwohl,

Es stellt sich heraus, dass sich finddie Dummheit mit namenicht auf andere Tests erstreckt. Verwenden Sie stattdessen einen regulären Ausdruck und das Problem ist behoben:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Ich denke, das kann als Fehler angesehen werden. Ist jemand bereit, einen Fehlerbericht einzureichen? Meine Version ist find (GNU findutils) 4.6.0


Wie wiederholbar sind deine Timings? Wenn Sie den -nameTest zuerst durchgeführt haben, war er möglicherweise langsamer, da der Verzeichnisinhalt nicht zwischengespeichert wurde. (Beim Testen -nameund -regexich finde, dass sie ungefähr die gleiche Zeit in Anspruch nehmen, zumindest wenn der Cache-Effekt berücksichtigt wurde. Natürlich kann es sich nur um eine andere Version von find... handeln)
psmears

@psmears Natürlich habe ich diese Tests mehrmals durchgeführt. Das Caching-Problem wurde bereits in den Kommentaren zur Frage vor der ersten Antwort erwähnt. Meine findVersion ist find (GNU findutils) 4.6.0
Hauke ​​Laging

Warum ist es überraschend, dass das Hinzufügen -name '*.txt'langsamer wird find? Es muss zusätzliche Arbeit leisten und jeden Dateinamen testen.
Barmar

@Barmar Einerseits kann diese zusätzliche Arbeit extrem schnell erledigt werden. Andererseits spart diese zusätzliche Arbeit andere Arbeit. findmuss weniger Daten schreiben. Und das Schreiben in eine Pipe ist viel langsamer.
Hauke ​​Laging

Das Schreiben auf eine Festplatte ist sehr langsam, das Schreiben in eine Pipe ist nicht so schlecht, es wird nur in einen Kernelpuffer kopiert. Beachten Sie, dass Sie in Ihrem ersten Test mehr schreiben, um /dev/nullirgendwie weniger Systemzeit zu verbrauchen .
Barmar

0

Hinweis : Ich gehe davon aus, dass Sie meinen find . -name filename(ansonsten suchen Sie nach verschiedenen Dingen; untersucht find filenametatsächlich einen Pfad namens Dateiname , der möglicherweise fast keine Dateien enthält und daher sehr schnell beendet wird).


Angenommen, Sie haben ein Verzeichnis mit fünftausend Dateien. Bei den meisten Dateisystemen werden diese Dateien tatsächlich in einer gespeicherten Baumstruktur , die schnell ermöglicht es jedem , einen suchen angegebene Datei.

Wenn Sie also findnach einer Datei suchen, deren Name nur überprüft werden muss, findwerden Sie nach dieser Datei und nur nach dieser Datei im zugrunde liegenden Dateisystem gefragt , das nur sehr wenige Seiten aus dem Massenspeicher liest. Wenn das Dateisystem also das Geld wert ist, wird dieser Vorgang viel schneller ausgeführt als das Durchlaufen des gesamten Baums , um alle Einträge abzurufen.

Wenn Sie nach einer Ebene fragen, findaber genau das tun Sie, durchqueren Sie den gesamten Baum und lesen. Jeder. Single. Eintrag. Bei großen Verzeichnissen kann dies ein Problem sein (genau aus diesem Grund erstellen mehrere Softwareprogramme, die viele Dateien auf der Festplatte speichern müssen, zwei oder drei Komponenten tiefe "Verzeichnisbäume": Auf diese Weise muss jedes einzelne Blatt nur weniger enthalten Dateien).


-2

Nehmen wir an, die Datei / john / paul / george / ringo / beatles existiert und die gesuchte Datei heißt "Steine".

find / stones

find vergleicht 'Beatles' mit 'Steinen' und lässt es fallen, wenn 's' und 'b' nicht übereinstimmen.

find / | grep stones

In diesem Fall wird find '/ john / paul / george / ringo / beatles' an grep übergeben und grep muss sich durch den gesamten Pfad arbeiten, bevor festgestellt wird, ob es eine Übereinstimmung ist.

grep macht deshalb viel mehr arbeit, weshalb es länger dauert


1
Hast du das ausprobiert?
Hauke ​​Laging

3
Die Kosten für die Zeichenfolgenvergleiche (extrem einfach und billig) werden durch die E / A-Kosten (oder nur für den Syscall, wenn zwischengespeichert) der Verzeichnissuche vollständig in den Schatten gestellt.
Mat

grep ist kein Zeichenfolgenvergleich, sondern ein Vergleich mit regulären Ausdrücken. Dies bedeutet, dass es sich durch die gesamte Zeichenfolge arbeiten muss, bis es entweder eine Übereinstimmung findet oder das Ende erreicht. Die Verzeichnissuchen sind auf jeden Fall gleich.
Paranoid

@Paranoid Hm, über welche Version von find sprichst du? Es ist anscheinend nichts wie der Fund, den ich in Debian gewohnt bin.
Pipe
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.