Bagaimana saya bisa memfilter konten file tar, menghasilkan file tar lain di dalam pipa?

13

Pertimbangkan file tar tunggal dari sistem eksternal yang berisi beberapa direktori dengan berbagai atribut yang ingin saya pertahankan seperti izin, mtimes, dll. Bagaimana saya dapat dengan mudah mengambil subset dari file-file ini sebagai pengguna biasa (bukan root)?

Mencari sesuatu seperti:

tar -f some.tar.gz --subset subdir/ | ssh remote@system tar xvz

Juga penting bahwa atribut utama (kepemilikan, grup, mode, mtime) dalam arsip tar ini dipertahankan. Bagaimana dengan atribut lain dalam file tar seperti kata kunci header yang diperluas ?

Poin bonus untuk solusi yang menghindari penggunaan direktori sementara jika subdir ini berisi file besar.

Lekensteyn
sumber

Jawaban:

14

bsdtar (berdasarkan libarchive) dapat menyaring tar (dan beberapa arsip lainnya) dari stdin ke stdout. Misalnya dapat melewati hanya nama file yang cocok dengan suatu pola, dan dapat melakukan s/old/new/penggantian nama. Ini sudah dikemas untuk sebagian besar distro, misalnya seperti bsdtardi Ubuntu.

sudo apt-get install bsdtar   # or aptitude, if you have it.

# example from the man page:
bsdtar -c -f new.tar --include='*foo*' @old.tgz
#create new.tar containing only entries from old.tgz containing the string ‘foo’
bsdtar -czf - --include='*foo*' @-  # filter stdin to stdout, with gzip compression of output.

Catatan yang memiliki banyak pilihan format kompresi untuk input / output, sehingga Anda tidak perlu menyalurkan sendiri melalui gunzip / lz4 secara manual. Anda dapat menggunakan -untuk stdin dengan @tarfilesintaks, dan / atau -untuk stdout seperti biasa.


Pencarian saya juga menemukan alat modifikasi tar streaming ini yang tampaknya ingin Anda menentukan perubahan arsip yang Anda inginkan menggunakan javascript. (Saya pikir semuanya ditulis dalam js).

https://github.com/mafintosh/tar-stream

Peter Cordes
sumber
1
Luar biasa, tidak tahu bahwa @original.tarpendekatan ini dimungkinkan dengan bsdtar. Tampaknya bekerja dengan atribut yang diperluas dan kompresi juga, </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(dan karena alasan tertentu pilihan kosong menghasilkan serangkaian nol byte, tapi itu bukan masalah besar bagi saya).
Lekensteyn
1
Menurut pengujian saya, s/old/new/ tidak berfungsi pada file yang berasal dari arsip lama menggunakan @ old.tgz, itu hanya bekerja pada file nyata, pengarsipan langsung dari sistem file. Sungguh memalukan, karena itu akan menjadi kasus penggunaan yang paling berguna bagi saya.
Bart
4

Cara termudah adalah menyalin seluruh arsip; Saya kira Anda tidak ingin melakukan itu karena terlalu besar.

Alat baris perintah yang biasa ( tar, pax) tidak mendukung menyalin anggota arsip ke arsip lain.

Jika Anda tidak perlu mempertahankan kepemilikan, saya sarankan menggunakan filesystem FUSE . Anda dapat menggunakan archivemount untuk me-mount arsip sebagai sistem file; lakukan ini untuk arsip sumber, dan jalankan tar pada sistem file yang terpasang.

archivemount some.tar.gz mnt
cd mnt
tar -cz subdir | ssh example.com tar -xz
fusermount -u mnt

Atau, Anda dapat menggunakan AVFS :

mountavfs
cd ~/.avfs$PWD/some.tar.gz\#
tar -cz subdir | ssh example.com tar -xz

Atau, Anda dapat menjalankan tararsip asli dan mengekstrak ke mesin jarak jauh melalui SSHFS .

sshfs example.com: mnt
cd mnt
tar -xf /path/to/some.tar.gz subdir
fusermount -u mnt

