awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Wenn Sie gzippte Dateien automatisch verarbeiten möchten, führen Sie dies entweder in einer Schleife mit zcat(langsam und ineffizient, da Sie awkin einer Schleife mehrmals nacheinander suchen, einmal für jeden Dateinamen) aus oder schreiben Sie denselben Algorithmus neu perlund verwenden Sie das IO::Uncompress::AnyUncompressBibliotheksmodul, das dies kann Dekomprimieren Sie verschiedene Arten von komprimierten Dateien (gzip, zip, bzip2, lzop). oder in Python, das auch Module für den Umgang mit komprimierten Dateien enthält.
In dieser perlVersion können IO::Uncompress::AnyUncompressbeliebig viele Muster und Dateinamen (entweder mit Klartext oder mit komprimiertem Text) eingegeben werden.
Alle vorherigen Argumente --werden als Suchmuster behandelt. Alle nachfolgenden Argumente --werden als Dateinamen behandelt. Primitive, aber effektive Optionsverwaltung für diesen Job. Ein besseres Optionshandling (z. B. um eine -iOption für Suchen ohne Berücksichtigung der Groß- / Kleinschreibung zu unterstützen) könnte mit den Modulen Getopt::Stdoder erreicht werden Getopt::Long.
Führen Sie es so aus:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(Ich werde keine Dateien auflisten {1..6}.txt.gzund {1..6}.txthier ... sie enthalten nur einige oder alle Wörter "eins" "zwei" "drei" "vier" "fünf" und "sechs" zum Testen. Die in der Ausgabe oben aufgelisteten Dateien Enthalten Sie alle drei Suchmuster. Testen Sie es selbst mit Ihren eigenen Daten.)
#! /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;
}
}
}
Ein Hash %patternsenthält den vollständigen Satz von Mustern, die Dateien enthalten müssen. Mindestens eines von jedem Mitglied
$_pstringist eine Zeichenfolge, die die sortierten Schlüssel dieses Hash enthält. Die Zeichenfolge $patternenthält einen vorkompilierten regulären Ausdruck, der ebenfalls aus dem %patternsHash erstellt wurde.
$patternwird mit jeder Zeile jeder Eingabedatei verglichen (wobei der /oModifikator nur zum Kompilieren verwendet wird, $patternda bekannt ist, dass er sich während des Laufs niemals ändert) und map()wird verwendet, um einen Hash (% s) zu erstellen, der die Übereinstimmungen für jede Datei enthält.
Wenn alle Muster in der aktuellen Datei angezeigt wurden (indem Sie vergleichen, ob $m_string(die sortierten Eingaben in %s) gleich sind $p_string), drucken Sie den Dateinamen und springen Sie zur nächsten Datei.
Dies ist keine besonders schnelle Lösung, aber nicht unangemessen langsam. Die erste Version benötigte 4 Minuten, um nach drei Wörtern in komprimierten Protokolldateien im Wert von 74 MB zu suchen (insgesamt 937 MB unkomprimiert). Diese aktuelle Version dauert 1m13s. Es könnten wahrscheinlich weitere Optimierungen vorgenommen werden.
Eine offensichtliche Optimierung ist dies in Verbindung mit dieser verwenden xargs‚s -Paka --max-procsmehr Suchen auf Teilmengen der Dateien parallel laufen zu lassen. Dazu müssen Sie die Anzahl der Dateien zählen und durch die Anzahl der Kerne / CPUs / Threads Ihres Systems dividieren (und durch Addieren von 1 aufrunden). Beispiel: In meinem Beispielsatz wurden 269 Dateien durchsucht, und mein System verfügt über 6 Kerne (ein AMD 1090T).
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[@]}" --
Mit dieser Optimierung wurden in nur 23 Sekunden alle 18 übereinstimmenden Dateien gefunden. Dasselbe könnte natürlich mit jeder anderen Lösung geschehen. HINWEIS: Die Reihenfolge der in der Ausgabe aufgelisteten Dateinamen ist unterschiedlich und muss ggf. nachträglich sortiert werden.
Wie von @arekolek festgestellt, können mehrere zgreps mit find -execoder xargswesentlich schneller vorgehen. Dieses Skript bietet jedoch den Vorteil, dass es eine beliebige Anzahl von zu suchenden Mustern unterstützt und mit verschiedenen Arten der Komprimierung umgehen kann.
Wenn sich das Skript darauf beschränkt, nur die ersten 100 Zeilen jeder Datei zu untersuchen, werden alle Zeilen (in meinem 74-MB-Beispiel mit 269 Dateien) in 0,6 Sekunden durchlaufen. Wenn dies in einigen Fällen nützlich ist, kann es zu einer Befehlszeilenoption gemacht werden (z. B. -l 100), aber es besteht das Risiko, dass nicht alle übereinstimmenden Dateien gefunden werden.
Übrigens werden laut Handbuch folgende IO::Uncompress::AnyUncompressKomprimierungsformate unterstützt:
Eine letzte (ich hoffe) Optimierung. Durch die Verwendung des PerlIO::gzipModuls (in debian as gepackt libperlio-gzip-perl) anstelle von konnte IO::Uncompress::AnyUncompressich die Zeit für die Verarbeitung meiner 74 MB großen Protokolldateien auf ca. 3,1 Sekunden reduzieren. Es gab auch einige kleine Verbesserungen durch die Verwendung eines einfachen Hashes anstatt Set::Scalar(was mit der IO::Uncompress::AnyUncompressVersion auch ein paar Sekunden sparte ).
PerlIO::gzipwurde als schnellster Perl-Gunzip in /programming//a/1539271/137158 empfohlen (gefunden mit einer Google-Suche nach perl fast gzip decompress)
Das Verwenden xargs -Pmit diesem hat es überhaupt nicht verbessert. Tatsächlich schien es ihn sogar um 0,1 bis 0,7 Sekunden zu verlangsamen. (Ich habe vier Läufe ausprobiert und mein System erledigt andere Dinge im Hintergrund, die das Timing verändern.)
Der Preis ist, dass diese Version des Skripts nur komprimierte und nicht komprimierte Dateien verarbeiten kann. Geschwindigkeit vs Flexibilität: 3,1 Sekunden für diese Version vs 23 Sekunden für die IO::Uncompress::AnyUncompressVersion mit einem xargs -PWrapper (oder 1m13s ohne 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;
}
}
}
gzipfreundlich sein, nurzcatdie Dateien zuerst.