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 awk
in einer Schleife mehrmals nacheinander suchen, einmal für jeden Dateinamen) aus oder schreiben Sie denselben Algorithmus neu perl
und verwenden Sie das IO::Uncompress::AnyUncompress
Bibliotheksmodul, 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 perl
Version können IO::Uncompress::AnyUncompress
beliebig 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 -i
Option für Suchen ohne Berücksichtigung der Groß- / Kleinschreibung zu unterstützen) könnte mit den Modulen Getopt::Std
oder 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.gz
und {1..6}.txt
hier ... 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 %patterns
enthält den vollständigen Satz von Mustern, die Dateien enthalten müssen. Mindestens eines von jedem Mitglied
$_pstring
ist eine Zeichenfolge, die die sortierten Schlüssel dieses Hash enthält. Die Zeichenfolge $pattern
enthält einen vorkompilierten regulären Ausdruck, der ebenfalls aus dem %patterns
Hash erstellt wurde.
$pattern
wird mit jeder Zeile jeder Eingabedatei verglichen (wobei der /o
Modifikator nur zum Kompilieren verwendet wird, $pattern
da 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 -P
aka --max-procs
mehr 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 zgrep
s mit find -exec
oder xargs
wesentlich 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::AnyUncompress
Komprimierungsformate unterstützt:
Eine letzte (ich hoffe) Optimierung. Durch die Verwendung des PerlIO::gzip
Moduls (in debian as gepackt libperlio-gzip-perl
) anstelle von konnte IO::Uncompress::AnyUncompress
ich 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::AnyUncompress
Version auch ein paar Sekunden sparte ).
PerlIO::gzip
wurde als schnellster Perl-Gunzip in /programming//a/1539271/137158 empfohlen (gefunden mit einer Google-Suche nach perl fast gzip decompress
)
Das Verwenden xargs -P
mit 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::AnyUncompress
Version mit einem xargs -P
Wrapper (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;
}
}
}
gzip
freundlich sein, nurzcat
die Dateien zuerst.