Bagaimana cara grep secara rekursif melalui file .gz?

135

Saya menggunakan skrip untuk secara teratur mengunduh pesan gmail saya yang mengompresi file .eml menjadi file .gz. Script membuat folder untuk setiap hari, dan kemudian memampatkan setiap pesan ke file sendiri.

Saya ingin cara mencari "string" di arsip ini.

Grep sendiri sepertinya tidak melakukannya. Saya juga mencoba SearchMonkey.

Kendor
sumber
16
gunakan zgrep:zgrep - search possibly compressed files for a regular expression
Arkadiusz Drabczyk

Jawaban:

141

Jika Anda ingin melakukan grep secara rekursif di semua file .eml.gz di direktori saat ini, Anda dapat menggunakan:

find . -name \*.eml.gz -print0 | xargs -0 zgrep "STRING"

Anda harus melarikan diri terlebih dahulu *sehingga shell tidak menafsirkannya. -print0memberitahu find untuk mencetak karakter nol setelah setiap file ditemukan; xargs -0membaca dari input standar dan menjalankan perintah setelahnya untuk setiap file; zgrepberfungsi seperti grep, tetapi tidak mengompres file terlebih dahulu.

JK Stafford
sumber
2
'-print0' dan '-0' tidak wajib. xargs menggunakan '\ n' secara default.
Jaime M.
1
Mereka diperlukan jika mungkin ada karakter spasi di jalur; tidak ada alasan selain kompleksitas untuk tidak menggunakannya.
Daniel Griscom
2
zgrepsebenarnya sepertinya lebih cepat daripada grepdijalankan pada file yang tidak terkompresi. Pasti karena file terkompresi dapat membaca HD dan didekompresi lebih cepat daripada membaca file yang tidak terkompresi dari HD.
Geremia
@Jaimem. xargsmenggunakan blanks (whitespace) secara default. Tentu, file hampir tidak pernah memiliki baris baru di dalamnya, tetapi spasi tidak pernah terdengar sebelumnya (bahkan jika sebagian besar jenis UNIX tidak menyukai mereka). Yang mengatakan, Anda dapat menyederhanakan tanpa khawatir tentang ruang putih bahkan lebih mudah: find . -name '*.eml.gz' -exec zgrep "STRING" {} +Itu mendapat banyak argumen yang sama per-peluncuran xargs, keamanan -print0/ -0, dan semua tanpa overhead dari peluncuran dan perpipaan proses ekstra, dan cukup ringkas. -execdengan +POSIX ditentukan, jadi itu harus di sebagian besar sistem mirip UNIX semi-baru-baru ini untuk pengetahuan saya.
ShadowRanger
@ Jared Apakah ada cara untuk melakukan pencarian wildcard hanya dengan mengetahui awal dari pola file? Sebagai contoh, saya memiliki file .gz yang memiliki cap tanggal / waktu di bagian akhir. ABCLog04_18_18_2_21.gz Apakah ada cara untuk mencari file yang dimulai dengan ABC * secara rekursif. Saya mencoba mengganti \*.eml.gzdalam contoh Anda di atas dengan ABCLog*dan mendapatkan kesalahan tentang format file .:find: paths must precede expression: ABCLog-2018-03-12-10-16-1.log.gz Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
DevelopingDeveloper
68

Ada banyak kebingungan di sini karena tidak hanya ada satu zgrep. Saya memiliki dua versi di sistem saya, zgrepdari gzipdan zgrepdari zutils. Yang pertama hanyalah skrip pembungkus yang memanggil gzip -cdfq. Itu tidak mendukung -r, --recursivesaklar. 1
Yang terakhir adalah c++program yang dan mendukung para -r, --recursivepilihan.
Menjalankan zgrep --version | head -n 1akan mengungkapkan yang mana (jika ada) dari mereka adalah default:

zgrep (gzip) 1.6

adalah skrip wrapper,

zgrep (zutils) 1.3

adalah cppexecutable.
Jika Anda memiliki yang terakhir, Anda dapat menjalankan:

zgrep 'pattern' -r --format=gz /path/to/dir

Bagaimanapun, seperti yang disarankan, find+ zgrepakan bekerja dengan baik dengan versi zgrep:

find /path/to/dir -name '*.gz' -exec zgrep -- 'pattern' {} +

Jika zgrephilang dari sistem Anda (sangat tidak mungkin), Anda dapat mencoba:

find /path/to/dir -name '*.gz' -exec sh -c 'gzip -cd "$0" | grep -- "pattern"' {} \;

tetapi ada kelemahan utama: Anda tidak akan tahu di mana pertandingan tersebut karena tidak ada nama file yang ditambahkan ke baris yang cocok.


1: karena akan bermasalah

