Como posso filtrar o conteúdo de um arquivo tar, produzindo outro arquivo tar no pipe?

13

Considere um único arquivo tar de um sistema externo que contém alguns diretórios com vários atributos que eu quero manter, como permissões, mtimes etc. Como posso facilmente pegar um subconjunto desses arquivos como um usuário comum (não root)?

Procurando algo como:

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

Também é essencial que os principais atributos (propriedade, grupo, modo, mtime) nesse arquivo tar sejam mantidos. E quanto a outros atributos em um arquivo tar, como palavras-chave de cabeçalho estendidas ?

Pontos de bônus para uma solução que evita o uso de um diretório temporário, caso esse subdiretório contenha arquivos enormes.

Lekensteyn
fonte

Respostas:

14

O bsdtar (baseado em libarchive) pode filtrar o tar (e alguns outros arquivos) de stdin para stdout. Por exemplo, ele pode passar apenas por nomes de arquivos que correspondem a um padrão e pode s/old/new/renomear. Já está empacotado para a maioria das distribuições, por exemplo, como bsdtarno 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.

Observe que há uma grande variedade de formatos de compressão para entrada / saída, para que você não precise passar manualmente pelo gunzip / lz4. Você pode usar o -stdin com a @tarfilesintaxe e / ou o -stdout normalmente.


Minha pesquisa também encontrou esta ferramenta de modificação de alcatrão de streaming que parece desejar definir as alterações de arquivo que você deseja usando javascript. (Eu acho que a coisa toda está escrita em js).

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

Peter Cordes
fonte
1
Excelente, não sabia que essa @original.tarabordagem era possível com o bsdtar. Parece funcionar também com atributos estendidos e compactação </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(e por algum motivo uma seleção vazia produz uma série de zero bytes, mas isso não é um grande problema para mim).
Lekensteyn
1
De acordo com meus testes, s/old/new/ não funciona em arquivos provenientes de arquivos antigos usando @ old.tgz, funciona apenas em arquivos reais, arquivando diretamente no sistema de arquivos. É realmente uma pena, pois seria o caso de uso mais útil para mim.
Bart
4

A maneira mais fácil seria copiar todo o arquivo; Presumo que você não queira fazer isso porque é muito grande.

As ferramentas de linha de comando habituais ( tar, pax) não suportam a cópia de membros de um arquivo para outro arquivo.

Se você não precisou preservar a propriedade, sugiro o uso de sistemas de arquivos FUSE . Você pode usar o archivemount para montar um archive como um sistema de arquivos; faça isso no arquivo de origem e execute tar no sistema de arquivos montado.

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

Como alternativa, você pode usar o AVFS :

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

Como alternativa, você pode executar tarno arquivo original e extrair para a máquina remota pelo SSHFS .

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

No entanto, todos esses métodos são complicados se você precisar preservar a propriedade. Todos eles envolvem a extração para um arquivo na máquina local; portanto, a propriedade desse arquivo deverá ser a propriedade remota pretendida . Isso requer a execução como raiz e pode não fornecer o resultado pretendido se os arquivos pertencerem a contas com nomes ou IDs que diferem entre a máquina local e o host remoto.

A tarfilebiblioteca do Python fornece uma maneira bastante fácil de manipular membros tar, para que você possa embaralhá-los de um arquivo tar para outro. Ele suporta os formatos padrão POSIX (ustar, pax), bem como algumas extensões GNU. Aqui está um script Python não testado que lê um arquivo tar (possivelmente compactado com gzip ou bzip2) em sua entrada padrão e grava um arquivo tar compactado com bzip2 em sua saída padrão. Os membros da fonte são copiados se começarem com o argumento passado para o 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- parar de ser mau'
fonte
1
O bsdtar (baseado em libarchive) pode filtrar arquivos tar em tempo real, veja minha resposta.
22615 Peter Cordes
A tarefa era extrair dados de uma imagem de firmware, para que a propriedade / associação ao grupo sejam realmente importantes. A abordagem python pode funcionar.
Lekensteyn
0

Uma abordagem alternativa sem privilégios é usar o fakerootprograma para fingir que você tem permissão para alterar a propriedade. Enquanto outros atributos tar são perdidos, ele mantém o modo, mtime e uid / gid. Esses comandos criam um diretório temporário, extraem um subconjunto dos arquivos e finalmente criam um novo arquivo morto:

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

O GNU tartem uma --deleteopção:

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

Dessa forma, você pode obter um subconjunto do tar de entrada especificando o que não incluir na saída.

Infelizmente, não consegui --excludetrabalhar com a opção --delete, então parece que você precisa primeiro obter uma lista explícita ( -t) de itens a serem excluídos e depois passá-la para outra chamada de tar.

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

Ou você pode armazenar a lista em um arquivo externo, se for muito longo ou complexo:

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

Pelo que eu sei, o tarcomando não pode usar o formato tar tanto como entrada e saída. Você precisará extrair seus arquivos localmente de alguma forma e usar o tar novamente para criar um arquivo tar on-the-fly, com algo parecido com isto (o -meio de entrada / saída padrão é usado em vez de um arquivo):

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

Observe que ter tara capacidade de extrair um arquivo tarfile diretamente em outro arquivo tarfile é uma ideia interessante ...

Uriel
fonte
Sem raiz, isso perderá todas as informações de propriedade / grupo que eu quero manter explicitamente.
Lekensteyn
1
Você deve editar sua pergunta para incluir que você não tem acesso root no seu host.
Uriel