Saya sedang mencari cara untuk membuat daftar semua file dalam direktori yang berisi set lengkap kata kunci yang saya cari, di mana saja dalam file tersebut.
Jadi, kata kunci tidak perlu muncul di baris yang sama.
Salah satu cara untuk melakukan ini adalah:
grep -l one $(grep -l two $(grep -l three *))
Tiga kata kunci hanyalah sebuah contoh, bisa juga dua, atau empat, dan seterusnya.
Cara kedua yang bisa saya pikirkan adalah:
grep -l one * | xargs grep -l two | xargs grep -l three
Metode ketiga, yang muncul dalam pertanyaan lain , adalah:
find . -type f \
-exec grep -q one {} \; -a \
-exec grep -q two {} \; -a \
-exec grep -q three {} \; -a -print
Tapi itu jelas bukan arah yang saya tuju di sini. Aku ingin sesuatu yang memerlukan lebih sedikit mengetik, dan mungkin hanya satu panggilan untuk grep
, awk
, perl
atau serupa.
Misalnya, saya suka cara awk
Anda mencocokkan baris yang berisi semua kata kunci , seperti:
awk '/one/ && /two/ && /three/' *
Atau, cetak hanya nama file:
awk '/one/ && /two/ && /three/ { print FILENAME ; nextfile }' *
Tetapi saya ingin mencari file di mana kata kunci mungkin berada di mana saja dalam file, tidak harus pada baris yang sama.
Solusi yang disukai akan lebih ramah gzip, misalnya grep
memiliki zgrep
varian yang berfungsi pada file terkompresi. Mengapa saya menyebutkan ini, adalah bahwa beberapa solusi mungkin tidak berfungsi dengan baik mengingat kendala ini. Misalnya, dalam awk
contoh mencetak file yang cocok, Anda tidak bisa begitu saja melakukan:
zcat * | awk '/pattern/ {print FILENAME; nextfile}'
Anda perlu mengubah perintah secara signifikan, menjadi sesuatu seperti:
for f in *; do zcat $f | awk -v F=$f '/pattern/ { print F; nextfile }'; done
Jadi, karena kendala, Anda perlu menelepon awk
berkali-kali, meskipun Anda hanya dapat melakukannya sekali dengan file yang tidak dikompresi. Dan tentu saja, akan lebih baik untuk hanya melakukan zawk '/pattern/ {print FILENAME; nextfile}' *
dan mendapatkan efek yang sama, jadi saya lebih suka solusi yang memungkinkan ini.
gzip
ramah, cukupzcat
file terlebih dahulu.grep
solusinya mudah diadaptasi hanya dengan awalangrep
panggilan denganz
, tidak perlu bagi saya untuk juga menangani nama file.grep
. AFAIK, hanyagrep
dancat
memiliki "z-varian" standar. Saya tidak berpikir Anda akan mendapatkan sesuatu yang lebih sederhana daripada menggunakanfor f in *; do zcat -f $f ...
solusi. Apa pun yang lain harus menjadi program lengkap yang memeriksa format file sebelum membuka atau menggunakan perpustakaan untuk melakukan hal yang sama.Jawaban:
Jika Anda ingin secara otomatis menangani file gzip, jalankan ini dalam satu lingkaran dengan
zcat
(lambat dan tidak efisien karena Anda akan forkingawk
berkali-kali dalam satu lingkaran, sekali untuk setiap nama file) atau menulis ulang algoritma yang samaperl
dan menggunakanIO::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 digunakanIO::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 modulGetopt::Std
atauGetopt::Long
.Jalankan seperti ini:
(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)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), danmap()
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: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 denganfind -exec
atauxargs
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 sebagailibperlio-gzip-perl
) alih-alihIO::Uncompress::AnyUncompress
saya punya waktu sekitar 3,1 detik untuk memproses 74MB file log saya. Ada juga beberapa perbaikan kecil dengan menggunakan hash sederhana daripadaSet::Scalar
(yang juga menghemat beberapa detik denganIO::Uncompress::AnyUncompress
versi).PerlIO::gzip
direkomendasikan sebagai perl gunzip tercepat di /programming//a/1539271/137158 (ditemukan dengan pencarian googleperl 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 denganxargs -P
pembungkus (atau tanpa 1m13sxargs -P
).sumber
for f in *; do zcat $f | awk -v F=$f '/one/ {a++}; /two/ {b++}; /three/ {c++}; a&&b&&c { print F; nextfile }'; done
berfungsi dengan baik, tetapi memang, membutuhkan 3 kali selamagrep
solusi saya , dan sebenarnya lebih rumit.apt-get install libset-scalar-perl
menggunakan skrip. Tetapi tampaknya tidak berakhir dalam waktu yang wajar.Setel pemisah rekaman
.
sehinggaawk
akan memperlakukan seluruh file sebagai satu baris:Demikian pula dengan
perl
:sumber
for f in *; do zcat $f | awk -v RS='.' -v F=$f '/one/ && /two/ && /three/ { print F }'; done
tidak menghasilkan apa-apa.zcat -f "$f"
jika beberapa file tidak dikompresi.awk -v RS='.' '/bfs/&&/none/&&/rgg/{print FILENAME}' greptest/*.txt
masih tidak mengembalikan hasil, sementaragrep -l rgg $(grep -l none $(grep -l bfs greptest/*.txt))
mengembalikan hasil yang diharapkan.Untuk file terkompresi, Anda bisa mengulang setiap file dan mendekompres terlebih dahulu. Kemudian, dengan versi jawaban yang sedikit dimodifikasi, Anda dapat melakukan:
Script Perl akan keluar dengan
0
status (sukses) jika ketiga string ditemukan. The}{
adalah Perl singkatan untukEND{}
. Apa pun yang mengikuti itu akan dieksekusi setelah semua input telah diproses. Jadi skrip akan keluar dengan status keluar non-0 jika tidak semua string ditemukan. Oleh karena itu,&& printf '%s\n' "$f"
akan mencetak nama file hanya jika ketiganya ditemukan.Atau, untuk menghindari memuat file ke dalam memori:
Akhirnya, jika Anda benar-benar ingin melakukan semuanya dalam sebuah skrip, Anda dapat melakukan:
Simpan skrip di atas sebagai
foo.pl
tempat Anda$PATH
, buat itu dapat dieksekusi dan jalankan seperti ini:sumber
Dari semua solusi yang diusulkan sejauh ini, solusi asli saya menggunakan grep adalah yang tercepat, selesai dalam 25 detik. Kelemahannya adalah membosankan untuk menambahkan dan menghapus kata kunci. Jadi saya datang dengan skrip (dijuluki
multi
) yang mensimulasikan perilaku, tetapi memungkinkan untuk mengubah sintaks:Jadi sekarang, menulis
multi grep one two three -- *
setara dengan proposal asli saya dan berjalan dalam waktu yang bersamaan. Saya juga dapat dengan mudah menggunakannya pada file terkompresi dengan menggunakanzgrep
argumen pertama sebagai gantinya.Solusi lain
Saya juga bereksperimen dengan skrip Python menggunakan dua strategi: mencari semua kata kunci baris demi baris, dan mencari di seluruh file kata kunci berdasarkan kata kunci. Strategi kedua lebih cepat dalam kasus saya. Tapi itu lebih lambat daripada hanya menggunakan
grep
, finishing dalam 33 detik. Pencocokan kata kunci baris demi baris selesai dalam 60 detik.The Script yang diberikan oleh Terdon selesai dalam 54 detik. Sebenarnya butuh waktu dinding 39 detik, karena prosesor saya adalah dual core. Yang menarik, karena skrip Python saya mengambil 49 detik waktu dinding (dan
grep
29 detik).The Script oleh cas gagal untuk mengakhiri dalam waktu yang wajar, bahkan pada sejumlah kecil file yang diproses dengan
grep
di bawah 4 detik, jadi aku harus membunuhnya.Tetapi
awk
proposal aslinya , meskipun lebih lambat darigrep
apa adanya, memiliki potensi keuntungan. Dalam beberapa kasus, setidaknya dalam pengalaman saya, mungkin untuk mengharapkan bahwa semua kata kunci harus muncul di suatu tempat di kepala file jika ada di file sama sekali. Ini memberikan solusi ini dorongan dramatis dalam kinerja:Selesai dalam seperempat detik, dibandingkan dengan 25 detik.
Tentu saja, kami mungkin tidak memiliki keunggulan dalam mencari kata kunci yang diketahui terjadi di dekat awal file. Dalam kasus seperti itu, solusi tanpa
NR>100 {exit}
membutuhkan 63 detik (50-an waktu dinding).File tidak terkompresi
Tidak ada perbedaan yang signifikan dalam menjalankan waktu antara
grep
solusi saya danawk
proposal cas , keduanya membutuhkan sepersekian detik untuk dieksekusi.Perhatikan bahwa inisialisasi variabel
FNR == 1 { f1=f2=f3=0; }
wajib dalam kasus tersebut untuk mengatur ulang penghitung untuk setiap file yang diproses berikutnya. Dengan demikian, solusi ini memerlukan pengeditan perintah di tiga tempat jika Anda ingin mengubah kata kunci atau menambahkan yang baru. Di sisi lain,grep
Anda hanya dapat menambahkan| xargs grep -l four
atau mengedit kata kunci yang Anda inginkan.Kelemahan dari
grep
solusi yang menggunakan substitusi perintah, adalah bahwa itu akan hang jika di manapun dalam rantai, sebelum langkah terakhir, tidak ada file yang cocok. Ini tidak mempengaruhixargs
varian karena pipa akan dibatalkan setelahgrep
mengembalikan status tidak nol. Saya telah memperbarui skrip saya untuk digunakanxargs
sehingga saya tidak harus menangani ini sendiri, membuat skrip lebih sederhana.sumber
not all(p in text for p in patterns)
not
) dan selesai dalam 32 detik, jadi tidak banyak perbaikan, tapi tentu saja lebih mudah dibaca.PerlIO::gzip
bukanIO::Uncompress::AnyUncompress
. sekarang hanya membutuhkan 3,1 detik, bukannya 1m13s untuk memproses 74MB file log saya.eval $(lesspipe)
(misalnya di Anda.profile
, dll), Anda dapat menggunakanless
sebagai gantizcat -f
danfor
pembungkus lingkaran Andaawk
akan dapat memproses segala jenis file yangless
dapat (gzip, bzip2, xz, dan banyak lagi) .... kurang bisa mendeteksi jika stdout adalah pipa dan hanya akan mengeluarkan aliran ke stdout jika itu.Opsi lain - mengumpankan kata satu per satu
xargs
untuk menjalankannyagrep
terhadap file.xargs
itu sendiri dapat dibuat untuk keluar segera setelah doagrep
kegagalan pengembalian dengan kembali255
ke sana (periksaxargs
dokumentasi). Tentu saja pemijahan cangkang dan forking yang terlibat dalam solusi ini kemungkinan akan memperlambatnya secara signifikandan untuk mengulanginya
sumber
_
danfile
? Akankah pencarian ini dalam banyak file diteruskan sebagai argumen dan mengembalikan file yang berisi semua kata kunci?_
, itu dilewatkan sebagai$0
ke shell menelurkan - ini akan muncul sebagai nama perintah dalam outputps
- saya akan tunduk pada master di sini