Я ищу способ перечислить все файлы в каталоге, которые содержат полный набор ключевых слов, которые я ищу, в любом месте файла.
Таким образом, ключевые слова не должны появляться в одной строке.
Один из способов сделать это будет:
grep -l one $(grep -l two $(grep -l three *))
Три ключевых слова - это просто пример, с таким же успехом может быть два или четыре, и так далее.
Второй способ, который я могу придумать:
grep -l one * | xargs grep -l two | xargs grep -l three
Третий метод, который появился в другом вопросе , был бы:
find . -type f \
-exec grep -q one {} \; -a \
-exec grep -q two {} \; -a \
-exec grep -q three {} \; -a -print
Но это определенно не то направление, куда я иду. Я хочу что - то , что требует меньше печатать, и , возможно , только один вызов grep
, awk
, perl
или аналогичный.
Например, мне нравится, как можно awk
сопоставлять строки, содержащие все ключевые слова , например:
awk '/one/ && /two/ && /three/' *
Или напечатайте только имена файлов:
awk '/one/ && /two/ && /three/ { print FILENAME ; nextfile }' *
Но я хочу найти файлы, в которых ключевые слова могут находиться где угодно в файле, не обязательно в одной строке.
Предпочтительные решения будут дружественными к gzip, например, grep
имеет zgrep
вариант, который работает с сжатыми файлами. Почему я упоминаю об этом, так это то, что некоторые решения могут не работать должным образом, учитывая это ограничение. Например, в awk
примере печати совпадающих файлов вы не можете просто сделать:
zcat * | awk '/pattern/ {print FILENAME; nextfile}'
Вам необходимо значительно изменить команду, например:
for f in *; do zcat $f | awk -v F=$f '/pattern/ { print F; nextfile }'; done
Таким образом, из-за ограничений вам нужно звонить awk
много раз, даже если вы можете сделать это только один раз с несжатыми файлами. И, конечно, было бы приятнее просто сделать zawk '/pattern/ {print FILENAME; nextfile}' *
и получить тот же эффект, поэтому я бы предпочел решения, которые позволяют это.
gzip
дружелюбными,zcat
сначала только файлы.grep
решения легко адаптируются, просто добавляя префиксы кgrep
вызовамz
, и мне не нужно обрабатывать имена файлов.grep
. AFAIK, толькоgrep
иcat
есть стандартные «z-варианты». Я не думаю, что вы получите что-то проще, чем использоватьfor f in *; do zcat -f $f ...
решение. Все остальное должно быть полной программой, которая проверяет форматы файлов перед открытием или использует библиотеку, чтобы сделать то же самое.Ответы:
Если вы хотите автоматически обрабатывать сжатые файлы, либо запустите это в цикле с
zcat
(медленно и неэффективно, потому что вы будете разветвлятьсяawk
много раз в цикле, по одному разу для каждого имени файла), либо перепишите тот же алгоритмperl
и используйтеIO::Uncompress::AnyUncompress
модуль библиотеки, который может распакуйте несколько различных типов сжатых файлов (gzip, zip, bzip2, lzop). или в python, который также имеет модули для обработки сжатых файлов.Вот
perl
версия, которая используетсяIO::Uncompress::AnyUncompress
для разрешения любого количества шаблонов и любого количества имен файлов (содержащих либо простой текст, либо сжатый текст).Все аргументы ранее
--
рассматриваются как шаблоны поиска. Все аргументы после--
рассматриваются как имена файлов. Примитивный, но эффективный вариант обработки для этой работы. Лучшая обработка опций (например, для поддержки-i
опции для поиска без учета регистра) может быть достигнута с помощью модулейGetopt::Std
илиGetopt::Long
.Запустите это так:
(Я не буду перечислять файлы
{1..6}.txt.gz
и{1..6}.txt
здесь ... они просто содержат некоторые или все слова «один», «два», «три», «четыре», «пять» и «шесть» для тестирования. Файлы, перечисленные в выходных данных выше СЛЕДУЕТ содержать все три шаблона поиска. Попробуйте сами, используя свои собственные данные)Хеш
%patterns
содержит полный набор шаблонов, которые файлы должны содержать, по крайней мере, один из каждого члена$_pstring
представляет собой строку, содержащую отсортированные ключи этого хеша. Строка$pattern
содержит предварительно скомпилированное регулярное выражение, также построенное из%patterns
хеша.$pattern
сравнивается с каждой строкой каждого входного файла (используя/o
модификатор для компиляции$pattern
только один раз, поскольку мы знаем, что он никогда не изменится во время выполнения), иmap()
используется для построения хэша (% s), содержащего совпадения для каждого файла.Всякий раз, когда все шаблоны были замечены в текущем файле (сравнивая if
$m_string
(отсортированные ключи в%s
) равны$p_string
), выведите имя файла и перейдите к следующему файлу.Это не очень быстрое решение, но не слишком медленное. Первой версии потребовалось 4 млн. 58 секунд для поиска трех слов в файлах сжатых журналов объемом 74 МБ (всего без сжатия - 937 МБ). Эта текущая версия занимает 1m13s. Вероятно, возможны дальнейшие оптимизации.
Одна очевидная оптимизация состоит в том, чтобы использовать это вместе с
xargs
'-P
ska '--max-procs
для параллельного запуска множественного поиска по подмножествам файлов. Чтобы сделать это, вам нужно посчитать количество файлов и разделить на количество ядер / процессоров / потоков, которые есть в вашей системе (и округлить, добавив 1). Например, в моем наборе выборок было найдено 269 файлов, а в моей системе 6 ядер (1090 драм), поэтому:С этой оптимизацией потребовалось всего 23 секунды, чтобы найти все 18 подходящих файлов. Конечно, то же самое можно сделать с любым другим решением. ПРИМЕЧАНИЕ. Порядок имен файлов, перечисленных в выходных данных, будет другим, поэтому, возможно, потребуется отсортировать их позже, если это имеет значение.
Как отметил @arekolek, множественные файлы
zgrep
сfind -exec
илиxargs
могут выполнять это значительно быстрее, но этот сценарий имеет преимущество, заключающееся в поддержке любого количества шаблонов для поиска и может работать с несколькими различными типами сжатия.Если сценарий ограничен проверкой только первых 100 строк каждого файла, он проходит через все из них (в моем примере из 269 файлов размером 74 МБ) за 0,6 секунды. Если это полезно в некоторых случаях, его можно включить в параметр командной строки (например
-l 100
), но есть риск не найти все подходящие файлы.Кстати, согласно справочной странице
IO::Uncompress::AnyUncompress
, поддерживаемые форматы сжатия:Одна последняя (я надеюсь) оптимизация. Используя вместо этого
PerlIO::gzip
модуль (упакованный в debian aslibperlio-gzip-perl
),IO::Uncompress::AnyUncompress
я сократил время обработки файлов до 74 МБ до 3,1 секунды . Были также некоторые небольшие улучшения с использованием простого хешаSet::Scalar
(что также сэкономило несколько секунд сIO::Uncompress::AnyUncompress
версией).PerlIO::gzip
был рекомендован как самый быстрый Perl Gunzip в /programming//a/1539271/137158 (найдено с помощью поиска Googleperl fast gzip decompress
)Использование
xargs -P
с этим не улучшило это вообще. На самом деле, казалось, что он даже замедлился на 0,1–0,7 секунды. (Я пробовал четыре запуска, и моя система делает другие вещи в фоновом режиме, которые изменят время)Цена в том, что эта версия скрипта может обрабатывать только сжатые и несжатые файлы. Скорость против гибкости: 3,1 секунды для этой версии против 23 секунд для
IO::Uncompress::AnyUncompress
версии сxargs -P
оберткой (или 1m13s безxargs -P
).источник
for f in *; do zcat $f | awk -v F=$f '/one/ {a++}; /two/ {b++}; /three/ {c++}; a&&b&&c { print F; nextfile }'; done
работает нормально, но на самом деле занимает в 3 раза больше времени, чем моеgrep
решение, и на самом деле более сложный.apt-get install libset-scalar-perl
использовать сценарий. Но это, кажется, не заканчивается в любое разумное время.Установите разделитель записей
.
так,awk
чтобы весь файл обрабатывался как одна строка:Аналогично с
perl
:источник
for f in *; do zcat $f | awk -v RS='.' -v F=$f '/one/ && /two/ && /three/ { print F }'; done
ничего не выводит.zcat -f "$f"
если некоторые файлы не сжаты.awk -v RS='.' '/bfs/&&/none/&&/rgg/{print FILENAME}' greptest/*.txt
до сих пор не возвращает результатов, в то время какgrep -l rgg $(grep -l none $(grep -l bfs greptest/*.txt))
возвращает ожидаемые результаты.Для сжатых файлов вы можете зациклить каждый файл и распаковать в первую очередь. Затем, с немного измененной версией других ответов, вы можете сделать:
Сценарий Perl завершится со
0
статусом (успех), если все три строки были найдены.}{
Является Perl стенографии дляEND{}
. Все, что следует за ним, будет выполнено после того, как весь ввод был обработан. Таким образом, скрипт выйдет с состоянием выхода, отличным от 0, если не все строки были найдены. Следовательно,&& printf '%s\n' "$f"
имя файла будет напечатано, только если все три найдены.Или, чтобы избежать загрузки файла в память:
Наконец, если вы действительно хотите сделать все это в сценарии, вы можете сделать:
Сохраните приведенный выше скрипт как
foo.pl
где-то в вашем$PATH
, сделайте его исполняемым и запустите его так:источник
Из всех предложенных решений мое самое оригинальное решение с использованием grep - самое быстрое, заканчивающееся за 25 секунд. Недостатком является то, что добавлять и удалять ключевые слова утомительно. Поэтому я придумал скрипт (дублированный
multi
), который имитирует поведение, но позволяет изменить синтаксис:Так что теперь написание
multi grep one two three -- *
эквивалентно моему первоначальному предложению и выполняется в то же время. Я также могу легко использовать его для сжатых файлов, используяzgrep
вместо этого первый аргумент.Другие решения
Я также экспериментировал со скриптом Python, используя две стратегии: поиск по всем ключевым словам построчно и поиск по всему файлу по ключевым словам. Вторая стратегия была быстрее в моем случае. Но это было медленнее, чем просто использование
grep
, заканчиваясь за 33 секунды. Строковое соответствие ключевых слов завершено за 60 секунд.Сценарий дается terdon закончил в 54 секунд. На самом деле это заняло 39 секунд простоя, потому что мой процессор двухъядерный Что интересно, потому что мой скрипт на Python занял 49 секунд времени на стене (и
grep
был 29 секунд).Сценарий КАН не удалось завершить в разумные сроки, даже в меньшем количестве файлов , которые были обработаны с
grep
4 секунды, так что я должен был убить его.Но его оригинальное
awk
предложение, хотя и медленнее, чемgrep
есть, имеет потенциальное преимущество. В некоторых случаях, по моему опыту, можно ожидать, что все ключевые слова должны появиться где-нибудь в заголовке файла, если они вообще есть в файле. Это дает этому решению значительное повышение производительности:Заканчивается за четверть секунды, в отличие от 25 секунд.
Конечно, у нас может не быть преимущества поиска по ключевым словам, которые, как известно, встречаются в начале файлов. В этом случае решение без
NR>100 {exit}
занимает 63 секунды (50 секунд времени стены).Несжатые файлы
Между моим
grep
решением иawk
предложением cas нет существенной разницы во времени выполнения , обе выполняются за доли секунды.Обратите внимание, что инициализация переменной
FNR == 1 { f1=f2=f3=0; }
обязательна в этом случае для сброса счетчиков для каждого последующего обработанного файла. Таким образом, это решение требует редактирования команды в трех местах, если вы хотите изменить ключевое слово или добавить новые. С другой стороны,grep
вы можете просто добавить| xargs grep -l four
или изменить ключевое слово, которое вы хотите.Недостатком
grep
решения, использующего подстановку команд, является то, что оно будет зависать, если где-либо в цепочке, перед последним шагом, не найдены подходящие файлы. Это не влияет наxargs
вариант, потому что канал будет прерван, как толькоgrep
вернет ненулевой статус. Я обновил свой сценарий, чтобы использовать его,xargs
поэтому мне не нужно обрабатывать его самостоятельно, что делает сценарий проще.источник
not all(p in text for p in patterns)
not
), и он закончился за 32 секунды, так что не так много улучшений, но это, безусловно, более читабельно.PerlIO::gzip
а неIO::Uncompress::AnyUncompress
. теперь требуется всего 3,1 секунды вместо 1 м13 с для обработки моих 74 МБ файлов журнала.eval $(lesspipe)
(например, в вашем.profile
и т. Д.), Вы можете использоватьless
вместо этого,zcat -f
и вашаfor
обертка циклаawk
будет в состоянии обработать любой тип файла, которыйless
может (gzip, bzip2, xz и другие) .... less может определить, является ли stdout каналом, и просто выведет поток на стандартный вывод, если он есть.Другой вариант - подавать слова по одному, чтобы
xargs
он работалgrep
с файлом.xargs
Сам может быть выполнен, чтобы выйти, как только вызовgrep
возврата возвращается, возвращаясь255
к нему (см.xargs
документацию). Конечно, нерест раковин и разветвление, вовлеченные в это решение, вероятно, значительно замедлят егои зациклить
источник
_
иfile
? Будет ли этот поиск в нескольких файлах передаваться в качестве аргумента и возвращать файлы, содержащие все ключевые слова?_
, что он передается как$0
порожденная оболочка - это будет отображаться как имя команды в выходных данныхps
- я бы отложил это до мастера здесь