awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Si vous souhaitez gérer automatiquement les fichiers gzippés, exécutez-le en boucle avec zcat
(lent et inefficace car vous bifurquerez awk
plusieurs fois dans une boucle, une fois pour chaque nom de fichier) ou réécrivez le même algorithme perl
et utilisez le IO::Uncompress::AnyUncompress
module de bibliothèque qui peut décompressez plusieurs types de fichiers compressés (gzip, zip, bzip2, lzop). ou en python, qui a également des modules pour gérer les fichiers compressés.
Voici une perl
version qui utilise IO::Uncompress::AnyUncompress
pour autoriser un nombre illimité de modèles et un nombre illimité de noms de fichiers (contenant du texte brut ou du texte compressé).
Tous les arguments avant --
sont traités comme des modèles de recherche. Tous les arguments après--
sont traités comme des noms de fichiers. Gestion des options primitive mais efficace pour ce travail. Une meilleure gestion des options (par exemple pour prendre en charge une -i
option pour les recherches non sensibles à la casse) pourrait être obtenue avec les modules Getopt::Std
ou Getopt::Long
.
Exécutez-le comme ceci:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(Je ne répertorierai pas les fichiers {1..6}.txt.gz
et{1..6}.txt
ici ... ils contiennent juste une partie ou la totalité des mots "un" "deux" "trois" "quatre" "cinq" et "six" pour les tests. Les fichiers répertoriés dans la sortie ci-dessus Contenez les trois modèles de recherche. Testez-le vous-même avec vos propres données)
#! /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 hachage %patterns
contient l'ensemble complet de modèles que les fichiers doivent contenir au moins un de chaque membre
$_pstring
est une chaîne contenant les clés triées de ce hachage. La chaîne $pattern
contient une expression régulière précompilée également construite à partir du %patterns
hachage.
$pattern
est comparé à chaque ligne de chaque fichier d'entrée (en utilisant le /o
modificateur pour compiler $pattern
une seule fois car nous savons qu'il ne changera jamais pendant l'exécution), et map()
est utilisé pour créer un hachage (% s) contenant les correspondances pour chaque fichier.
Chaque fois que tous les modèles ont été vus dans le fichier actuel (en comparant si $m_string
(les clés triées dans %s
) sont égales à $p_string
), imprimez le nom de fichier et passez au fichier suivant.
Ce n'est pas une solution particulièrement rapide, mais ce n'est pas excessivement lent. La première version a pris 4 min 58 s pour rechercher trois mots dans 74 Mo de fichiers journaux compressés (totalisant 937 Mo non compressés). Cette version actuelle prend 1m13s. Il y a probablement d'autres optimisations qui pourraient être faites.
Une optimisation évidente consiste à l'utiliser en conjonction avec xargs
's -P
aka --max-procs
pour exécuter plusieurs recherches sur des sous-ensembles de fichiers en parallèle. Pour ce faire, vous devez compter le nombre de fichiers et diviser par le nombre de cœurs / cpus / threads de votre système (et arrondir en ajoutant 1). Par exemple, 269 fichiers ont été recherchés dans mon jeu d'échantillons et mon système a 6 cœurs (un AMD 1090T), donc:
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[@]}" --
Avec cette optimisation, il n'a fallu que 23 secondes pour trouver les 18 fichiers correspondants. Bien sûr, la même chose pourrait être faite avec n'importe quelle autre solution. REMARQUE: l'ordre des noms de fichiers répertoriés dans la sortie sera différent, il peut donc être nécessaire de les trier ultérieurement si cela est important.
Comme indiqué par @arekolek, plusieurs zgrep
s avec find -exec
ou xargs
peuvent le faire beaucoup plus rapidement, mais ce script a l'avantage de prendre en charge un certain nombre de modèles à rechercher et est capable de gérer plusieurs types de compression différents.
Si le script se limite à examiner uniquement les 100 premières lignes de chaque fichier, il les traverse toutes (dans mon échantillon de 74 Mo de 269 fichiers) en 0,6 seconde. Si cela est utile dans certains cas, il pourrait être transformé en une option de ligne de commande (par exemple -l 100
) mais il a le risque de ne pas trouver tous les fichiers correspondants.
BTW, selon la page de manuel de IO::Uncompress::AnyUncompress
, les formats de compression pris en charge sont:
Une dernière (j'espère) optimisation. En utilisant le PerlIO::gzip
module (empaqueté dans debian as libperlio-gzip-perl
) au lieu de IO::Uncompress::AnyUncompress
j'ai réduit le temps à environ 3,1 secondes pour traiter mes 74 Mo de fichiers journaux. Il y a également eu quelques petites améliorations en utilisant un hachage simple plutôt que Set::Scalar
(ce qui a également sauvé quelques secondes avec leIO::Uncompress::AnyUncompress
version).
PerlIO::gzip
a été recommandé comme le gunzip perl le plus rapide dans /programming//a/1539271/137158 (trouvé avec une recherche google pour perl fast gzip decompress
)
Utiliser xargs -P
avec cela ne l'a pas amélioré du tout. En fait, il semblait même le ralentir de 0,1 à 0,7 seconde. (J'ai essayé quatre runs et mon système fait d'autres choses en arrière-plan qui modifieront le timing)
Le prix est que cette version du script ne peut gérer que les fichiers compressés et compressés. Vitesse vs flexibilité: 3,1 secondes pour cette version vs 23 secondes pour la IO::Uncompress::AnyUncompress
version avec xargs -P
wrapper (ou 1m13 sans 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
conviviaux, justezcat
les fichiers en premier.