awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Si desea manejar automáticamente los archivos comprimidos, ejecute esto en un bucle con zcat
(lento e ineficiente porque se bifurcará awk
muchas veces en un bucle, una vez para cada nombre de archivo) o vuelva a escribir el mismo algoritmo perl
y use el IO::Uncompress::AnyUncompress
módulo de biblioteca que puede descomprima varios tipos diferentes de archivos comprimidos (gzip, zip, bzip2, lzop). o en python, que también tiene módulos para manejar archivos comprimidos.
Aquí hay una perl
versión que utiliza IO::Uncompress::AnyUncompress
para permitir cualquier número de patrones y cualquier número de nombres de archivos (que contengan texto sin formato o texto comprimido).
Todos los argumentos anteriores --
se tratan como patrones de búsqueda. Todos los argumentos posteriores --
se tratan como nombres de archivo. Manejo de opciones primitivo pero efectivo para este trabajo. Mejor manejo opción (por ejemplo, para soportar una -i
opción para las búsquedas de mayúsculas y minúsculas) podría lograrse con el Getopt::Std
o Getopt::Long
los módulos.
Ejecútelo así:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(No enumeraré archivos {1..6}.txt.gz
y {1..6}.txt
aquí ... solo contienen algunas o todas las palabras "uno" "dos" "tres" "cuatro" "cinco" y "seis" para probar. Los archivos enumerados en el resultado anterior SÍ contiene los tres patrones de búsqueda. Pruébelo usted mismo con sus propios datos)
#! /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 el conjunto completo de patrones que los archivos deben contener, al menos uno de cada miembro
$_pstring
es una cadena que contiene las claves ordenadas de ese hash. La cadena $pattern
contiene una expresión regular precompilada también construida a partir del %patterns
hash.
$pattern
se compara con cada línea de cada archivo de entrada (usando el /o
modificador para compilar $pattern
solo una vez, ya que sabemos que nunca cambiará durante la ejecución), y map()
se usa para construir un hash (% s) que contiene las coincidencias para cada archivo.
Siempre que se hayan visto todos los patrones en el archivo actual (al comparar si $m_string
(las claves ordenadas en %s
) son iguales a $p_string
), imprima el nombre del archivo y pase al siguiente archivo.
Esta no es una solución particularmente rápida, pero no es irrazonablemente lenta. La primera versión tardó 4m58s en buscar tres palabras en 74MB de archivos de registro comprimidos (un total de 937MB sin comprimir). Esta versión actual dura 1m13s. Probablemente hay más optimizaciones que podrían hacerse.
Una optimización obvia es usar esto junto con xargs
' -P
aka' --max-procs
para ejecutar múltiples búsquedas en subconjuntos de archivos en paralelo. Para hacer eso, debe contar la cantidad de archivos y dividir por la cantidad de núcleos / cpus / hilos que tiene su sistema (y redondear agregando 1). por ejemplo, se buscaron 269 archivos en mi conjunto de muestras, y mi sistema tiene 6 núcleos (un AMD 1090T), por lo que:
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 esa optimización, tomó solo 23 segundos encontrar los 18 archivos coincidentes. Por supuesto, lo mismo podría hacerse con cualquiera de las otras soluciones. NOTA: El orden de los nombres de archivo enumerados en la salida será diferente, por lo que puede ser necesario ordenarlos después si eso es importante.
Como señaló @arekolek, múltiples zgrep
s con find -exec
o xargs
pueden hacerlo significativamente más rápido, pero este script tiene la ventaja de admitir cualquier número de patrones para buscar, y es capaz de manejar varios tipos diferentes de compresión.
Si el script se limita a examinar solo las primeras 100 líneas de cada archivo, se ejecuta a través de todas ellas (en mi muestra de 74MB de 269 archivos) en 0.6 segundos. Si esto es útil en algunos casos, podría convertirse en una opción de línea de comando (por ejemplo -l 100
), pero tiene el riesgo de no encontrar todos los archivos coincidentes.
Por cierto, de acuerdo con la página del manual para IO::Uncompress::AnyUncompress
, los formatos de compresión admitidos son:
Una última (espero) optimización. Al usar el PerlIO::gzip
módulo (empaquetado en Debian como libperlio-gzip-perl
) en lugar de hacerlo IO::Uncompress::AnyUncompress
, obtuve el tiempo de espera de aproximadamente 3,1 segundos para procesar mis 74 MB de archivos de registro. También hubo algunas pequeñas mejoras al usar un hash simple en lugar de Set::Scalar
(que también ahorró unos segundos con la IO::Uncompress::AnyUncompress
versión).
PerlIO::gzip
fue recomendado como el gunzip perl más rápido en /programming//a/1539271/137158 (encontrado con una búsqueda en google para perl fast gzip decompress
)
Usar xargs -P
con esto no lo mejoró en absoluto. De hecho, incluso pareció ralentizarlo entre 0.1 y 0.7 segundos. (Intenté cuatro ejecuciones y mi sistema hace otras cosas en segundo plano que alterarán el tiempo)
El precio es que esta versión del script solo puede manejar archivos comprimidos y descomprimidos. Velocidad frente a flexibilidad: 3.1 segundos para esta versión frente a 23 segundos para la IO::Uncompress::AnyUncompress
versión con xargs -P
envoltura (o 1m13s sin 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
amigables, solozcat
los archivos primero.