Rekursives grep vs find / -type f -exec grep {} \; Was ist effizienter / schneller?

70

Was ist effizienter, um herauszufinden, welche Dateien in einem gesamten Dateisystem einen String enthalten: rekursives grep oder find with grep in einer exec-Anweisung? Ich nehme an, find wäre effizienter, weil Sie zumindest ein bisschen filtern können, wenn Sie die Dateierweiterung oder eine Regex kennen, die dem Dateinamen entspricht, aber wenn Sie nur wissen, -type fwelche besser ist? GNU grep 2.6.3; find (GNU findutils) 4.4.2

Beispiel:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;

Gregg Leventhal
quelle
1
Mathematik / Informatik / Algorithmus-Effizienz basiert nicht auf Meinungen.
Gregg Leventhal
Überprüfen Sie diese. Obwohl nicht rekursiv, würde es ein besseres Verständnis geben. unix.stackexchange.com/questions/47983/…
Ramesh
8
@AvinashRaj er fragt nicht nach Meinung. Er fragt, was effizienter und / oder schneller ist und nicht welches "besser" ist. Dies ist eine perfekt beantwortbare Frage, die eine einzige, spezifische Antwort hat, die davon abhängt, wie diese beiden Programme ihre Arbeit tun und was genau Sie ihnen zum Durchsuchen geben.
terdon
2
Beachten Sie, dass das -exec {} +Formular weniger Gabeln enthält und daher schneller sein sollte als -exec {} \;. Möglicherweise müssen Sie den Optionen ein -H(oder -h) hinzufügen grep, um eine genau gleichwertige Ausgabe zu erhalten.
Mikel
Sie wollten wahrscheinlich nicht die -rOption grepfür die zweite
Qwertzguy

Antworten:

85

Ich bin mir nicht sicher:

