Secara efisien menghapus file dari .tgz besar

14

Asumsikan saya memiliki kompresi tar-ball gzip compressedArchive.tgz (+100 file, total + 5gb).

Apa yang akan menjadi cara tercepat untuk menghapus semua entri yang cocok dengan pola nama file yang diberikan misalnya awalan * .jpg dan kemudian menyimpan sisa-sisa dalam gzip: ed tar-ball lagi?

Mengganti arsip lama atau membuat arsip baru tidak penting, mana yang tercepat.

Aksel Willgert
sumber

Jawaban:

14

Dengan GNU tar, Anda dapat melakukan:

pigz -d < file.tgz |
  tar --delete --wildcards -f - '*/prefix*.jpg' |
  pigz > newfile.tgz

Dengan bsdtar:

pigz -d < file.tgz |
  bsdtar -cf - --exclude='*/prefix*.jpg' @- |
  pigz > newfile.tgz

( pigzmenjadi versi multi-utas gzip).

Anda dapat menimpa file itu sendiri seperti:

{ pigz -d < file.tgz |
    tar --delete --wildcards -f - '*/prefix*.jpg' |
    pigz &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file.tgz

Tapi itu cukup berisiko, terutama jika hasilnya lebih sedikit dikompresi daripada file asli (dalam hal ini, yang kedua pigzmungkin berakhir menimpa area file yang belum dibaca yang pertama).

Stéphane Chazelas
sumber
terima kasih atas jawabannya, terangkat. akan menjalankan patokan minggu depan untuk melihat mana yang berkinerja lebih baik untuk arsip dan sistem saya dan menerimanya.
Aksel Willgert
8

Jangan mengabaikan cara yang mudah: itu mungkin cukup cepat untuk tujuan Anda. Dengan avfs untuk mengakses arsip sebagai direktori:

cd ~/.avfs/path/to/original.tar.gz\#
pax -w -s '/^.*\.jpg$//' | gzip >/path/to/filtered.tar.gz        # POSIX
tar -czf /path/to/filtered.tar.gz -s '/^.*\.jpg$//' .            # BSD
tar -czf /path/to/filtered.tar.gz --transform '/^.*\.jpg$//' .   # GNU

Dengan alat yang lebih primitif, ekstrak terlebih dahulu file tidak termasuk .jpgfile, lalu buat arsip baru.

mkdir tmpdir && cd tmpdir
<original.tar.gz gzip -d | pax -r -pe -s '/^.*\.jpg$//'
pax -w . | gzip >filtered.tar.gz
cd .. && rm -rf tmpdir

Jika tar Anda memiliki --exclude:

mkdir tmpdir && cd tmpdir
tar -xzf original.tar.gz --exclude='*.jpg'
tar -czf filtered.tar.gz .
cd .. && rm -rf tmpdir

Namun ini dapat memotong kepemilikan file dan mode jika Anda tidak menjalankannya sebagai root. Untuk hasil terbaik, gunakan direktori sementara pada sistem file cepat - tmpfs jika Anda memiliki yang cukup besar.

Dukungan bagi pengarsip untuk bertindak sebagai pass-through (yaitu membaca arsip dan menulis arsip) cenderung terbatas. Tar GNU dapat menghapus anggota dari arsip dengan --deleteopsi operasi (" --deleteOpsi tersebut telah dilaporkan berfungsi dengan baik ketika tarbertindak sebagai filter dari stdinke stdout."), Dan itu mungkin opsi terbaik Anda.

Anda dapat membuat filter arsip yang kuat dalam beberapa baris Python. Its tarfileperpustakaan dapat membaca dan menulis dari non-seekable sungai, dan Anda dapat menggunakan kode sewenang-wenang dengan Python untuk filter, rename, memodifikasi ...

#!/usr/bin/python
import re, sys, tarfile
source = tarfile.open(fileobj=sys.stdin, mode='r|*')
dest = tarfile.open(fileobj=sys.stdout, mode='w|gz')
for member in source:
    if not (member.isreg() and re.match(r'.*\.jpg\Z', member.name)):
        sys.stderr.write(member.name + '\n')
        dest.addfile(member, source.extractfile(member))
dest.close()
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Itu juga akan membuat uid / nama pengguna jika dijalankan sebagai root kecuali jika dilakukan pada mesin yang memiliki pemetaan nama pengguna <=> uid yang sama dengan yang digunakan untuk membuat file tar. ACL, atribut yang diperluas dapat dipengaruhi juga. Dengan tar, Anda mungkin ingin menambahkan popsi.
Stéphane Chazelas
2

Dengan tar yang ada di Mac OSX, Anda bisa melakukan ini:

tar -czf b.tgz --exclude '*.jpg' @a.tgz
mv b.tgz a.tgz
Jake
sumber
1

Untuk melakukan ini, Anda mungkin harus mengekstrak semua isi file .tgz dalam direktori lokal lalu menghapus file yang tidak Anda inginkan kemudian mengkompres ulang file .tgz.

Ini lama dan Anda membutuhkan ruang disk yang cukup tetapi untuk yang terbaik dari pengetahuan saya, tidak ada cara lain untuk melakukannya.

Karena Anda sudah memiliki beberapa jalur seperti /tmpdir/withalotofspaceitu yang memiliki ruang kosong yang cukup (periksa menggunakan df -h /tmpdir/withalotofspace), Anda dapat melakukan sesuatu seperti ini:

$ cd /tmpdir/withalotofspace
$ tar -xvfz /path/to/compressedArchive.tgz
$ find /tmpdir/withalotofspace/ -type f -iname '*.jpg' -delete
$ tar -cvzf /path/to/purgedcompressedArchive.tgz .
DavAlPi
sumber
Seperti yang ditunjukkan oleh jawaban lain, melalui pemipaan tidak perlu menyimpan data yang tidak terkompresi pada disk kapan saja
Tobias Kienzler
0

Saya suka jawabannya oleh @Gilles, kecuali itu bisa lebih disederhanakan. Setelah membuka ritsleting, misalnya gunzip foo.tgzfile akan foo.tardan file dapat dihapus dengan tar -f foo.tar --delete file|directory. Di bawah ini adalah contoh menghapus direktori dari file tar.

    phablet@ubuntu-phablet:~/Downloads$ tar -cvf moo.tar moo1/
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -f moo.tar --delete "moo1/moo2/moo3"
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/

Jenis file spesifik dapat ditemukan bersama tar -tf foo.tar|egrep -i '.jpg$'.

Funmungus
sumber