awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
如果您想自动处理压缩文件,请使用以下命令循环运行zcat
(速度慢且效率低,因为您将awk
在循环中分叉多次,每个文件名一次),或者重写相同的算法perl
并使用IO::Uncompress::AnyUncompress
库模块解压缩几种不同类型的压缩文件(gzip,zip,bzip2,lzop)。或在python中,它也具有用于处理压缩文件的模块。
这里的一个perl
版本的用途IO::Uncompress::AnyUncompress
,以允许任何数量的图案和任意数量的文件名(包含纯文本或压缩的文本)。
之前的所有参数--
均视为搜索模式。之后的所有args --
均视为文件名。此工作的原始但有效的选项处理。-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
在这里...它们仅包含部分或全部单词“一个”,“两个”,“三个”,“四个”,“五个”和“六个”进行测试。上面输出中列出的文件必须包含所有三种搜索模式。使用自己的数据进行测试)
#! /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
包含一组完整的模式,文件必须包含至少一组每个成员,这
$_pstring
是一个包含该散列的排序键的字符串。该字符串$pattern
包含一个预编译的正则表达式,该正则表达式也是从%patterns
哈希中构建的。
$pattern
与每个输入文件的每一行进行比较(使用/o
修饰符$pattern
仅编译一次,因为我们知道它在运行期间不会更改),并map()
用于构建包含每个文件匹配项的哈希(%s)。
只要在当前文件中看到了所有模式(通过比较$m_string
()中的排序键%s
是否等于$p_string
),就打印文件名并跳至下一个文件。
这不是一个特别快的解决方案,但也不是不合理地缓慢。第一个版本耗时4分58秒,在价值74MB的压缩日志文件(总共937MB未压缩)中搜索三个单词。当前版本需要1m13s。可能还有进一步的优化方法。
一个明显的优化是在同时使用这项功能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[@]}" --
通过这种优化,只花了23秒即可找到所有18个匹配的文件。当然,其他解决方案也可以做到这一点。注意:输出中列出的文件名顺序将有所不同,因此,如果需要,以后可能需要对其进行排序。
正如@arekolek指出的那样,zgrep
使用find -exec
或xargs
可以很快速地实现多个,但是此脚本的优点是支持搜索任意数量的模式,并且能够处理几种不同类型的压缩。
如果该脚本仅限于检查每个文件的前100行,则它将在0.6秒内遍历所有文件(在我的269个文件的74MB样本中)。如果在某些情况下有用,则可以将其设置为命令行选项(例如-l 100
),但是存在无法找到所有匹配文件的风险。
顺便说一句,根据的手册页IO::Uncompress::AnyUncompress
,支持的压缩格式为:
最后(我希望)优化。通过使用PerlIO::gzip
模块(打包为debian中的libperlio-gzip-perl
),IO::Uncompress::AnyUncompress
我将处理74MB日志文件的时间缩短到了约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秒。(我尝试了四次运行,并且系统在后台执行了其他操作,这会更改时间)
代价是此脚本版本只能处理压缩和未压缩的文件。速度与灵活性:此版本为3.1秒,而IO::Uncompress::AnyUncompress
带xargs -P
包装的版本为23秒(或不带的1m13s 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
友好,只zcat
需要文件即可。