Come posso filtrare il contenuto di un file tar, producendo un altro file tar nella pipe?


13

Prendi in considerazione un singolo file tar da un sistema esterno che contiene alcune directory con vari attributi che voglio conservare come permessi, mtime, ecc. Come posso prendere facilmente un sottoinsieme di questi file come utente normale (non root)?

Alla ricerca di qualcosa come:

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

È anche essenziale mantenere gli attributi principali (proprietà, gruppo, modalità, mtime) in questo archivio tar. Che dire di altri attributi in un file tar come le parole chiave dell'intestazione estesa ?

Punti bonus per una soluzione che evita l'uso di una directory temporanea nel caso in cui questo subdir contenga file enormi.

Risposte:


14

bsdtar (basato su libarchive) può filtrare tar (e alcuni altri archivi) da stdin a stdout. Ad esempio, può passare solo i nomi di file corrispondenti a un modello e può s/old/new/rinominare. È già confezionato per la maggior parte delle distro, ad esempio come bsdtarin 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.

Nota che ha una vasta scelta di formati di compressione per input / output, quindi non devi reindirizzare manualmente gunzip / lz4 da solo. È possibile utilizzare -per stdin con la @tarfilesintassi e / o -per stdout come al solito.


La mia ricerca ha anche trovato questo strumento di modifica dello streaming tar che sembra voler definire le modifiche all'archivio desiderate utilizzando JavaScript. (Penso che tutto sia scritto in js).

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


1
Eccellente, non sapevo che questo @original.tarapproccio fosse possibile con bsdtar. Sembra funzionare anche con attributi estesi e compressione </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(e per qualche ragione una selezione vuota produce una serie di zero byte, ma questo non è un grosso problema per me).
Lekensteyn,

1
Secondo i miei test, s/old/new/ non funziona su file provenienti da vecchi archivi usando @ old.tgz, funziona solo su file reali, archiviando direttamente dal filesystem. È davvero un peccato, poiché sarebbe il caso d'uso più utile per me.
Bart il

4

Il modo più semplice sarebbe copiare l'intero archivio; Presumo che tu non voglia farlo perché è troppo grande.

I soliti strumenti da riga di comando ( tar, pax) non supportano la copia dei membri di un archivio in un altro archivio.

Se non fosse necessario preservare la proprietà, suggerirei di utilizzare i filesystem FUSE . Puoi usare archivemount per montare un archivio come filesystem; fallo per l'archivio sorgente ed esegui tar sul filesystem montato.

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

In alternativa, è possibile utilizzare AVFS :

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

In alternativa, è possibile eseguire tarsull'archivio originale ed estrarre sul computer remoto tramite SSHFS .

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

Tuttavia, tutti questi metodi sono ingombranti se è necessario preservare la proprietà. Tutti implicano l'estrazione in un file sul computer locale, quindi la proprietà di questo file dovrà essere la proprietà remota prevista . Ciò richiede l'esecuzione come root e potrebbe non dare il risultato desiderato se i file sono di proprietà di account che hanno nomi o ID che differiscono tra il computer locale e l'host remoto.

La tarfilelibreria di Python fornisce un modo abbastanza semplice per manipolare i membri tar, in modo da poterli mescolare da un file tar all'altro. Supporta formati standard POSIX (ustar, pax) e alcune estensioni GNU. Ecco uno script Python non testato che legge un file tar (possibilmente compresso con gzip o bzip2) sul suo input standard e scrive un file tar compresso con bzip2 sul suo output standard. I membri dell'origine vengono copiati se iniziano con l'argomento passato allo script.

#!/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()

Da invocare come

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj

1
bsdtar (basato su libarchive) può filtrare gli archivi tar al volo, vedi la mia risposta.
Peter Cordes,

Il compito era quello di estrarre i dati da un'immagine del firmware, quindi l'appartenenza alla proprietà / al gruppo è davvero importante. L'approccio python potrebbe funzionare però.
Lekensteyn,

0

Un approccio alternativo senza privilegi consiste nell'utilizzare il fakerootprogramma per far finta di essere autorizzato a cambiare proprietà. Mentre altri attributi tar vengono persi, mantiene la modalità, mtime e uid / gid. Questi comandi creano una directory temporanea, estraggono un sottoinsieme dei file e infine creano un nuovo archivio:

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

0

GNU tarha --deleteun'opzione:

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

In questo modo, è possibile ottenere un sottoinsieme del tar di input specificando cosa non includere nell'output.

Sfortunatamente non ho avuto la --excludepossibilità di lavorare con l' opzione --delete, quindi sembra che tu debba prima ottenere un elenco esplicito ( -t) di cose da eliminare e poi passarlo a un'altra invocazione di tar.

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

Oppure puoi memorizzare l'elenco in un file esterno se è troppo lungo o complesso:

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

-1

Da quello che so, il tarcomando non può usare il formato tar sia come input che come output. Dovrai estrarre i tuoi file localmente in qualche modo e usare di nuovo tar per creare un file tar al volo, con qualcosa del genere ( -significa che l'input / output standart viene utilizzato al posto di un file):

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

Nota che tarpoter estrarre un file tar direttamente in un altro file tar è un'idea interessante ...


Senza root questo perderà tutte le informazioni di proprietà / gruppo che voglio esplicitamente conservare.
Lekensteyn,

1
È necessario modificare la domanda per includere che non si dispone dell'accesso root sull'host.
Uriel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.