awk 'FNR == 1 { f1=f2=f3=0; }; /one/ { f1++ }; /two/ { f2++ }; /three/ { f3++ }; f1 && f2 && f3 { print FILENAME; nextfile; }' *
Если вы хотите автоматически обрабатывать файлы с gzip-сжатием, либо запустите это в цикле с
zcat
(медленно и неэффективно, потому что вы будете разветвлятьawk
много раз в цикле, один раз для каждого имени файла) или перепишите тот же алгоритм вperl
и используйте библиотечный модульIO :: Uncompress :: AnyUncompress
, который может распаковывать несколько различных типов сжатых файлов (gzip, zip, bzip2 , lzop). или в python, в котором также есть модули для обработки сжатых файлов.Вот версия
perl
, которая используетIO :: Uncompress :: AnyUncompress
, чтобы разрешить любое количество шаблонов и любое количество имен файлов (содержащих простой текст или сжатый текст).Все аргументы до
-
обрабатываются как шаблоны поиска. Все аргументы после-
рассматриваются как имена файлов. Примитивная, но эффективная обработка опций для этой работы. Лучшая обработка параметров (например, поддержка параметра-i
для поиска без учета регистра) может быть достигнута с помощью модулейGetopt :: Std
илиGetopt :: Long
. .Запустите его так:
$ ./arekolek.pl one two three -- *.gz *.txt 1.txt.gz 4.txt.gz 5.txt.gz 1.txt 4.txt 5.txt
(Я не буду перечислять здесь файлы
{1..6} .txt.gz
и{1..6} .txt
... они просто содержат некоторые или все слова "один" "два" "три" "четыре" "пять" и "шесть" для тестирования. Файлы, перечисленные в выходных данных выше, ДОЛЖНЫ содержать все три шаблона поиска.Проверьте это самостоятельно на своих данных)#! /usr/bin/perl use strict; use warnings; use IO::Uncompress::AnyUncompress qw(anyuncompress $AnyUncompressError) ; my %patterns=(); my @filenames=(); my $fileargs=0; # all args before '--' are search patterns, all args after '--' are # filenames foreach (@ARGV) { if ($_ eq '--') { $fileargs++ ; next }; if ($fileargs) { push @filenames, $_; } else { $patterns{$_}=1; }; }; my $pattern=join('|',keys %patterns); $pattern=qr($pattern); my $p_string=join('',sort keys %patterns); foreach my $f (@filenames) { #my $lc=0; my %s = (); my $z = new IO::Uncompress::AnyUncompress($f) or die "IO::Uncompress::AnyUncompress failed: $AnyUncompressError\n"; while ($_ = $z->getline) { #last if ($lc++ > 100); my @matches=( m/($pattern)/og); next unless (@matches); map { $s{$_}=1 } @matches; my $m_string=join('',sort keys %s); if ($m_string eq $p_string) { print "$f\n" ; last; } } }
Хэш
% шаблонов
содержит полный набор шаблонов, которые файлы должны содержать хотя бы по одному из каждого члена$ _ pstring
- строка, содержащая отсортированные ключи этого хэша. Строка$ pattern
содержит предварительно скомпилированное регулярное выражение, также построенное из хэша% patterns
.
$ pattern
сравнивается с каждой строкой каждого входного файла (с использованием модификатора/ o
для компиляции$ pattern
только один раз, поскольку мы знаем, что он никогда не изменится во время выполнения), аmap ()
используется для построения хэша (% s), содержащего совпадения для каждого файла.Всякий раз, когда все шаблоны были замечены в текущем файле (путем сравнения if
$ m_string
(отсортированные ключи в% s
) равны$ p_string
]), напечатайте имя файла и перейдите к следующему файлу.Это не особенно быстрое решение, но оно не является необоснованно медленным. Первая версия занимала 4 минуты 58 секунд на поиск трех слов в сжатых файлах журнала объемом 74 МБ (всего 937 МБ без сжатия). Текущая версия занимает 1 мин. 13 сек. Вероятно, можно было бы сделать дальнейшую оптимизацию.
Одна очевидная оптимизация заключается в использовании этого вместе с
xargs
-P
aka- max-procs
для выполнения нескольких поисков по подмножествам файлы параллельно. Для этого вам нужно подсчитать количество файлов и разделить на количество ядер / процессоров / потоков, которые есть в вашей системе (и округлить, добавив 1). напримерв моем наборе образцов выполнялся поиск 269 файлов, а в моей системе 6 ядер (AMD 1090T), поэтому:patterns=(one two three) searchpath='/var/log/apache2/' cores=6 filecount=$(find "$searchpath" -type f -name 'access.*' | wc -l) filespercore=$((filecount / cores + 1)) find "$searchpath" -type f -print0 | xargs -0r -n "$filespercore" -P "$cores" ./arekolek.pl "${patterns[@]}" --
При такой оптимизации потребовалось всего 23 секунды, чтобы найти все 18 совпадающих файлов. Конечно, то же самое можно сделать и с любым другим решением. ПРИМЕЧАНИЕ. Порядок имен файлов, перечисленных в выходных данных, будет другим, поэтому, возможно, впоследствии потребуется их сортировка, если это имеет значение.
Как отмечает @arekolek, несколько
zgrep
сfind -exec
илиxargs
могут сделать это значительно быстрее, но этот сценарий имеет то преимущество, что поддерживает любое количество шаблонов для поиска и может работать с несколькими различными типами сжатия.Если сценарий ограничен проверкой только первых 100 строк каждого файла, он проходит все из них (в моем образце размером 74 МБ из 269 файлов) за 0,6 секунды. Если это полезно в некоторых случаях, его можно преобразовать в параметр командной строки (например,
-l 100
), но при этом существует риск не найти всех совпадающих файлов.Кстати, согласно странице руководства для
IO :: Uncompress :: AnyUncompress
, поддерживаются следующие форматы сжатия:
- zlib RFC 1950 ,
- deflate ] RFC 1951 (необязательно),
- gzip RFC 1952 ,
- zip,
- bzip2,
- lzop,
- lzf,
- lzma,
- xz
Последняя (надеюсь) оптимизация. Используя модуль
PerlIO :: gzip
(упакованный в debian какlibperlio-gzip-perl
) вместоIO :: Uncompress :: AnyUncompress
, я получил время примерно до 3.1 секунда для обработки моих 74 МБ файлов журнала. Были также некоторые небольшие улучшения за счет использования простого хеша вместоSet :: Scalar
(что также позволило сэкономить несколько секунд с версиейIO :: Uncompress :: AnyUncompress
).
PerlIO :: gzip
был рекомендован как самый быстрый perl gunzip в https: // stackoverflow.com / a / 1539271/137158 (найдено с помощью поиска в Google по запросуperl fast gzip распаковать
)Использование
xargs -P
с этим нисколько не улучшило его . Фактически, казалось, что это даже замедлило его где-то от 0,1 до 0,7 секунды. (Я пробовал четыре запуска, и моя система выполняет другие функции в фоновом режиме, которые изменяют время)Цена в том, что эта версия скрипта может обрабатывать только файлы, сжатые и сжатые в формате gzip. Скорость против гибкости: 3,1 секунды для этой версии против 23 секунд для версии
IO :: Uncompress :: AnyUncompress
с оболочкойxargs -P
(или 1 мин. 13 с безxargs -P
).#! /usr/bin/perl use strict; use warnings; use PerlIO::gzip; my %patterns=(); my @filenames=(); my $fileargs=0; # all args before '--' are search patterns, all args after '--' are # filenames foreach (@ARGV) { if ($_ eq '--') { $fileargs++ ; next }; if ($fileargs) { push @filenames, $_; } else { $patterns{$_}=1; }; }; my $pattern=join('|',keys %patterns); $pattern=qr($pattern); my $p_string=join('',sort keys %patterns); foreach my $f (@filenames) { open(F, "<:gzip(autopop)", $f) or die "couldn't open $f: $!\n"; #my $lc=0; my %s = (); while (
) { #last if ($lc++ > 100); my @matches=(m/($pattern)/ogi); next unless (@matches); map { $s{$_}=1 } @matches; my $m_string=join('',sort keys %s); if ($m_string eq $p_string) { print "$f\n" ; close(F); last; } } }