don_crissti
sumber
1
jika zgrepdari zutils tidak tersedia Anda dapat menginstalnya di Ubuntu dengan sudo apt-get install zutils.
therealmarv
1
Lanjutan dari @therealmarv ... dan kemudian Ubuntu akan menggunakan zutils zgrep bukan yang gzip. Lalu -r bekerja!
Elijah Lynn
Apakah ada cara untuk mencetak nomor baris dari pola yang cocok?
DogEatDog
@DogEatDog - sama seperti grep -n, zgrep -nakan mencetak baris no. Ada dalam manual ...
don_crissti
7

agadalah varian dari grep, dengan beberapa fitur tambahan yang bagus.

  • memiliki opsi -z untuk file terkompresi,
  • memiliki banyak fitur ack.
  • cepat

Begitu:

ag -r -z your-pattern-goes-here   folder

Jika tidak dipasang,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)
Joao
sumber
1
Saya mendapat ag: truncated file: Successhasilnya. Adakah bendera lain yang harus saya tambahkan?
Yar
4

Rekursi sendiri mudah:

   -r, --recursive
          Read all files  under  each  directory,  recursively,  following
          symbolic  links  only  if they are on the command line.  This is
          equivalent to the -d recurse option.

   -R, --dereference-recursive
          Read all files under each directory,  recursively.   Follow  all
          symbolic links, unlike -r.

Namun, untuk file terkompresi Anda memerlukan sesuatu seperti:

shopt globstar 
for file in /path/to/directory/**/*gz; do zcat ""$file" | grep pattern; done

path/to/directory harus menjadi direktori induk yang berisi subdirektori untuk setiap hari.


zgrepadalah jawaban yang jelas tetapi, sayangnya, itu tidak mendukung -rbendera. Dari man zgrep:

Opsi grep ini akan menyebabkan zgrep berakhir dengan kode kesalahan: (- [d rR zZ] | --di * | --exc * | --inc * | --rec * | --nu *).

terdon
sumber
3

Jika sistem Anda memiliki zgrep, Anda dapat melakukannya

zgrep -irs your-pattern-goes-here the-folder-to-search-goes-here/

Jika sistem Anda tidak memiliki zgrep, Anda dapat menggunakan perintah find untuk menjalankan zcat dan grep terhadap setiap file seperti ini:

find the-folder-to-search-goes-here/ -name '*.gz' \ -exec sh -c 'echo "Searching {}" ; zcat "{}" | grep your-pattern-goes-here ' \;

Nate dari Kalamazoo
sumber
Maafkan saya greeness pada ini ... file yang akan dicari adalah beberapa lapisan dalam. ~ / gmvault-db / db / 2015-02 berisi folder untuk setiap bulan yang diarsipkan, dan kemudian di bawahnya file .gz untuk bulan itu disimpan. Jika saya mencari .mil di dalam seluruh pohon itu, apakah itu yang akan saya lakukan? find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Mencari {}"; zcat "{}" | grep .mil '\;
Kendor
1
Tidak apa-apa - "r" di -irs akan menyebabkan zgrep mencari secara rekursif. Perintah find beroperasi secara rekursif secara default, sehingga file apa pun yang berakhiran .gz akan di-zcatted dan diteruskan ke grep. (dan {} akan diperluas ke jalur relatif file yang akan dicari). Jadi, ketika Anda mendapatkan hit, itu akan didahului oleh Searching ~/gmvault-db/db/2015-02/03/whatever.gz
Nate dari Kalamazoo
Inilah yang saya dapatkan kembali: find: "paths harus mendahului ekspresi: -exec" Inilah perintah yang saya gunakan: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Mencari { } "; zcat "{}" | grep .mil '\;
Kendor
menghapus garis miring terbalik antara '* .gz' dan -exec.
Nate dari Kalamazoo
4
zgreptidak akan menerima -rbendera karena suatu alasan. Itu disebutkan dalam man zgrep(juga lihat jawaban saya).
terdon
0

xzgrep -l "string" ./*/*.eml.gz

xzgrep adalah turunan dari utils zgrep (less / bin / xzgrep)

Dari halaman Manual:

xzgrep memanggil grep (1) pada file yang dapat dikompresi atau dikompresi dengan xz (1), lzma (1), gzip (1), bzip2 (1), atau lzop (1). Semua opsi yang ditentukan diteruskan langsung ke grep (1).

-l cetak nama file yang cocok

-R untuk rekursi tidak akan berfungsi karena secara khusus dilarang dalam skrip, namun shell globbing sederhana harus membawa kita ke sana

./*/*.eml.gz

dari jalur relatif di mana ./today/sample.eml.gz, cocok dengan semua contoh yang satu tingkat di bawah posisi relatif kami di shell, yang diakhiri dengan ".eml.gz"

John
sumber