Рекурсивна grep vs find / -type f -exec grep {} \; Що ефективніше / швидше?


70

Що є більш ефективним для пошуку, які файли у всій файловій системі містять рядок: рекурсивний grep або знайти grep у виконанні exec? Я припускаю, що пошук виявиться більш ефективним, тому що ви можете принаймні виконати деяку фільтрацію, якщо знаєте розширення файлу або регулярний вираз, який відповідає імені файлу, але коли ви знаєте лише, -type fщо краще? GNU grep 2.6.3; знайти (GNU findutils) 4.4.2

Приклад:

grep -r -i 'the brown dog' /

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


1
Ефективність математики / інформатики / алгоритму не ґрунтується на думці.
Грегг Левенталь

Перевірте це. Хоч і не рекурсивно, це дасть зрозуміти, що краще. unix.stackexchange.com/questions/47983/…
Рамеш

8
@AvinashRaj він не просить думки. Він запитує, хто є більш ефективним та / або швидшим , а не який "кращим". Це абсолютно відповідне запитання, яке має єдину, конкретну відповідь, яка залежить від того, як ці дві програми виконують свою роботу і від того, що саме ви даєте їм шукати.
тердон

2
Зауважте, що -exec {} +форма буде робити менше вил, тому має бути швидше, ніж -exec {} \;. Можливо, вам потрібно буде додати -H(або -h) до grepпараметрів, щоб отримати точно рівномірний вихід.
Мікель

Ви, ймовірно, не хотіли -rваріанту grepдля другого
qwertzguy

Відповіді:


85

Я не впевнений:

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

це дійсно те, що ви мали на увазі. Це означає, що греп рекурсивно у всіх не прихованих файлах і dirs /(але все ж заглядайте всередині прихованих файлів та dirs всередині цих).

Якщо припустити, що ви мали на увазі:

grep -r -i 'the brown dog' /

Кілька речей, які слід зазначити:

  • Не всі grepреалізації підтримуються -r. І серед тих, хто це робить, поведінка відрізняється: дехто дотримується посилань на каталоги під час проходження дерева каталогів (це означає, що ви можете кілька разів шукати один і той же файл або навіть працювати в нескінченних циклах), деякі не будуть. Деякі заглянуть у файли пристроїв (і це займе досить багато часу, /dev/zeroнаприклад) або труби або двійкові файли ..., а деякі - не.
  • Це ефективно, оскільки grepпочинає огляд файлів, як тільки їх виявляє. Але хоча він виглядає у файлі, він більше не шукає більше файлів для пошуку (що, мабуть, так само добре в більшості випадків)

Ваше:

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

(видалено, -rщо тут не мало сенсу) жахливо неефективне, оскільки ви працюєте з одним grepфайлом. ;слід використовувати лише для команд, які приймають лише один аргумент. Більше того, оскільки він grepвиглядає лише в одному файлі, він не буде друкувати ім'я файлу, тож ви не знатимете, де збіги.

Ви не заглядаєте у файли пристроїв, труби, посилання ..., ви не переходите до посилань, але ви все ще потенційно переглядаєте такі речі /proc/mem.

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

було б набагато краще, тому що grepбуло б запущено якомога менше команд. Ви отримаєте ім'я файлу, якщо в останньому запуску не буде лише один файл. Для цього краще використовувати:

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

або з GNU grep:

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

Зауважте, що grepвін не буде запущений, поки findне знайде достатньо файлів, щоб він пережовувався, тому буде деяка початкова затримка. І findне буде шукати більше файлів, поки попередній grepне повернеться. Виділення та передача великого списку файлів має певний (ймовірно, незначний) вплив, тому, загалом, це, мабуть, буде менш ефективним, ніж grep -rте, що не слідує за посиланням або заглядом всередину пристроїв.

За допомогою інструментів GNU:

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

Як і вище, grepбуде запущено якомога менше екземплярів, але findбуде продовжено шукати більше файлів, тоді як перша grepвиклик шукає всередині першої партії. Це може бути, а може і не бути перевагою. Наприклад, якщо дані, що зберігаються на обертових жорстких дисках, findі grepдоступ до даних, що зберігаються в різних місцях на диску, сповільнить пропускну здатність диска, викликаючи постійне переміщення головки диска. У налаштуваннях RAID (де findі grepможуть отримати доступ до різних дисків) або на SSD, це може спричинити позитивні зміни.

У налаштуваннях RAID виконання декількох одночасних grep викликів також може покращити ситуацію. Ще з інструментами GNU на накопичувачі RAID1 з 3 дисками,

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

може значно підвищити продуктивність. Однак зауважте, що друга grepбуде запущена лише після того, як буде знайдено достатньо файлів для заповнення першої grepкоманди. Ви можете додати -nпараметр, щоб xargsце відбулося швидше (і передавати менше файлів за grepвиклик).

