Temukan secara rekursif semua file arsip dengan beragam format arsip dan cari mereka untuk pola nama file

11

Paling-paling saya ingin mendapat panggilan seperti ini:

$searchtool /path/to/search/ -contained-file-name "*vacation*jpg"

... jadi alat ini

  • melakukan pemindaian rekursif dari jalur yang diberikan
  • mengambil semua file dengan format arsip yang didukung yang setidaknya harus "paling umum" seperti zip, rar, 7z, tar.bz, tar.gz ...
  • dan pindai daftar file arsip untuk pola nama yang dimaksud (di sini *vacation*jpg)

Saya mengetahui cara menggunakan alat bantu, tar, unzip, dan yang serupa. Saya bisa menggabungkan ini dengan skrip shell tapi saya sedang mencari solusi sederhana yang mungkin shell-liner atau alat khusus (petunjuk untuk alat GUI dipersilahkan tetapi solusi saya harus berbasis baris perintah).

mdo
sumber

Jawaban:

9

(Diadaptasi dari Bagaimana saya secara rekursif memahami arsip terkompresi? )

Instal AVFS , sistem file yang menyediakan akses transparan di dalam arsip. Pertama jalankan perintah ini sekali untuk mengatur tampilan sistem file mesin Anda di mana Anda dapat mengakses arsip seolah-olah itu adalah direktori:

mountavfs

Setelah ini, jika /path/to/archive.zipmerupakan arsip yang dikenali, maka ~/.avfs/path/to/archive.zip#adalah direktori yang tampaknya mengandung isi arsip.

find ~/.avfs"$PWD" \( -name '*.7z' -o -name '*.zip' -o -name '*.tar.gz' -o -name '*.tgz' \) \
     -exec sh -c '
                  find "$0#" -name "*vacation*.jpg"
                 ' {} 'Test::Version' \;

Penjelasan:

  • Pasang sistem file AVFS.
  • Cari file arsip ~/.avfs$PWD, yang merupakan tampilan AVFS dari direktori saat ini.
  • Untuk setiap arsip, jalankan cuplikan shell yang ditentukan (dengan $0= nama arsip dan $1= pola untuk dicari).
  • $0#adalah tampilan direktori arsip $0.
  • {\}daripada {}diperlukan jika findpengganti luar {}dalam -exec ;argumen (beberapa melakukannya, beberapa tidak).

Atau dalam zsh ≥4.3:

mountavfs
ls -l ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip)(e\''
     reply=($REPLY\#/**/*vacation*.jpg(.N))
'\')

Penjelasan:

  • ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip) cocok dengan arsip dalam tampilan AVFS dari direktori saat ini dan subdirektori.
  • PATTERN(e\''CODE'\')berlaku KODE untuk setiap kecocokan POLA. Nama file yang cocok ada di $REPLY. Mengatur replyarray mengubah kecocokan menjadi daftar nama.
  • $REPLY\# adalah tampilan direktori arsip.
  • $REPLY\#/**/*vacation*.jpgcocok dengan *vacation*.jpgfile dalam arsip.
  • The Ngumpal kualifikasi membuat pola memperluas untuk daftar kosong jika tidak ada pertandingan.
Gilles 'SANGAT berhenti menjadi jahat'
sumber
9

Jika Anda menginginkan sesuatu yang lebih sederhana daripada solusi AVFS, saya menulis skrip Python untuk melakukannya bernama arkfind . Anda sebenarnya bisa melakukannya

$ arkfind /path/to/search/ -g "*vacation*jpg"

Ini akan melakukan ini secara rekursif, sehingga Anda dapat melihat arsip di dalam arsip dengan kedalaman yang sewenang-wenang.

detly
sumber
Terima kasih, kontribusi yang bagus! Apalagi jika AVFS ada pilihan.
mdo
Akan lebih bagus jika mendukung file jar.
Chemik
@Chemik - dicatat ! Saya akan melakukan sedikit lebih banyak pekerjaan di akhir pekan ini :) JAR seharusnya tidak terlalu sulit, saya percaya itu benar-benar hanya file zip ke dunia luar.
detly
@Chemik - Saya baru mencobanya, dan seharusnya mendukung file JAR dalam bentuknya yang sekarang. Bisakah Anda mengujinya, dan jika tidak berfungsi seperti yang Anda harapkan, ajukan bug pada halaman Github? (Saya baru saja memperbaiki bug, jadi pastikan untuk memperbarui salinan Anda.)
detly
1
Ya saya mengerti sekarang, itu berhasil. Anda dapat menambahkan "file JAR" ke README :)
Chemik
2

Solusi saya yang biasa :

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|DESIRED_FILE_TO_SEARCH'

Contoh:

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|characterize.txt'

Resuls seperti:

foozip1.zip:
foozip2.zip:
foozip3.zip:
    DESIRED_FILE_TO_SEARCH
foozip4.zip:
...

Jika Anda hanya ingin file zip dengan hits di atasnya:

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|FILENAME' | grep -B1 'FILENAME'

FILENAME di sini digunakan dua kali, sehingga Anda dapat menggunakan variabel.

