awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Jeśli chcesz automatycznie obsługiwać pliki spakowane gzip, uruchom to w pętli za pomocą zcat
(powolne i nieefektywne, ponieważ będziesz rozwidlać awk
wiele razy w pętli, raz dla każdej nazwy pliku) lub przepisz ten sam algorytm perl
i użyj IO::Uncompress::AnyUncompress
modułu biblioteki, który może rozpakuj kilka różnych rodzajów skompresowanych plików (gzip, zip, bzip2, lzop). lub w pythonie, który ma również moduły do obsługi skompresowanych plików.
Oto perl
wersja, która używaIO::Uncompress::AnyUncompress
pozwala na dowolną liczbę wzorców i dowolną liczbę nazw plików (zawierających zwykły tekst lub skompresowany tekst).
Wszystkie argumenty wcześniej --
są traktowane jako wzorce wyszukiwania. Wszystkie argumenty później --
są traktowane jak nazwy plików. Prymitywna, ale skuteczna obsługa opcji dla tego zadania. Lepszą obsługę opcji (np. W celu obsługi -i
opcji wyszukiwania bez rozróżniania wielkości liter) można uzyskać za pomocą Getopt::Std
lubGetopt::Long
modułów .
Uruchom tak:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(Nie wymienię plików, {1..6}.txt.gz
a {1..6}.txt
tutaj ... zawierają one tylko niektóre lub wszystkie słowa „jeden” „dwa” „trzy” „cztery” „pięć” i „sześć” do testowania. Pliki wymienione w wynikach powyżej Zawierają wszystkie trzy wzorce wyszukiwania. Sprawdź to sam na podstawie własnych danych)
#! /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;
}
}
}
Hash %patterns
zawiera pełny zestaw wzorców, które pliki muszą zawierać co najmniej jeden z każdego elementu,
$_pstring
to ciąg zawierający posortowane klucze tego hasha. Ciąg $pattern
zawiera wstępnie skompilowane wyrażenie regularne również zbudowane z %patterns
skrótu.
$pattern
jest porównywany z każdą linią każdego pliku wejściowego (przy użyciu /o
modyfikatora do kompilacji $pattern
tylko raz, ponieważ wiemy, że nigdy się nie zmieni podczas uruchamiania), i map()
jest używany do budowania skrótu (% s) zawierającego dopasowania dla każdego pliku.
Ilekroć wszystkie wzory są widoczne w bieżącym pliku (przez porównanie, czy $m_string
(posortowane klucze %s
są równe $p_string
), wydrukuj nazwę pliku i przejdź do następnego pliku.
Nie jest to szczególnie szybkie rozwiązanie, ale nie jest nieuzasadnione powolne. Pierwsza wersja zajęła 4m58 sekund, aby wyszukać trzy słowa w skompresowanych plikach dziennika o wartości 74 MB (łącznie 937 MB bez kompresji). Ta aktualna wersja zajmuje 1m13s. Prawdopodobnie można dokonać dalszych optymalizacji.
Jednym z oczywistych optymalizacji jest do tego użyć w połączeniu z xargs
„s -P
aka --max-procs
uruchamianie wielu wyszukiwań w podgrupach plików równolegle. Aby to zrobić, musisz policzyć liczbę plików i podzielić przez liczbę rdzeni / cpus / wątków, które ma Twój system (i zaokrąglić w górę, dodając 1). np. w moim zestawie próbek przeszukano 269 plików, a mój system ma 6 rdzeni (AMD 1090T), więc:
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[@]}" --
Dzięki tej optymalizacji znalezienie wszystkich 18 pasujących plików zajęło tylko 23 sekundy. Oczywiście to samo można zrobić z dowolnym innym rozwiązaniem. UWAGA: Kolejność nazw plików wymienionych w danych wyjściowych będzie inna, więc może to wymagać późniejszego posortowania, jeśli to ma znaczenie.
Jak zauważył @arekolek, wiele zgrep
s z find -exec
lub xargs
może to zrobić znacznie szybciej, ale ten skrypt ma tę zaletę, że obsługuje dowolną liczbę wzorców do wyszukiwania i jest w stanie poradzić sobie z kilkoma różnymi typami kompresji.
Jeśli skrypt ogranicza się do zbadania tylko pierwszych 100 wierszy każdego pliku, przechodzi przez wszystkie (w mojej 74 MB próbce 269 plików) w 0,6 sekundy. Jeśli jest to przydatne w niektórych przypadkach, można je przekształcić w opcję wiersza poleceń (np. -l 100
), Ale istnieje ryzyko , że nie uda się znaleźć wszystkich pasujących plików.
BTW, według strony IO::Uncompress::AnyUncompress
podręcznika, obsługiwane formaty kompresji to:
- zlib RFC 1950 ,
- spuścić powietrze z RFC 1951 (opcjonalnie),
- gzip RFC 1952 ,
- zamek błyskawiczny,
- bzip2,
- lzop,
- lzf,
- lzma,
- xz
Ostatnia (mam nadzieję) optymalizacja. Korzystając z PerlIO::gzip
modułu (spakowanego w debian as libperlio-gzip-perl
) zamiast zużywać IO::Uncompress::AnyUncompress
czas do około 3,1 sekundy na przetworzenie moich 74 MB plików dziennika. Wprowadzono również niewielkie ulepszenia, używając prostego skrótu zamiast Set::Scalar
(co również pozwoliło zaoszczędzić kilka sekund w przypadku IO::Uncompress::AnyUncompress
wersji).
PerlIO::gzip
był zalecany jako najszybszy perl gunzip w /programming//a/1539271/137158 (znaleziony przy wyszukiwaniu w Google perl fast gzip decompress
)
Używanie xargs -P
z tym wcale go nie poprawiło. W rzeczywistości wydawało się, że nawet spowalnia to od 0,1 do 0,7 sekundy. (Próbowałem czterech uruchomień, a mój system wykonuje inne czynności w tle, które zmienią czas)
Cena jest taka, że ta wersja skryptu obsługuje tylko pliki spakowane i nieskompresowane. Szybkość vs elastyczność: 3,1 sekundy dla tej wersji vs 23 sekund dla IO::Uncompress::AnyUncompress
wersji z xargs -P
otoką (lub 1m13s bez 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
przyjaźni, tylkozcat
pliki.