Comment puis-je filtrer le contenu d'un fichier tar, produisant un autre fichier tar dans le tuyau?

13

Considérez un seul fichier tar d'un système externe qui contient certains répertoires avec divers attributs que je souhaite conserver tels que des autorisations, des mtimes, etc. Comment puis-je facilement prendre un sous-ensemble de ces fichiers en tant qu'utilisateur normal (pas root)?

Vous cherchez quelque chose comme:

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

Il est également essentiel que les principaux attributs (propriété, groupe, mode, mtime) de cette archive tar soient conservés. Qu'en est-il des autres attributs d'un fichier tar tels que les mots-clés d'en-tête étendus ?

Points bonus pour une solution qui évite l'utilisation d'un répertoire temporaire au cas où ce sous-répertoire contiendrait des fichiers volumineux.

Lekensteyn
la source

Réponses:

14

bsdtar (basé sur libarchive) peut filtrer tar (et quelques autres archives) de stdin à stdout. Il peut par exemple passer uniquement par les noms de fichiers correspondant à un modèle et peut s/old/new/renommer. Il est déjà conditionné pour la plupart des distributions, par exemple comme bsdtardans 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.

Notez qu'il dispose d'un large choix de formats de compression pour les entrées / sorties, vous n'avez donc pas à diriger manuellement par gunzip / lz4 vous-même. Vous pouvez utiliser -pour stdin avec la @tarfilesyntaxe, et / ou -pour stdout comme d'habitude.


Ma recherche a également trouvé cet outil de modification de tar en streaming qui semble vouloir que vous définissiez les modifications d'archive que vous souhaitez en utilisant javascript. (Je pense que le tout est écrit en js).

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

Peter Cordes
la source
1
Excellent, je ne savais pas que cette @original.tarapproche était possible avec bsdtar. Semble également fonctionner avec des attributs étendus et la compression </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(et pour une raison quelconque, une sélection vide produit une série de zéro octet, mais ce n'est pas un problème majeur pour moi).
Lekensteyn
1
Selon mes tests, s/old/new/ ne fonctionne pas sur les fichiers provenant d'anciennes archives utilisant @ old.tgz, il ne fonctionne que sur des fichiers réels, en archivant directement depuis le système de fichiers. C'est vraiment dommage, car ce serait le cas d'utilisation le plus utile pour moi.
bart
4

Le moyen le plus simple serait de copier toute l'archive; Je suppose que vous ne voulez pas faire ça parce que c'est trop grand.

Les outils de ligne de commande habituels ( tar,pax ) ne prennent pas en charge la copie des membres d'une archive vers une autre archive.

Si vous n'avez pas besoin de conserver la propriété, je vous suggère d'utiliser des systèmes de fichiers FUSE . Vous pouvez utiliser archivemount pour monter une archive en tant que système de fichiers; faites-le pour l'archive source et exécutez tar sur le système de fichiers monté.

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

Alternativement, vous pouvez utiliser AVFS :

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

Alternativement, vous pouvez exécuter tarsur l'archive d'origine et extraire sur la machine distante via SSHFS .

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

Cependant, toutes ces méthodes sont lourdes si vous devez conserver la propriété. Ils impliquent tous d'extraire dans un fichier sur la machine locale, donc la propriété de ce fichier devra être celle prévue propriété distante . Cela nécessite d'être exécuté en tant que root et peut ne pas donner le résultat souhaité si les fichiers appartiennent à des comptes dont les noms ou les ID diffèrent entre la machine locale et l'hôte distant.

La tarfilebibliothèque de Python fournit un moyen assez simple de manipuler les membres tar, vous pouvez donc les mélanger d'un fichier tar à un autre. Il prend en charge les formats standard POSIX (ustar, pax) ainsi que certaines extensions GNU. Voici un script Python non testé qui lit un fichier tar (éventuellement compressé avec gzip ou bzip2) sur son entrée standard et écrit un fichier tar compressé avec bzip2 sur sa sortie standard. Les membres de la source sont copiés s'ils commencent avec l'argument passé au 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()

À invoquer en tant que

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj
Gilles 'SO- arrête d'être méchant'
la source
1
bsdtar (basé sur libarchive) peut filtrer les archives tar à la volée, voir ma réponse.
Peter Cordes
La tâche consistait à extraire les données d'une image de micrologiciel, de sorte que la propriété / l'appartenance au groupe sont en effet importantes. L'approche python pourrait cependant fonctionner.
Lekensteyn
0

Une autre approche sans privilège consiste à utiliser le fakerootprogramme pour prétendre que vous êtes autorisé à changer de propriétaire. Bien que d'autres attributs tar soient perdus, il conserve le mode, mtime et uid / gid. Ces commandes créent un répertoire temporaire, extraient un sous-ensemble des fichiers et créent enfin une nouvelle archive:

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
la source
0

GNU tara une --deleteoption:

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

De cette façon, vous pouvez obtenir un sous-ensemble du tar d'entrée en spécifiant ce qui ne doit pas être inclus dans la sortie.

Malheureusement, je ne pouvais pas obtenir l' --excludeoption de travailler avec --delete, il semble donc que vous devez d'abord obtenir une liste explicite ( -t) des choses à supprimer, puis la transmettre à une autre invocation de tar.

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

Ou vous pouvez stocker la liste dans un fichier externe s'il est trop long ou complexe:

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

D'après ce que je sais, la tarcommande ne peut pas utiliser le format tar à la fois comme entrée et sortie. Vous devrez extraire vos fichiers localement d'une manière ou d'une autre et utiliser à nouveau tar pour créer un fichier tar à la volée, avec quelque chose comme ça (le -moyen d'entrée / sortie standard est utilisé à la place d'un fichier):

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

Notez tarqu'être capable d'extraire un fichier tar directement dans un autre fichier tar est une idée intéressante ...

Uriel
la source
Sans root, cela perdra toutes les informations de propriété / groupe que je souhaite explicitement conserver.
Lekensteyn
1
Vous devez modifier votre question pour inclure que vous ne disposez pas d'un accès root sur votre hôte.
Uriel