awk 'FNR == 1 { f1=f2=f3=0; };
/one/ { f1++ };
/two/ { f2++ };
/three/ { f3++ };
f1 && f2 && f3 {
print FILENAME;
nextfile;
}' *
Jika Anda ingin secara otomatis menangani file gzip, jalankan ini dalam satu lingkaran dengan zcat
(lambat dan tidak efisien karena Anda akan forking awk
berkali-kali dalam satu lingkaran, sekali untuk setiap nama file) atau menulis ulang algoritma yang sama perl
dan menggunakan IO::Uncompress::AnyUncompress
modul perpustakaan yang dapat dekompresi beberapa jenis file terkompresi (gzip, zip, bzip2, lzop). atau dalam python, yang juga memiliki modul untuk menangani file terkompresi.
Berikut adalah perl
versi yang digunakan IO::Uncompress::AnyUncompress
untuk memungkinkan sejumlah pola dan sejumlah nama file (mengandung teks biasa atau teks terkompresi).
Semua argumen sebelumnya --
diperlakukan sebagai pola pencarian. Semua argumen setelah --
diperlakukan sebagai nama file. Opsi penanganan yang primitif tetapi efektif untuk pekerjaan ini. Penanganan opsi yang lebih baik (misalnya untuk mendukung -i
opsi untuk pencarian case-sensitive) dapat dicapai dengan modul Getopt::Std
atau Getopt::Long
.
Jalankan seperti ini:
$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt
(Saya tidak akan membuat daftar file {1..6}.txt.gz
dan di {1..6}.txt
sini ... mereka hanya berisi beberapa atau semua kata "satu" "dua" "tiga" "empat" "lima" dan "enam" untuk pengujian. File-file yang tercantum dalam output di atas LAKUKAN mengandung ketiga pola pencarian. Uji sendiri dengan data Anda sendiri)
#! /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;
}
}
}
Hash %patterns
adalah berisi kumpulan pola lengkap yang file harus mengandung setidaknya satu dari setiap anggota
$_pstring
adalah string yang berisi kunci yang diurutkan dari hash itu. String $pattern
berisi ekspresi reguler pra-dikompilasi juga dibangun dari %patterns
hash.
$pattern
dibandingkan dengan setiap baris dari setiap file input (menggunakan /o
pengubah untuk mengkompilasi $pattern
hanya sekali seperti yang kita tahu itu tidak akan pernah berubah selama menjalankan), dan map()
digunakan untuk membangun hash (% s) yang berisi kecocokan untuk setiap file.
Setiap kali semua pola telah terlihat di file saat ini (dengan membandingkan jika $m_string
(kunci yang diurutkan dalam %s
) sama dengan $p_string
), cetak nama file dan lewati ke file berikutnya.
Ini bukan solusi yang sangat cepat, tetapi tidak terlalu lambat. Versi pertama mengambil 4m58s untuk mencari tiga kata dalam 74MB senilai file log terkompresi (total 937MB terkompresi). Versi saat ini membutuhkan 1m13s. Mungkin ada optimisasi lebih lanjut yang bisa dilakukan.
Salah satu optimasi jelas adalah dengan menggunakan ini dalam hubungannya dengan xargs
's -P
alias --max-procs
untuk menjalankan beberapa pencarian pada himpunan bagian dari file secara paralel. Untuk melakukan itu, Anda perlu menghitung jumlah file dan membaginya dengan jumlah core / cpus / threads yang dimiliki sistem Anda (dan akhiri dengan menambahkan 1). misalnya ada 269 file yang sedang dicari dalam set sampel saya, dan sistem saya memiliki 6 core (AMD 1090T), jadi:
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[@]}" --
Dengan optimasi itu, hanya butuh 23 detik untuk menemukan semua 18 file yang cocok. Tentu saja, hal yang sama dapat dilakukan dengan solusi lainnya. CATATAN: Urutan nama file yang tercantum dalam output akan berbeda, jadi mungkin perlu disortir sesudahnya jika itu penting.
Seperti dicatat oleh @arekolek, banyak zgrep
s dengan find -exec
atau xargs
dapat melakukannya secara signifikan lebih cepat, tetapi skrip ini memiliki keuntungan mendukung sejumlah pola untuk mencari, dan mampu menangani beberapa jenis kompresi yang berbeda.
Jika skrip terbatas untuk memeriksa hanya 100 baris pertama dari setiap file, skrip tersebut menjalankan semuanya (dalam 74MB sampel 269 file saya) dalam 0,6 detik. Jika ini berguna dalam beberapa kasus, ini dapat dibuat menjadi opsi baris perintah (misalnya -l 100
) tetapi berisiko tidak menemukan semua file yang cocok.
BTW, menurut halaman manual untuk IO::Uncompress::AnyUncompress
, format kompresi yang didukung adalah:
Optimasi terakhir (saya harap). Dengan menggunakan PerlIO::gzip
modul (dikemas dalam bahasa debian sebagai libperlio-gzip-perl
) alih-alih IO::Uncompress::AnyUncompress
saya punya waktu sekitar 3,1 detik untuk memproses 74MB file log saya. Ada juga beberapa perbaikan kecil dengan menggunakan hash sederhana daripada Set::Scalar
(yang juga menghemat beberapa detik dengan IO::Uncompress::AnyUncompress
versi).
PerlIO::gzip
direkomendasikan sebagai perl gunzip tercepat di /programming//a/1539271/137158 (ditemukan dengan pencarian google perl fast gzip decompress
)
Menggunakan xargs -P
dengan ini tidak meningkatkan sama sekali. Bahkan itu bahkan tampaknya memperlambatnya mulai dari 0,1 hingga 0,7 detik. (Saya mencoba empat kali dan sistem saya melakukan hal-hal lain di latar belakang yang akan mengubah waktunya)
Harganya adalah versi skrip ini hanya dapat menangani file yang di-gzip dan tidak dikompresi. Kecepatan vs fleksibilitas: 3,1 detik untuk versi ini vs 23 detik untuk IO::Uncompress::AnyUncompress
versi dengan xargs -P
pembungkus (atau tanpa 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
ramah, cukupzcat
file terlebih dahulu.