Dengan find, Anda mungkin menggunakan PATH / TO / SEARCH

Rodrigo Gurgel
sumber
2

Solusi lain yang berfungsi adalah zgrep

zgrep -r filename *.zip
John Oxley
sumber
1
Apa implementasi zgrepitu? Itu tidak berfungsi dengan yang dikirim bersama GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6)
Stéphane Chazelas
2

Keramahtamahan pengguna IMHO juga harus diperhatikan:

 while read -r zip_file ; do echo "$zip_file" ; unzip -l "$zip_file" | \
 grep -i --color=always -R "$to_srch"; \
 done < <(find . \( -name '*.7z' -o -name '*.zip' \)) | \
 less -R

dan untuk tar (yang ini belum diuji ...)

 while read -r tar_file ; do echo "$tar_file" ; tar -tf  "$tar_file" | \
 grep -i --color=always -R "$to_srch"; \
 done < <(find . \( -name '*.tar.gz' -o -name '*.tar' \)) | \
 less -R
Yordan Georgiev
sumber
unzipImplementasi apa yang dapat menangani file 7z atau tar.gz?
Stéphane Chazelas
ya itu adalah bug ... dikoreksi ... orang pasti harus menggunakan binari yang benar untuk jenis file yang benar ... Saya hanya bertujuan untuk mendemonstrasikan satu-liner .. ya ampun yang ini hampir akan sampai pada kondisi siap sebagai bagaimana cara menerima ...
Yordan Georgiev
0

libarchive's bsdtardapat menangani sebagian besar dari mereka format file, sehingga Anda bisa melakukan:

find . \( -name '*.zip' -o     \
          -name '*.tar' -o     \
          -name '*.tar.gz' -o  \
          -name '*.tar.bz2' -o \
          -name '*.tar.xz' -o  \
          -name '*.tgz' -o     \
          -name '*.tbz2' -o    \
          -name '*.7z' -o      \
          -name '*.iso' -o     \
          -name '*.cpio' -o    \
          -name '*.a' -o       \
          -name '*.ar' \)      \
       -type f                 \
       -exec bsdtar tf {} '*vacation*jpg' \; 2> /dev/null

Yang dapat Anda sederhanakan (dan tingkatkan agar sesuai dengan kasus-tidak sensitif) dengan GNU finddengan:

find . -regextype egrep \
       -iregex '.*\.(zip|7z|iso|cpio|ar?|tar(|\.[gx]z|\.bz2)|tgz|tbz2)' \
       -type f \
       -exec bsdtar tf {} '*vacation*jpg' \; 2> /dev/null

Itu tidak mencetak jalur arsip tempat *vacation*jpgfile - file itu ditemukan. Untuk mencetak nama itu, Anda dapat mengganti baris terakhir dengan:

-exec sh -ac '
   for ARCHIVE do
     bsdtar tf "$ARCHIVE" "*vacation*jpg" |
       awk '\''{print ENVIRON["ARCHIVE"] ": " $0}'\''
   done' sh {} + 2> /dev/null

yang memberikan output seperti:

./a.zip: foo/blah_vacation.jpg
./a.zip: bar/blih_vacation.jpg
./a.tar.gz: foo/blah_vacation.jpg
./a.tar.gz: bar/blih_vacation.jpg

Atau dengan zsh:

setopt extendedglob # best in ~/.zshrc
for archive (**/*.(#i)(zip|7z|iso|cpio|a|ar|tar(|.gz|.xz|.bz2)|tgz|tbz2)(.ND)) {
  matches=("${(f@)$(bsdtar tf $archive '*vacation*jpg' 2> /dev/null)"})
  (($#matches)) && printf '%s\n' "$archive: "$^matches
}

Perhatikan bahwa ada sejumlah format file lain yang hanya zipatau tgzfile dalam suka .jaratau .docxfile. Anda dapat menambahkannya ke find/ zshpola pencarian Anda, bsdtartidak peduli tentang ekstensi (seperti pada, itu tidak bergantung pada ekstensi untuk menentukan jenis file).

Perhatikan bahwa di *vacation*.jpgatas cocok dengan jalur anggota arsip lengkap, bukan hanya nama file, sehingga akan cocok pada vacation.jpgtetapi juga pada vacation/2014/file.jpg.

Untuk mencocokkan hanya pada nama file, satu trik adalah dengan menggunakan mode ekstrak , gunakan -s(substitusi) yang menggunakan regexps dengan pbendera untuk mencetak nama file yang cocok dan kemudian pastikan tidak ada file yang diekstraksi, seperti:

bsdtar -'s|.*vacation[^/]*$||' -'s|.*||' -xf "$archive"

Perhatikan bahwa ini akan menampilkan daftar pada stderr dan menambahkan >>ke setiap baris. Dalam kasus apa pun bsdtar, seperti kebanyakan tarimplementasi dapat memotong nama file pada layar jika mengandung beberapa karakter seperti baris baru atau garis miring terbalik (diterjemahkan sebagai \natau \\).

Stéphane Chazelas
sumber