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' -Pska ' --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сначала только файлы.