Також зауважте, що якщо ви перенаправляєте xargsвихід на що-небудь, крім термінального пристрою, то grepss почне буферизацію їх виводу, що означає, що вихід цих greps, ймовірно, буде неправильно переплетений. Вам доведеться використовувати їх stdbuf -oL(якщо вони доступні, як у GNU або FreeBSD), щоб обійти це (у вас все ще можуть виникнути проблеми з дуже довгими рядками (як правило,> 4KiB)) або записати свої результати в окремий файл і об'єднати їх. все врешті-решт.

Тут ви шукаєте рядок, який ви шукаєте, виправлений (а не регулярний вираз), тому використання -Fпараметра може змінити ситуацію (навряд чи, оскільки grepреалізації знають, як це вже оптимізувати).

Інша річ, яка може мати велику різницю, - це виправити локаль на C, якщо ви знаходитесь у багатобайтовій мові:

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

Щоб не заглядати всередину /proc, /sys..., використовуйте -xdevта вкажіть файлові системи, в яких потрібно шукати:

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

Або обріжте шляхи, які ви хочете явно виключити:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +

Я не думаю, що хтось може вказати мені на ресурс - або пояснити - що значить {} та +. На сторінках man для exec, grep або пошуку у вікні Solaris я не бачу нічого. Це просто оболонка, яка об'єднує імена файлів і передає їх у режим grep?

3
@Poldie, це чітко пояснено при описі -execприсудка на сторінці чоловіка Solaris
Stéphane Chazelas

Ага, так. Я не тікав від свого {char, шукаючи сторінки чоловіка. Ваше посилання краще; Сторінки чоловіка мені здаються жахливими для читання.

1
RAID1 w / 3 диски? Як це дивно ...
подумайте

1
@tink, так RAID1 є на 2 або більше дисках. За допомогою 3 дисків порівняно з 2 дисками ви збільшуєте надмірність та швидкість читання, тоді як продуктивність запису приблизно однакова. З 3 дисками на відміну від 2, це означає, що ви також можете виправити помилки, оскільки коли трохи перегортає одну з копій, ви зможете сказати, що правильно, перевіривши всі 3 копії, тоді як з 2 дисками, ви не можете дійсно скажи.
Стефан Шазелас

13

Якщо *в grepвиклику байдуже для вас , то перший повинен бути більш ефективним , так як тільки один екземпляр grepзапускається, і вилки не вільні. У більшості випадків це буде швидше, *але в кращих випадках сортування може це змінити.

Можуть бути й інші find- grepструктури, які краще працюють, особливо з багатьма невеликими файлами. Читання великої кількості записів та вводів файлів одночасно може покращити продуктивність на обертових носіях.

Але давайте подивимося на статистику системних викликів:

знайти

> 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

тільки греп

> 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

1
У масштабі пошуку всієї файлової системи вилки незначні. I / O - це те, що ви хочете зменшити.
Жиль

Хоча це помилка з ОП, порівняння є невірним, вам слід видалити -rпрапор grepпри використанні find. Ви можете бачити, що він знову і знову шукав ті самі файли, порівнюючи кількість того, openщо сталося.
qwertzguy

1
@qwertzguy, ні, це -rмає бути нешкідливим, оскільки -type fгарантії жодного з аргументів не є каталогами. Множинні open()s, швидше за все, зменшуються до інших файлів, відкритих grepпри кожному виклику (бібліотеки, дані локалізації ...) (спасибі за редагування моєї відповіді btw)
Stéphane Chazelas

5

Якщо ви знаходитесь на SSD і шукати час неважливо, ви можете використовувати паралель GNU:

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

Це дозволить виконувати до 8 греп-процесів одночасно на основі findзнайденого.

Це призведе до розбиття накопичувача на жорсткому диску, але SSD повинен досить добре впоратися з цим.


-1

Ще одна річ, яку слід розглянути на цьому, полягає в наступному.

Чи будь-який із каталогів, через які grep доведеться рекурсивно проходити, міститиме більше файлів, ніж параметр nofile вашої системи ? (наприклад, кількість ручок відкритого файлу, за замовчуванням 1024 для більшості дистрибутивів Linux)

Якщо так, то знайти , безумовно, шлях, оскільки деякі версії grep вибухнуть зі списком аргументів занадто довгою помилкою, коли він потрапляє в каталог з більшою кількістю файлів, ніж максимально налаштований розмір відкритого файлу.

Тільки мої 2 ¢.


1
Навіщо grepвибуху? Принаймні, з GNU grep, якщо ви надаєте шлях із заднім числом /та використовуєте -Rйого, просто перейдіть через каталоги. Панцир не збирається розширювати нічого , якщо не дати оболонки-кульки. Тож у наведеному прикладі ( /*) лише вміст /речовини, а не підпапки, які будуть просто перелічені grep, не передаються як аргумент із оболонки.
0xC0000022L

Ну, враховуючи, що ОП запитувала про пошук рекурсивно (наприклад, "grep -r -i 'коричнева собака" / * "), я бачив, як греп GNU (принаймні версія 2.9) вибухнув із:" - bash: / bin / grep: Список аргументів занадто довгий ", використовуючи точний пошук OP, який використовується в каталозі, в якому було понад 140 000 підкаталогів.
B.Kaatz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.