awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Если вы хотите автоматически обрабатывать сжатые файлы, либо запустите это в цикле с 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;
}
}
}
Хеш %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 драм), поэтому:
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 строк каждого файла, он проходит через все из них (в моем примере из 269 файлов размером 74 МБ) за 0,6 секунды. Если это полезно в некоторых случаях, его можно включить в параметр командной строки (например -l 100
), но есть риск не найти все подходящие файлы.
Кстати, согласно справочной странице IO::Uncompress::AnyUncompress
, поддерживаемые форматы сжатия:
Одна последняя (я надеюсь) оптимизация. Используя вместо этого PerlIO::gzip
модуль (упакованный в debian as libperlio-gzip-perl
), IO::Uncompress::AnyUncompress
я сократил время обработки файлов до 74 МБ до 3,1 секунды . Были также некоторые небольшие улучшения с использованием простого хеша Set::Scalar
(что также сэкономило несколько секунд с IO::Uncompress::AnyUncompress
версией).
PerlIO::gzip
был рекомендован как самый быстрый Perl Gunzip в /programming//a/1539271/137158 (найдено с помощью поиска Google perl fast gzip decompress
)
Использование xargs -P
с этим не улучшило это вообще. На самом деле, казалось, что он даже замедлился на 0,1–0,7 секунды. (Я пробовал четыре запуска, и моя система делает другие вещи в фоновом режиме, которые изменят время)
Цена в том, что эта версия скрипта может обрабатывать только сжатые и несжатые файлы. Скорость против гибкости: 3,1 секунды для этой версии против 23 секунд для IO::Uncompress::AnyUncompress
версии с xargs -P
оберткой (или 1m13s без 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 (<F>) {
#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;
}
}
}
gzip
дружелюбными,zcat
сначала только файлы.