¿Cómo puedo filtrar el contenido de un archivo tar, produciendo otro archivo tar en la tubería?

13

Considere un único archivo tar de un sistema externo que contiene algunos directorios con varios atributos que quiero conservar, como permisos, mtimes, etc. ¿Cómo puedo tomar fácilmente un subconjunto de estos archivos como usuario normal (no root)?

Buscando algo como:

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

También es esencial que se conserven los atributos principales (propiedad, grupo, modo, mtime) en este archivo tar. ¿Qué pasa con otros atributos en un archivo tar, como palabras clave de encabezado extendido ?

Puntos de bonificación para una solución que evita el uso de un directorio temporal en caso de que este subdirectorio contenga archivos enormes.

Lekensteyn
fuente

Respuestas:

14

bsdtar (basado en libarchive) puede filtrar tar (y algunos otros archivos) de stdin a stdout. Puede, por ejemplo, pasar solo por nombres de archivos que coincidan con un patrón, y puede s/old/new/cambiar el nombre. Ya está empaquetado para la mayoría de las distribuciones, por ejemplo, como bsdtaren 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.

Tenga en cuenta que tiene una amplia variedad de formatos de compresión para entrada / salida, por lo que no tiene que canalizar manualmente gunzip / lz4. Puede usar -para stdin con la @tarfilesintaxis, y / o -para stdout como normal.


Mi búsqueda también encontró esta herramienta de modificación de tar de transmisión que parece querer que usted defina los cambios de archivo que desea usando JavaScript. (Creo que todo está escrito en js).

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

Peter Cordes
fuente
1
Excelente, no sabía que este @original.tarenfoque era posible con bsdtar. Parece funcionar también con atributos extendidos y compresión </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(y, por alguna razón, una selección vacía produce una serie de cero bytes, pero eso no es un problema importante para mí).
Lekensteyn
1
Según mis pruebas, s/old/new/ no funciona en archivos que provienen de archivos antiguos usando @ old.tgz, solo funciona en archivos reales, archivando directamente desde el sistema de archivos. Realmente es una pena, ya que sería el caso de uso más útil para mí.
Bart
4

La forma más fácil sería copiar todo el archivo; Supongo que no quieres hacer eso porque es demasiado grande.

Las herramientas de línea de comandos habituales ( tar, pax) no admiten copiar miembros de un archivo a otro archivo.

Si no necesita preservar la propiedad, le sugiero que use los sistemas de archivos FUSE . Puede usar archivemount para montar un archivo como sistema de archivos; haga esto para el archivo fuente y ejecute tar en el sistema de archivos montado.

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

Alternativamente, puede usar AVFS :

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

Alternativamente, puede ejecutar tarel archivo original y extraerlo a la máquina remota a través de SSHFS .

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

Sin embargo, todos estos métodos son engorrosos si necesita preservar la propiedad. Todos implican extraer a un archivo en la máquina local, por lo que la propiedad de este archivo tendrá que ser la propiedad remota prevista . Esto requiere ejecutarse como root y puede no dar el resultado deseado si los archivos son propiedad de cuentas que tienen nombres o ID que difieren entre la máquina local y el host remoto.

La tarfilebiblioteca de Python proporciona una forma bastante fácil de manipular miembros tar, por lo que puede barajarlos de un archivo tar a otro. Admite formatos estándar POSIX (ustar, pax), así como algunas extensiones GNU. Aquí hay un script Python no probado que lee un archivo tar (posiblemente comprimido con gzip o bzip2) en su entrada estándar y escribe un archivo tar comprimido con bzip2 en su salida estándar. Los miembros de la fuente se copian si comienzan con el argumento pasado al 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()

Para ser invocado como

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj
Gilles 'SO- deja de ser malvado'
fuente
1
bsdtar (basado en libarchive) puede filtrar archivos tar sobre la marcha, vea mi respuesta.
Peter Cordes
La tarea consistía en extraer datos de una imagen de firmware, por lo que la propiedad / pertenencia al grupo es realmente importante. Sin embargo, el enfoque de Python podría funcionar.
Lekensteyn
0

Un enfoque alternativo sin privilegios es usar el fakerootprograma para fingir que se le permite cambiar la propiedad. Mientras que otros atributos tar se pierden, mantiene el modo, mtime y uid / gid. Estos comandos crean un directorio temporal, extraen un subconjunto de los archivos y finalmente crean un nuevo archivo:

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
fuente
0

GNU tartiene una --deleteopción:

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

De esta manera, puede obtener un subconjunto del tar de entrada especificando qué no incluir en la salida.

Desafortunadamente, no pude obtener la --excludeopción para trabajar --delete, por lo que parece que primero necesita obtener una lista explícita ( -t) de cosas para eliminar y luego pasarla a otra invocación tar.

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

O puede almacenar la lista en un archivo externo si es demasiado larga o compleja:

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

Por lo que sé, el tarcomando no puede usar el formato tar tanto como entrada como salida. Tendrá que extraer sus archivos localmente de alguna manera, y usar tar nuevamente para crear un archivo tar-on-the-fly, con algo como esto (el -medio de entrada / salida estándar se usa en lugar de un archivo):

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

Tenga en cuenta que tarpoder extraer un archivo tar directamente en otro archivo tar es una idea interesante ...

Uriel
fuente
Sin root, perderá toda la información de propiedad / grupo que quiero conservar explícitamente.
Lekensteyn
1
Debe editar su pregunta para incluir que no tiene acceso de root en su host.
Uriel