awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Якщо ви хочете автоматично обробляти gzipped файли, виконайте це у циклі 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), що містить відповідність для кожного файлу.
Всякий раз, коли всі шаблони були помічені в поточному файлі (порівнявши, якщо $m_string
(відсортовані клавіші в %s
) дорівнює $p_string
), надрукуйте ім'я файлу та перейдіть до наступного файлу.
Це не особливо швидке рішення, але не є надмірно повільним. Перша версія зайняла 4m58s для пошуку трьох слів у стислих файлах журналу вартістю 74 Мб (загалом 937 Мб нестиснуто). Ця поточна версія займає 1м13. Можливо, є додаткові оптимізації, які можна зробити.
Однією з очевидних оптимізацій є використання цього в поєднанні з xargs
‘s -P
aka --max-procs
для паралельного запуску декількох пошукових запитів у підмножинах файлів. Для цього вам потрібно порахувати кількість файлів і розділити на кількість ядер / cpus / потоків у вашій системі (і закріпити, додавши 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
s з find -exec
або xargs
можуть зробити це значно швидше, але цей сценарій має перевагу в підтримці будь-якої кількості шаблонів для пошуку і здатний вирішувати кілька різних типів стиснення.
Якщо сценарій обмежується вивченням лише перших 100 рядків кожного файлу, він проходить через усі вони (у моїй вибірці з 749 МБ з 269 файлів) за 0,6 секунди. Якщо це корисно в деяких випадках, його можна перетворити на параметр командного рядка (наприклад -l 100
), але це ризик знайти не всі файли, що відповідають.
BTW, відповідно до сторінки man для IO::Uncompress::AnyUncompress
, підтримуються формати стиснення:
Остання остання (сподіваюся) оптимізація. Використовуючи PerlIO::gzip
модуль (упакований в debian як libperlio-gzip-perl
), замість цього IO::Uncompress::AnyUncompress
я отримав час приблизно до 3,1 секунди для обробки моїх файлів журналу 74 Мб. Були також деякі невеликі вдосконалення, використовуючи простий хеш, а не Set::Scalar
(що також економило кілька секунд з IO::Uncompress::AnyUncompress
версією).
PerlIO::gzip
рекомендовано як найшвидший perzi gunzip в /programming//a/1539271/137158 (знайдено за допомогою пошуку Google perl fast gzip decompress
)
Використання xargs -P
з цим зовсім не покращило його. Насправді навіть здавалося, що він уповільнить її на будь-якому рівні від 0,1 до 0,7 секунди. (Я спробував чотири запуски, і моя система робить інші речі у фоновому режимі, що змінить час)
Ціна полягає в тому, що ця версія сценарію може обробляти лише gzipped та нестиснені файли. Швидкість проти гнучкості: 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 (<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
файли спочатку.