awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Se si desidera gestire automaticamente i file compressi con gzip, eseguirlo in un ciclo con zcat
(lento e inefficiente perché si biforcerà awk
più volte in un ciclo, una volta per ogni nome file) o riscrivere lo stesso algoritmo perl
e utilizzare il IO::Uncompress::AnyUncompress
modulo libreria che può decomprimere diversi tipi di file compressi (gzip, zip, bzip2, lzop). o in python, che ha anche moduli per la gestione di file compressi.
Ecco una perl
versione che utilizza IO::Uncompress::AnyUncompress
per consentire un numero qualsiasi di motivi e qualsiasi numero di nomi di file (contenenti sia testo normale che testo compresso).
Tutti gli argomenti precedenti --
sono trattati come schemi di ricerca. Tutti gli argomenti successivi --
vengono trattati come nomi di file. Gestione delle opzioni primitiva ma efficace per questo lavoro. Una migliore gestione delle opzioni (ad esempio per supportare -i
un'opzione per ricerche senza distinzione tra maiuscole e minuscole) potrebbe essere ottenuta con i moduli Getopt::Std
o Getopt::Long
.
Eseguilo così:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(Non elencherò i file {1..6}.txt.gz
e {1..6}.txt
qui ... contengono solo alcune o tutte le parole "uno" "due" "tre" "quattro" "cinque" e "sei" per il test. I file elencati nell'output sopra Contiene tutti e tre i modelli di ricerca. Provalo tu stesso con i tuoi dati)
#! /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;
}
}
}
Un hash %patterns
contiene il set completo di pattern che i file devono contenere almeno uno di ogni membro
$_pstring
è una stringa contenente le chiavi ordinate di tale hash. La stringa $pattern
contiene un'espressione regolare precompilata creata anche %patterns
dall'hash.
$pattern
viene confrontato con ciascuna riga di ciascun file di input (utilizzando il /o
modificatore per compilare $pattern
una sola volta poiché sappiamo che non cambierà mai durante l'esecuzione) e map()
viene utilizzato per creare un hash (% s) contenente le corrispondenze per ciascun file.
Ogni volta che tutti i motivi sono stati visti nel file corrente (confrontando se $m_string
(le chiavi ordinate %s
) è uguale a $p_string
), stampa il nome del file e salta al file successivo.
Questa non è una soluzione particolarmente veloce, ma non è irragionevolmente lenta. La prima versione impiegava 4 m 58 per cercare tre parole in 74 MB di file di registro compressi (per un totale di 937 MB non compressi). Questa versione corrente richiede 1m13s. Probabilmente ci sono ulteriori ottimizzazioni che potrebbero essere fatte.
Un'ottimizzazione ovvia è quella di utilizzare questo in combinazione con xargs
'il -P
aka --max-procs
per eseguire ricerche multiple su sottoinsiemi dei file in parallelo. Per fare ciò, devi contare il numero di file e dividerlo per il numero di core / cpus / thread che il tuo sistema ha (e arrotondare aggiungendo 1). ad esempio, nel mio set di campioni sono stati cercati 269 file e il mio sistema ha 6 core (un AMD 1090T), quindi:
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[@]}" --
Con tale ottimizzazione, ci sono voluti solo 23 secondi per trovare tutti i 18 file corrispondenti. Naturalmente, lo stesso potrebbe essere fatto con una qualsiasi delle altre soluzioni. NOTA: l'ordine dei nomi dei file elencati nell'output sarà diverso, quindi potrebbe essere necessario ordinarli in seguito, se ciò è importante.
Come notato da @arekolek, più zgrep
s con find -exec
o xargs
possono farlo in modo significativamente più veloce, ma questo script ha il vantaggio di supportare qualsiasi numero di pattern da cercare ed è in grado di gestire diversi tipi di compressione.
Se lo script si limita a esaminare solo le prime 100 righe di ciascun file, le esegue tutte (nel mio esempio di 74 MB di 269 file) in 0,6 secondi. Se questo è utile in alcuni casi, potrebbe essere trasformato in un'opzione della riga di comando (ad es. -l 100
) Ma ha il rischio di non trovare tutti i file corrispondenti.
A proposito, secondo la pagina man di IO::Uncompress::AnyUncompress
, i formati di compressione supportati sono:
Un'ultima (spero) ottimizzazione. Usando il PerlIO::gzip
modulo (impacchettato in debian as libperlio-gzip-perl
) invece di IO::Uncompress::AnyUncompress
ho ottenuto il tempo a circa 3,1 secondi per l'elaborazione dei miei 74 MB di file di registro. Ci sono stati anche alcuni piccoli miglioramenti usando un semplice hash anziché Set::Scalar
(che ha anche salvato alcuni secondi con la IO::Uncompress::AnyUncompress
versione).
PerlIO::gzip
è stato raccomandato come il più veloce gunzip perl in /programming//a/1539271/137158 (trovato con una ricerca su Google per perl fast gzip decompress
)
L'uso xargs -P
con questo non lo ha migliorato affatto. In effetti, sembrava addirittura rallentarlo da 0,1 a 0,7 secondi. (Ho provato quattro esecuzioni e il mio sistema fa altre cose in background che alterano i tempi)
Il prezzo è che questa versione dello script può gestire solo file compressi con gzip e non compressi. Velocità vs flessibilità: 3,1 secondi per questa versione vs 23 secondi per la IO::Uncompress::AnyUncompress
versione con un xargs -P
wrapper (o 1m13s senza 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
amichevoli, solozcat
i file per primi.