awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
gzip圧縮されたファイルを自動的に処理する場合は、これをループで実行しますzcat
(awk
ループ内でファイル名ごとに何度も分岐するため、低速で非効率的です)か、同じアルゴリズムを書き換えperl
てIO::Uncompress::AnyUncompress
ライブラリモジュールを使用しますいくつかの異なる種類の圧縮ファイル(gzip、zip、bzip2、lzop)を解凍します。または、圧縮ファイルを処理するためのモジュールもあるpythonで。
以下perl
が使用するバージョンですIO::Uncompress::AnyUncompress
、任意の数のパターンと任意の数のファイル名(プレーンテキストまたは圧縮テキストを含む)を許可するために。
以前のすべての引数--
は、検索パターンとして扱われます。以降のすべての引数--
はファイル名として扱われます。このジョブの基本的だが効果的なオプション処理。より良いオプション処理(-i
大文字と小文字を区別しない検索のオプションをサポートするなど)は、Getopt::Std
またはGetopt::Long
モジュールでます。
次のように実行します。
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(ここではファイル{1..6}.txt.gz
をリストしません{1..6}.txt
。テストのために、「1」、「2」、「3」、「4」、「5」、「6」という単語の一部またはすべてが含まれています。上記の出力にリストされているファイル3つの検索パターンをすべて含めてください。独自のデータを使用して自分でテストしてください)
#! /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;
}
}
}
ハッシュに%patterns
は、ファイルに含まれるパターンの完全なセットが含まれます。各メンバーの少なくとも1つは
$_pstring
、そのハッシュのソートされたキーを含む文字列です。文字列に$pattern
は、以下から構築されたプリコンパイル済みの正規表現が含まれています%patterns
ハッシュます。
$pattern
は、各入力ファイルの各行と比較され(実行中に変更されないことがわかっている/o
ため、修飾子を使用して$pattern
一度だけコンパイルします)、map()
各ファイルの一致を含むハッシュ(%s)を構築するために使用されます。
現在のファイルにすべてのパターンが表示されたときはいつでも$m_string
((のソートされたキー%s
)が次と等しいかどうかを比較することにより)$p_string
)、ファイル名を出力し、次のファイルにスキップします。
これは特に高速なソリューションではありませんが、不当に遅いというわけではありません。最初のバージョンでは、74MBの圧縮ログファイル(合計937MBの非圧縮)で3つの単語を検索するのに4m58秒かかりました。この現行バージョンには1分13秒かかります。おそらくさらなる最適化が行われる可能性があります。
1つの明らかな最適化は、これをxargs
の-P
別名と組み合わせて使用して--max-procs
、ファイルのサブセットに対して複数の検索を並行して実行することです。これを行うには、ファイルの数をカウントし、システムにあるコア/ CPU /スレッドの数で割る必要があります(1を追加して切り上げます)。たとえば、私のサンプルセットでは269個のファイルが検索され、システムには6つのコア(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[@]}" --
この最適化により、一致する18個のファイルすべてを見つけるのに23秒しかかかりませんでした。もちろん、他のソリューションでも同じことができます。注:出力にリストされるファイル名の順序は異なるため、重要な場合は後でソートする必要があります。
@arekolekで述べたように、複数zgrep
持つのfind -exec
かはxargs
かなり速くそれを行うことができますが、このスクリプトはを検索するためのパターンの任意の数をサポートするという利点があり、圧縮、いくつかの異なるタイプに対処することができます。
スクリプトが各ファイルの最初の100行のみの検査に制限されている場合、0.6秒ですべてのファイル(269ファイルの74MBサンプル)を実行します。これがいくつかの場合に役立つ場合、コマンドラインオプション(例:)にすることができます-l 100
が、一致するすべてのファイルが見つからないというリスクがあります。
ところで、のmanページによると、IO::Uncompress::AnyUncompress
サポートされている圧縮形式は次のとおりです。
最後に(私は)最適化を行います。代わりにPerlIO::gzip
(debianにパッケージ化されたlibperlio-gzip-perl
)モジュールを使用することで、74MBのログファイルを処理IO::Uncompress::AnyUncompress
する時間を約3.1秒に短縮しました。Set::Scalar
(IO::Uncompress::AnyUncompress
バージョンで数秒も節約された)ではなく、単純なハッシュを使用することによって、いくつかの小さな改善もありました。
PerlIO::gzip
/programming//a/1539271/137158で最速のperl gunzipとして推奨されました(Google検索で発見perl fast gzip decompress
)
これを使用xargs -P
してもまったく改善されませんでした。実際、0.1秒から0.7秒の範囲で速度を落とすようにも見えました。(私は4回実行しましたが、システムはバックグラウンドで他の処理を行い、タイミングを変更します)
ただし、このバージョンのスクリプトはgzip圧縮されたファイルと圧縮されていないファイルのみを処理できます。速度と柔軟性:このバージョンでは3.1秒IO::Uncompress::AnyUncompress
、xargs -P
ラッパーあり(または1m13sなしxargs -P
)では23秒です。
#! /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
友好的である必要はなくzcat
、ファイルだけが最初です。