awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Nếu bạn muốn tự động xử lý các tệp được nén, hãy chạy nó trong một vòng lặp với zcat
(chậm và không hiệu quả vì bạn sẽ sử dụng awk
nhiều lần trong một vòng lặp, một lần cho mỗi tên tệp) hoặc viết lại cùng một thuật toán perl
và sử dụng IO::Uncompress::AnyUncompress
mô-đun thư viện có thể giải nén một số loại tệp nén khác nhau (gzip, zip, bzip2, lzop). hoặc trong python, cũng có các mô-đun để xử lý các tệp nén.
Đây là perl
phiên bản sử dụng IO::Uncompress::AnyUncompress
để cho phép bất kỳ số lượng mẫu và bất kỳ số tên tệp nào (chứa văn bản thuần túy hoặc văn bản nén).
Tất cả các đối số trước đây --
được coi là mô hình tìm kiếm. Tất cả các args sau --
được coi là tên tập tin. Xử lý tùy chọn nguyên thủy nhưng hiệu quả cho công việc này. Xử lý tùy chọn tốt hơn (ví dụ để hỗ trợ -i
tùy chọn cho các tìm kiếm không phân biệt chữ hoa chữ thường) có thể đạt được bằng Getopt::Std
hoặc Getopt::Long
các mô-đun.
Chạy nó như vậy:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(Tôi sẽ không liệt kê các tệp {1..6}.txt.gz
và {1..6}.txt
ở đây ... chúng chỉ chứa một số hoặc tất cả các từ "một" "hai" "ba" "bốn" "năm" và "sáu" để kiểm tra. Các tệp được liệt kê ở đầu ra ở trên NÊN chứa cả ba mẫu tìm kiếm. Tự kiểm tra nó với dữ liệu của riêng bạn)
#! /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;
}
}
}
Hàm băm %patterns
chứa tập hợp đầy đủ các mẫu mà các tệp phải chứa ít nhất một trong số mỗi thành viên
$_pstring
là một chuỗi chứa các khóa được sắp xếp của hàm băm đó. Chuỗi $pattern
chứa một biểu thức chính quy được biên dịch trước cũng được xây dựng từ %patterns
hàm băm.
$pattern
được so sánh với từng dòng của mỗi tệp đầu vào (sử dụng công cụ /o
sửa đổi để biên dịch $pattern
chỉ một lần vì chúng tôi biết nó sẽ không bao giờ thay đổi trong quá trình chạy) và map()
được sử dụng để tạo hàm băm (% s) có chứa các kết quả khớp cho mỗi tệp.
Bất cứ khi nào tất cả các mẫu đã được nhìn thấy trong tệp hiện tại (bằng cách so sánh nếu $m_string
(các phím được sắp xếp trong %s
) bằng $p_string
), hãy in tên tệp và chuyển sang tệp tiếp theo.
Đây không phải là một giải pháp đặc biệt nhanh, nhưng không chậm một cách vô lý. Phiên bản đầu tiên mất 4m58 để tìm kiếm ba từ trong tệp nhật ký nén trị giá 74 MB (tổng cộng 937 MB không nén). Phiên bản hiện tại này mất 1m13s. Có lẽ có những tối ưu hơn nữa có thể được thực hiện.
Một tối ưu hóa rõ ràng là sử dụng này kết hợp với xargs
's -P
aka --max-procs
để chạy nhiều tìm kiếm trên các tập con của các tập tin trong song song. Để làm điều đó, bạn cần đếm số lượng tệp và chia cho số lõi / cpus / luồng mà hệ thống của bạn có (và làm tròn bằng cách thêm 1). ví dụ: có 269 tệp đang được tìm kiếm trong bộ mẫu của tôi và hệ thống của tôi có 6 lõi (AMD 1090T), vì vậy:
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[@]}" --
Với tối ưu hóa đó, chỉ mất 23 giây để tìm thấy tất cả 18 tệp phù hợp. Tất nhiên, điều tương tự có thể được thực hiện với bất kỳ giải pháp nào khác. LƯU Ý: Thứ tự tên tệp được liệt kê trong đầu ra sẽ khác nhau, do đó có thể cần được sắp xếp sau đó nếu vấn đề đó xảy ra.
Như @arekolek đã lưu ý, nhiều zgrep
s có find -exec
hoặc xargs
có thể thực hiện nhanh hơn đáng kể, nhưng tập lệnh này có lợi thế là hỗ trợ bất kỳ số mẫu nào để tìm kiếm và có khả năng xử lý một số loại nén khác nhau.
Nếu tập lệnh bị giới hạn chỉ kiểm tra 100 dòng đầu tiên của mỗi tệp, thì tập lệnh sẽ chạy qua tất cả chúng (trong mẫu 74 MB của tôi là 269 tệp) trong 0,6 giây. Nếu điều này hữu ích trong một số trường hợp, nó có thể được tạo thành một tùy chọn dòng lệnh (ví dụ -l 100
) nhưng nó có nguy cơ không tìm thấy tất cả các tệp phù hợp.
BTW, theo trang man cho IO::Uncompress::AnyUncompress
, các định dạng nén được hỗ trợ là:
Một tối ưu hóa cuối cùng (tôi hy vọng). Bằng cách sử dụng PerlIO::gzip
mô-đun (được đóng gói trong debian như libperlio-gzip-perl
) thay vì IO::Uncompress::AnyUncompress
tôi giảm thời gian xuống còn khoảng 3,1 giây để xử lý 74 MB tệp nhật ký của mình. Ngoài ra còn có một số cải tiến nhỏ bằng cách sử dụng hàm băm đơn giản thay vì Set::Scalar
(cũng lưu một vài giây với IO::Uncompress::AnyUncompress
phiên bản).
PerlIO::gzip
đã được đề xuất là gunzip perl nhanh nhất trong /programming//a/1539271/137158 (được tìm thấy với một tìm kiếm google cho perl fast gzip decompress
)
Sử dụng xargs -P
với điều này đã không cải thiện nó cả. Trong thực tế, nó thậm chí dường như làm chậm nó xuống bất cứ nơi nào từ 0,1 đến 0,7 giây. (Tôi đã thử bốn lần chạy và hệ thống của tôi thực hiện các công việc khác trong nền sẽ thay đổi thời gian)
Giá là phiên bản của tập lệnh này chỉ có thể xử lý các tệp được nén và không nén. Tốc độ so với tính linh hoạt: 3,1 giây cho phiên bản này so với 23 giây cho IO::Uncompress::AnyUncompress
phiên bản có xargs -P
trình bao bọc (hoặc 1m13 không có 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
thân thiện, chỉ cầnzcat
các tệp trước.