it-swarm.com.de

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

Was ist effizienter, um herauszufinden, welche Dateien in einem gesamten Dateisystem eine Zeichenfolge enthalten: rekursives grep oder find mit grep in einer exec-Anweisung? Ich gehe davon aus, dass find effizienter ist, da Sie zumindest filtern können, wenn Sie die Dateierweiterung oder einen regulären Ausdruck kennen, der dem Dateinamen entspricht, aber nur -type f Welches ist besser? 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' {} \;

73
Gregg Leventhal

Ich bin mir nicht sicher:

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

ist wirklich das, was du meintest. Das würde bedeuten, dass grep in allen nicht versteckten Dateien und Verzeichnissen in / Rekursiv grep (aber immer noch in versteckte Dateien und Verzeichnisse in diesen schauen).

Angenommen, Sie meinten:

grep -r -i 'the brown dog' /

Ein paar Dinge zu beachten:

  • Nicht alle grep Implementierungen unterstützen -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 zum Beispiel in /dev/zero Einige Zeit dauern) oder in Pipes oder Binärdateien ... andere nicht.
  • Es ist effizient, da grep beginnt, in Dateien zu suchen, sobald sie entdeckt werden. 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 den -r, der hier keinen Sinn ergab) ist schrecklich ineffizient, weil Sie ein grep pro Datei ausführen. ; Sollte nur für Befehle verwendet werden, die nur ein Argument akzeptieren. Da grep nur in einer Datei angezeigt wird, wird der Dateiname hier nicht gedruckt, sodass Sie nicht wissen, wo die Übereinstimmungen liegen.

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

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

wäre viel besser, da so wenige grep Befehle wie möglich ausgeführt würden. Sie erhalten den Dateinamen, es sei denn, der letzte Lauf enthält 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 grep erst gestartet wird, wenn find genügend Dateien zum Kauen gefunden hat, sodass es zu einer anfänglichen Verzögerung kommt. Und find sucht erst dann nach weiteren Dateien, wenn das vorherige grep zurückgekehrt ist. Das Zuweisen und Übergeben der großen Dateiliste hat einige (wahrscheinlich vernachlässigbare) Auswirkungen. Alles in allem wird es wahrscheinlich weniger effizient sein als ein grep -r, Der keinem Symlink folgt oder in Geräte schaut.

Mit GNU Tools:

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

Wie oben werden so wenige grep Instanzen wie möglich ausgeführt, aber find sucht weiterhin nach weiteren Dateien, während der erste Aufruf von grep innerhalb des ersten Stapels liegt. Das kann jedoch ein Vorteil sein oder auch nicht. Wenn beispielsweise Daten auf rotierenden Festplatten gespeichert sind, verlangsamen find und grep der Zugriff auf Daten, die an verschiedenen Stellen auf der Festplatte gespeichert sind, den Festplattendurchsatz, indem der Festplattenkopf ständig bewegt wird. In einem RAID-Setup (in dem find und grep möglicherweise auf verschiedene Festplatten zugreifen) oder auf SSDs kann dies einen positiven Unterschied bewirken.

In einem RAID-Setup kann das Ausführen mehrerer gleichzeitigergrep Aufrufe ebenfalls die Dinge verbessern. Immer noch mit GNU Tools auf RAID1-Speicher mit 3 Festplatten,

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

kann die Leistung erheblich steigern. Beachten Sie jedoch, dass das zweite grep erst gestartet wird, wenn genügend Dateien gefunden wurden, um den ersten grep -Befehl auszufüllen. Sie können xargs eine -n - Option hinzufügen, damit dies früher geschieht (und weniger Dateien pro grep -Aufruf übergeben).

Beachten Sie auch, dass, wenn Sie die Ausgabe von xargs auf etwas anderes als ein Endgerät umleiten, die grepss ihre Ausgabe puffern, was bedeutet, dass die Ausgabe dieser greps wahrscheinlich wird falsch verschachtelt sein. Sie müssten stdbuf -oL (Wo verfügbar wie auf GNU oder FreeBSD)) verwenden, um dies zu umgehen (möglicherweise haben Sie immer noch Probleme mit sehr langen Leitungen (normalerweise> 4 KB) )) oder lassen Sie jede ihre Ausgabe in eine separate Datei schreiben und sie am Ende alle verketten.

Hier ist die gesuchte Zeichenfolge festgelegt (kein regulärer Ausdruck), sodass die Verwendung der Option -F Einen Unterschied machen kann (unwahrscheinlich, da grep-Implementierungen bereits wissen, wie dies optimiert werden kann).

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

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

Um zu vermeiden, dass Sie in /proc, /sys ... schauen, verwenden Sie -xdev Und geben Sie die Dateisysteme an, in denen Sie suchen möchten:

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

Oder Beschneiden 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 {} +
93

Wenn der * Im Aufruf von grep für Sie nicht wichtig ist, sollte der erste effizienter sein, da nur eine Instanz von grep gestartet wird und Gabeln nicht frei sind. In den meisten Fällen ist es sogar mit * Schneller, aber in Edge-Fällen könnte die Sortierung dies umkehren.

Möglicherweise gibt es andere find-grep -Strukturen, die insbesondere bei vielen kleinen Dateien besser funktionieren. Das gleichzeitige Lesen großer Mengen von Dateieinträgen und Inodes kann zu einer Leistungsverbesserung bei rotierenden Medien führen.

Aber schauen wir uns die Syscall-Statistiken an:

find

> 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

nur grep

> 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
14
Hauke Laging

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

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 find gefunden hat.

Dies wird ein Festplattenlaufwerk beschädigen, aber eine SSD sollte ziemlich gut damit umgehen können.

5
Naftuli Kay