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 awkplusieurs fois dans une boucle, une fois pour chaque nom de fichier) ou réécrivez le même algorithme perlet utilisez le IO::Uncompress::AnyUncompressmodule 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 perlversion qui utilise IO::Uncompress::AnyUncompresspour 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 -ioption pour les recherches non sensibles à la casse) pourrait être obtenue avec les modules Getopt::Stdou 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.gzet{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 %patternscontient l'ensemble complet de modèles que les fichiers doivent contenir au moins un de chaque membre
$_pstringest une chaîne contenant les clés triées de ce hachage. La chaîne $patterncontient une expression régulière précompilée également construite à partir du %patternshachage.
$patternest comparé à chaque ligne de chaque fichier d'entrée (en utilisant le /omodificateur pour compiler $patternune 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 -Paka --max-procspour 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 zgreps avec find -execou xargspeuvent 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::gzipmodule (empaqueté dans debian as libperlio-gzip-perl) au lieu de IO::Uncompress::AnyUncompressj'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::gzipa é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 -Pavec 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::AnyUncompressversion avec xargs -Pwrapper (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;
}
}
}
gzipconviviaux, justezcatles fichiers en premier.