grep -r -i 'the brown dog' /*

ist wirklich das, was du meintest. Das würde bedeuten, dass alle nicht ausgeblendeten Dateien und Verzeichnisse rekursiv mit grep versehen werden /(aber immer noch in ausgeblendeten Dateien und Verzeichnissen nachsehen).

Angenommen, Sie meinten:

grep -r -i 'the brown dog' /

Ein paar Dinge zu beachten:

  • Nicht alle grepImplementierungen werden unterstützt -r. Und unter denen, die dies tun, unterscheiden sich die Verhaltensweisen: Einige folgen beim Durchlaufen des Verzeichnisbaums Symlinks zu Verzeichnissen (was bedeutet, dass Sie möglicherweise mehrmals in derselben Datei suchen oder sogar in Endlosschleifen ausgeführt werden), andere nicht. Einige schauen in Gerätedateien (und es wird /dev/zerozum Beispiel einige Zeit in Anspruch nehmen) oder Pipes oder Binärdateien ..., andere nicht.
  • Es ist effizient, da es grepbeginnt, Dateien zu durchsuchen, sobald es sie entdeckt. Aber während es in einer Datei aussieht, sucht es nicht mehr nach weiteren Dateien, in denen gesucht werden kann (was in den meisten Fällen wahrscheinlich genauso gut ist).

Ihre:

find / -type f -exec grep -i 'the brown dog' {} \;

(Entfernt das, -rwas hier keinen Sinn machte) ist schrecklich ineffizient, weil Sie eins greppro Datei ausführen. ;sollte nur für Befehle verwendet werden, die nur ein Argument akzeptieren. Außerdem wird hier grepder Dateiname nicht gedruckt , da nur in einer Datei gesucht wird, sodass Sie nicht wissen, wo die Übereinstimmungen sind.

Sie suchen nicht in Gerätedateien, Pipes, Symlinks ..., Sie folgen keinen Symlinks, aber Sie suchen möglicherweise immer noch in Dingen wie /proc/mem.

find / -type f -exec grep -i 'the brown dog' {} +

wäre viel besser, weil so wenig grepBefehle wie möglich ausgeführt würden. Sie würden den Dateinamen erhalten, es sei denn, der letzte Lauf hat nur eine Datei. Dafür ist es besser zu verwenden:

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

oder mit GNU grep:

find / -type f -exec grep -Hi 'the brown dog' {} +

Beachten Sie, dass der grepVorgang erst gestartet wird, wenn findgenügend Dateien zum Kauen gefunden wurden, sodass es zu einer anfänglichen Verzögerung kommt. Und findwird nicht weiter nach weiteren Dateien suchen, bis die vorherige grepzurückgekehrt ist. Das Zuweisen und Weitergeben der großen Dateiliste hat einige (wahrscheinlich zu vernachlässigende) Auswirkungen. Alles in allem wird es also wahrscheinlich weniger effizient sein als ein grep -r, das nicht dem Symlink folgt oder nicht in Geräte hineinschaut.

Mit GNU-Tools:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

Wie oben beschrieben, werden so wenige grepInstanzen wie möglich ausgeführt, es findwird jedoch weiterhin nach weiteren Dateien gesucht, während der erste grepAufruf im ersten Stapel ausgeführt wird. Das kann ein Vorteil sein oder auch nicht. Wenn beispielsweise Daten auf rotierenden Festplatten gespeichert sind findund grepauf Daten zugegriffen wird, die an verschiedenen Stellen auf der Platte gespeichert sind, wird der Plattendurchsatz verlangsamt, indem der Plattenkopf sich ständig bewegt. In einem RAID - Setup (wo findund grepverschiedene Datenträger zugreifen kann) oder auf SSDs, das könnte einen positiven Unterschied machen.

In einem RAID-Setup können durch Ausführen mehrerer gleichzeitiger grep Aufrufe ebenfalls Verbesserungen erzielt werden. Immer noch mit GNU-Tools auf RAID1-Speicher mit 3 Festplatten,

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

könnte die Leistung erheblich steigern. Beachten Sie jedoch, dass der zweite Befehl greperst gestartet wird, wenn genügend Dateien gefunden wurden, um den ersten grepBefehl auszufüllen . Sie können eine -nOption hinzufügen xargs, damit dies früher geschieht (und weniger Dateien pro grepAufruf übergeben werden).

Beachten Sie auch, dass, wenn Sie die xargsAusgabe an etwas anderes als ein Endgerät umleiten , die grepss beginnen, ihre Ausgabe zu puffern, was bedeutet, dass die Ausgabe dieser greps wahrscheinlich falsch verschachtelt wird. Sie müssten sie verwenden stdbuf -oL(wo verfügbar wie unter GNU oder FreeBSD), um das zu umgehen (Sie haben möglicherweise immer noch Probleme mit sehr langen Zeilen (normalerweise> 4 KB)), oder jeder muss seine Ausgabe in eine separate Datei schreiben und diese verketten alles am Ende.

Hier ist die Zeichenfolge, nach der Sie suchen, fest (kein regulärer Ausdruck), daher kann die Verwendung der -FOption einen Unterschied bewirken (es ist unwahrscheinlich, dass grepImplementierungen bereits wissen, wie sie dies optimieren können).

Eine andere Sache, die einen großen Unterschied machen könnte, ist das Fixieren des Gebietsschemas auf C, wenn Sie sich in einem Mehrbyte-Gebietsschema befinden:

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

Um einen Blick hinein zu vermeiden /proc, /sys... verwenden Sie -xdevdie Dateisysteme, in denen Sie suchen möchten , und geben Sie sie an:

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

Oder kürzen Sie die Pfade, die Sie explizit ausschließen möchten:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +
Stéphane Chazelas
quelle
Ich nehme nicht an, dass mich jemand auf eine Ressource hinweisen oder erklären kann, was {} und + bedeuten. Es gibt nichts, was ich in den Manpages für exec, grep oder find auf der Solaris-Box sehen kann, die ich verwende. Verkettet nur die Shell Dateinamen und übergibt sie an grep?
3
@Poldie, das wird in der Beschreibung des -execPrädikats in der Solaris-Manpage
Stéphane Chazelas,
Ah ja. Ich bin meinem Namen nicht entgangen, als ich in der Manpage gesucht habe. Dein Link ist besser; Ich finde Manpages schrecklich zu lesen.
1
RAID1 mit 3 Festplatten? Wie seltsam ...
Tink
1
@tink, ja RAID1 befindet sich auf 2 oder mehr Festplatten. Mit 3 Festplatten im Vergleich zu 2 Festplatten erhöhen Sie die Redundanz und die Leseleistung, während die Schreibleistung in etwa gleich ist. Mit 3 statt mit 2 Festplatten können Sie also auch Fehler korrigieren. Wenn Sie eine der Kopien ein wenig umdrehen, können Sie feststellen, welche richtig ist, indem Sie alle 3 Kopien überprüfen, während Sie mit 2 Festplatten nicht feststellen können wirklich erzählen.
Stéphane Chazelas
13

Wenn die *in dem grepAufruf an Sie nicht wichtig ist , dann sollte die erste als nur eine Instanz effizienter grepgestartet und Gabeln sind nicht frei. In den meisten Fällen wird es sogar mit dem schneller sein, *aber in Randfällen könnte die Sortierung das umkehren.

Es kann auch andere sein find- grepStrukturen , die vor allem mit Dateien vielen kleinen besser funktionieren. Das gleichzeitige Lesen einer großen Anzahl von Dateieinträgen und Inodes kann zu einer Leistungsverbesserung bei rotierenden Medien führen.

Aber schauen wir uns die Syscall-Statistiken an:

finden

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

grep nur

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total
Hauke ​​Laging
quelle
1
Beim Durchsuchen eines gesamten Dateisystems sind die Gabeln vernachlässigbar. I / O ist das, was Sie reduzieren möchten.
Gilles
Obwohl es sich um einen Fehler aus dem OP handelt, der Vergleich nicht korrekt ist, sollten Sie das -rFlag von entfernen, grepwenn Sie verwenden find. Sie können feststellen, dass immer wieder dieselben Dateien durchsucht wurden, indem Sie deren Anzahl vergleichen open.
Qwertzguy
1
@qwertzguy, nein, das -rsollte harmlos sein, da die -type fGarantien keines der Argumente Verzeichnisse sind. Die Mehrfachnennungen open()sind eher auf die anderen Dateien zurückzuführen, die grepbei jedem Aufruf geöffnet wurden (Bibliotheken, Lokalisierungsdaten ...) (danke für die Bearbeitung meiner Antwort übrigens)
Stéphane Chazelas
5

Wenn Sie sich auf einer SSD befinden und die Suchzeit vernachlässigbar ist, können Sie GNU parallel verwenden:

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

Dadurch werden bis zu 8 grep-Prozesse gleichzeitig ausgeführt, je nachdem, was findgefunden wurde.

Dies wird ein Festplattenlaufwerk zerstören, aber eine SSD sollte ziemlich gut damit zurechtkommen.

Naftuli Kay
quelle
-1

Eine weitere Sache, die Sie in diesem Fall beachten sollten, ist folgende.

Enthält eines der Verzeichnisse, die grep rekursiv durchlaufen muss, mehr Dateien als die Dateieinstellung Ihres Systems ? (zB Anzahl offener Datei-Handles, Standard ist 1024 bei den meisten Linux-Distributionen)

Wenn ja, dann ist find definitiv der richtige Weg, da bestimmte Versionen von grep mit einem zu langen Fehler bombardiert werden, wenn ein Verzeichnis mit mehr Dateien als der maximalen Einstellung für die Handles für geöffnete Dateien gefunden wird.

Nur meine 2 ¢.

B.Kaatz
quelle
1
Warum sollte man grepbombardieren? Zumindest mit GNU grep, wenn Sie einen Pfad mit Trailing angeben /und ihn verwenden, werden -RSie einfach durch die Verzeichnisse iterieren. Die Shell wird nichts erweitern, es sei denn, Sie geben Shell-Globs. In dem gegebenen Beispiel ( /*) wird also nur der Inhalt der /Materie, nicht der Unterordner, die durch einfach aufgezählt werden grep, als Argument aus der Shell übergeben.
0xC0000022L
Nun, da das OP nach einer rekursiven Suche fragte (zB "grep -r -i 'the brown dog' / *"), habe ich GNUs grep (mindestens Version 2.9) Bombe gesehen mit: "- bash: / bin / grep: Argumentliste zu lang "Verwenden Sie die exakte Suche, die das OP für ein Verzeichnis verwendet hat, in dem sich über 140.000 Unterverzeichnisse befinden.
B. Kaatz