Namun semua metode ini rumit jika Anda perlu mempertahankan kepemilikan. Semuanya melibatkan pengekstrakan ke file di mesin lokal, jadi kepemilikan file ini harus menjadi kepemilikan jarak jauh yang dimaksud . Ini membutuhkan berjalan sebagai root dan mungkin tidak memberikan hasil yang dimaksudkan jika file dimiliki oleh akun yang memiliki nama atau ID yang berbeda antara mesin lokal dan host jarak jauh.

tarfilePustaka Python menyediakan cara yang cukup mudah untuk memanipulasi anggota tar, sehingga Anda dapat mengocoknya dari satu file tar ke yang lain. Ini mendukung format standar POSIX (ustar, pax) serta beberapa ekstensi GNU. Berikut ini adalah skrip Python yang belum teruji yang membaca file tar (mungkin dikompres dengan gzip atau bzip2) pada input standarnya dan menulis file tar yang dikompres dengan bzip2 pada output standarnya. Anggota dari sumber disalin jika mereka mulai dengan argumen yang diteruskan ke skrip.

#!/usr/bin/env python2
import sys, tarfile
source = tarfile.open(fileobj=sys.stdin)
destination = tarfile.open(fileobj=sys.stdout, mode='w:bz2')
for info in source:
    if info.name.startswith(sys.argv[1]):
        destination.addfile(info)
destination.close()

Untuk dipanggil sebagai

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
bsdtar (berdasarkan libarchive) dapat menyaring arsip tar dengan cepat, lihat jawaban saya.
Peter Cordes
Tugasnya adalah mengekstraksi data dari gambar firmware, jadi kepemilikan / keanggotaan grup memang penting. Pendekatan python bisa bekerja.
Lekensteyn
0

Pendekatan alternatif tanpa hak istimewa adalah menggunakan fakerootprogram untuk berpura-pura bahwa Anda diizinkan mengubah kepemilikan. Sementara atribut tar lainnya hilang, ia tetap mempertahankan mode, mtime dan uid / gid. Perintah-perintah ini membuat direktori sementara, mengekstrak subset dari file dan akhirnya membuat arsip baru:

mkdir tmp
<some.tar.gz \
fakeroot -- sh -c 'cd tmp && tar -xzf- subdir/ && tar -czf- subdir' |
   ssh remote@system tar -xzvf-
rm -rf tmp
Lekensteyn
sumber
0

GNU tarmemang memiliki --deleteopsi:

$ tar -c a b c | tar --delete a | tar -t
b
c

Dengan cara ini, Anda bisa mendapatkan subset dari tar input dengan menentukan apa yang tidak termasuk dalam output.

Sayangnya saya tidak bisa mendapatkan --excludeopsi untuk bekerja --delete, jadi sepertinya Anda pertama-tama perlu mendapatkan daftar eksplisit ( -t) hal-hal untuk dihapus dan kemudian meneruskannya ke permohonan lain tar.

$ tar --delete --no-recursion `tar -t --exclude subdir <some.tar` <some.tar | ssh ...

Atau Anda dapat menyimpan daftar dalam file eksternal jika terlalu panjang atau rumit:

$ tar -t --exclude subdir <some.tar >to_delete.lst
$ tar --delete --no-recursion -T to_delete.lst <some.tar | ssh ...
Karel Vlk
sumber
-1

Dari yang saya tahu, tarperintah tidak bisa menggunakan format tar baik sebagai input dan output. Anda harus mengekstrak file Anda secara lokal entah bagaimana, dan menggunakan tar lagi untuk membuat tarfile on-the-fly, dengan sesuatu seperti ini ( -sarana input / output standar digunakan sebagai pengganti file):

tar cf - subdir/ | ssh remote@system 'cd extractdir && tar xvf -'

Perhatikan bahwa memiliki tarkemampuan mengekstrak tarfile langsung di tarfile lain adalah ide yang menarik ...

Uriel
sumber
Tanpa root, ini akan kehilangan semua informasi kepemilikan / grup yang secara eksplisit ingin saya simpan.
Lekensteyn
1
Anda harus mengedit pertanyaan Anda untuk memasukkan bahwa Anda tidak memiliki akses root pada host Anda.
Uriel