Grep в пару тысяч файлов


13

У меня есть каталог с около 26 000 файлов, и мне нужно grep во всех этих файлах. Проблема в том, что мне это нужно как можно быстрее, поэтому не идеально делать скрипт, в котором grep будет брать имя одного файла из команды поиска и записывать совпадения в файл. Прежде чем "список аргументов слишком длинный", потребовалось около 2 минут, чтобы собрать все эти файлы. Есть идеи как это сделать? edit: есть скрипт, который постоянно создает новые файлы, поэтому невозможно поместить все файлы в разные каталоги.


1
использовать findс xargsилиgrep -R
Eddy_Em

Это работает хорошо, но это занимает 10 минут ...
user2778979

Ответы:


19

С find:

cd /the/dir
find . -type f -exec grep pattern {} +

( -type fэто поиск только в обычных файлах (также исключая символические ссылки, даже если они указывают на обычные файлы). Если вы хотите искать в любом типе файлов, кроме каталогов (но будьте осторожны, есть некоторые типы файлов, такие как fifos или / dev / zero, которые как правило, вы не хотите читать), замените его -type fна GNU-специфичный ! -xtype d( -xtype dсоответствует файлам типа directory после разрешения символической ссылки)).

С GNU grep:

grep -r pattern /the/dir

(но учтите, что если у вас нет последней версии GNU grep, она будет следовать символическим ссылкам при переходе в каталоги). Нестандартные файлы не будут искать, если вы не добавите -D readопцию. Последние версии GNU grepвсе равно не будут искать внутри символических ссылок.

Очень старые версии GNU findне поддерживают стандартный {} +синтаксис, но там вы можете использовать нестандартный:

cd /the/dir &&
  find . -type f -print0 | xargs -r0 grep pattern

Представления могут быть связаны с вводом / выводом. Это время для поиска будет временем, необходимым для чтения всех этих данных из хранилища.

Если данные находятся на избыточном дисковом массиве, чтение нескольких файлов за один раз может повысить производительность (и в противном случае их можно ухудшить). Если производительность не связана с вводом / выводом (потому что, например, все данные находятся в кеше), и у вас есть несколько процессоров, параллельная работа также grepsможет помочь. Вы можете сделать это с помощью GNU xargs«s -Pварианта.

Например, если данные находятся в массиве RAID1 с 3 дисками или если данные находятся в кеше, и у вас есть 3 ЦП, время которых можно сэкономить:

cd /the/dir &&
  find . -type f -print0 | xargs -n1000 -r0P3 grep pattern

(здесь используется, -n1000чтобы порождать новые grepкаждые 1000 файлов, до 3 параллельно работающих одновременно).

Однако обратите внимание, что если выходные данные grepперенаправлены, вы получите плохо чередующиеся выходные данные 3 grepпроцессов, и в этом случае вы можете запустить его как:

find . -type f -print0 | stdbuf -oL xargs -n1000 -r0P3 grep pattern

(в недавней системе GNU или FreeBSD) или используйте --line-bufferedопцию GNU grep.

Если patternэто фиксированная строка, добавление -Fопции может улучшить ситуацию.

Если это не многобайтовые символьные данные или если для сопоставления с этим шаблоном не имеет значения, являются ли данные многобайтовыми символами или нет, тогда:

cd /the/dir &&
  LC_ALL=C grep -r pattern .

может значительно улучшить производительность.

Если вам приходится часто выполнять такие поиски, вы можете проиндексировать свои данные с помощью одной из многочисленных поисковых систем.


3

26000 файлов в одном каталоге - это много для большинства файловых систем. Вероятно, значительная часть времени уходит на чтение этого большого каталога. Подумайте о том, чтобы разбить его на более мелкие каталоги с несколькими сотнями файлов в каждом.

Вызов findне может объяснить плохую производительность, если вы не сделаете это неправильно. Это быстрый способ обхода каталога и гарантии того, что вы не рискуете выполнить слишком длинную командную строку. Убедитесь, что вы используете -exec grep PATTERN {} +, который упаковывает столько файлов, сколько может для вызова команды, а не -exec grep PATTERN {} \;, который выполняется grepодин раз для файла: выполнение команды один раз для файла, вероятно, будет значительно медленнее.


Спасибо, я буду гуглить кое-что об этом и, вероятно, я разделю это. Я сделал именно то, о чем вы пишете, и это заняло в 3 раза больше времени, чем только grep ...
user2778979

Жиль, вы говорите, что производительность будет существенно отличаться для 26 000 файлов в одном каталоге по сравнению с 26 000 файлов, распределенных, скажем, по 100 каталогам?
user001

1
@ user001 Да. Насколько они различаются, зависит от файловой системы и, возможно, от основного хранилища, но я ожидаю, что любая файловая система будет измеримо быстрее с 260 файлами в каждой из 100 директорий по сравнению с 26000 файлами в одной директории.
Жиль "ТАК ... перестать быть злым"

Благодарю за разъяснение. Я задал дополнительный вопрос по этому вопросу, чтобы понять причину расхождения.
user001

0

Если вам нужно выполнить grep ВСЕХ файлов несколько раз (как вы сказали, запустив скрипт), я бы посоветовал посмотреть на ram-диски, скопировать все файлы туда, а затем grep файлы несколько раз, это ускорит ваш поиск в несколько раз. по крайней мере, 100x.

Вам просто нужно достаточно барана. Иначе, вы должны посмотреть на индексацию файлов, например. в базу данных lucene или nosql, а затем запускает запросы к ним.


Как уже отмечалось, это не помогает тому факту, что существует слишком много файлов для запуска grep. Также есть смысл: «Существует скрипт, который постоянно создает новые файлы, поэтому невозможно поместить все файлы в разные каталоги».
Джефф Шаллер

-2

Все файлы в каталоге

grep 'search string' *

с рекурсивно

grep -R 'search string' *

Хотите разработать -1?
Маркус

4
Я не понизил голос, но у вас есть несколько проблем: OP упомянул «arg list too long», который ваш первый не исправит и, вероятно, именно этим OP занимался раньше. Второй не помогает в этом отношении (помог бы, если бы вы использовали .вместо *). *исключит точечные файлы (хотя с -R, а не в рекурсивных каталогах). -R в отличие от -r следует символическим ссылкам даже в последних версиях GNU grep. У вас также будет проблема с файлами в текущем каталоге, имя которых начинается с-
Стефан Шазелас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.