Descompactando arquivos que estão voando através de um tubo

39

Posso fazer com que o descompacte ou que programas semelhantes funcionem na saída padrão? A situação é que estou baixando um arquivo zip, que deve ser descompactado em tempo real.

Problema relacionado: Como canalizar um arquivo baixado para a saída padrão no bash?

Alex
fonte
Parecia que deveria ser possível, mas parece que é possível extrair um zip e canalizar o arquivo para outro comando se o zip contiver apenas um único arquivo. Eu queria extrair um arquivo específico de um zip com vários arquivos. Em vez de canalizar, mudei para encadear vários comandos 'descompacte o arquivo.zip / caminho / arquivo && dostuff / caminho / arquivo && rm -rf / caminho'. Embora não respondesse à pergunta original e resultasse na criação de arquivos temporários, ele satisfez meus precisar.
22813 Stan Standdiel
Confira pigz. Nós o usamos em um cano. andrew.tumblr.com/post/2316602611
dmourati

Respostas:

22

Embora um arquivo zip seja de fato um formato de contêiner, não há razão para que ele não possa ser lido em um canal (stdin) se o arquivo puder caber na memória com bastante facilidade. Aqui está um script Python que utiliza um arquivo zip como entrada padrão e extrai o conteúdo para o diretório atual ou para um diretório especificado, se especificado.

import zipfile
import sys
import StringIO
data = StringIO.StringIO(sys.stdin.read())
z = zipfile.ZipFile(data)
dest = sys.argv[1] if len(sys.argv) == 2 else '.'
z.extractall(dest)

Esse script pode ser minificado para uma linha e criado como um alias.

alias unzip-stdin="python -c \"import zipfile,sys,StringIO;zipfile.ZipFile(StringIO.StringIO(sys.stdin.read())).extractall(sys.argv[1] if len(sys.argv) == 2 else '.')\""

Agora descompacte a saída do wget facilmente.

wget http://your.domain.com/your/file.zip -O - | unzip-stdin target_dir
Jason R. Coombs
fonte
11
Você e o python rock !!!
Farid Nouri Neshat
3
Agradável one-liner e +1 por mencionar que o arquivo deve caber na memória. (Infelizmente, não há como descompactar um arquivo pkzip devido à estrutura do formato do arquivo).
Lxgr
2
manter em mente este buffers tudo na memória antes de extrair
William Casarin
11
não há razão para que ele não possa ser lido como um fluxo se o arquivo caber na memória com facilidade o suficiente não for realmente preciso. O motivo pelo qual você é forçado a armazenar em buffer todo o arquivo zip na memória antes de extrair o conteúdo é especificamente porque ele não pode ser lido como um fluxo. Obviamente, ainda pode ser útil evitar gravar o arquivo zip em um arquivo.
Håkan Lindqvist
Isto é não um fluxo, você está lendo o arquivo inteiro na memória, usando o .read()método
Romuald Brunet
17

É improvável que funcione como você espera. Zip não é apenas um formato de compactação, mas também um formato de contêiner. Ele acumula os trabalhos do tar e do gzip.bzip2 em um. Dito isto, se o seu zip tiver um único arquivo, você pode usar o descompacte -p para extrair os arquivos para o stdout. Se você tiver mais de um arquivo, não há como saber onde eles começam e param.

Quanto à leitura de stdin, a página de manual descompactar tem esta frase:

Os arquivos lidos a partir da entrada padrão ainda não são suportados, exceto com o funzip (e somente o primeiro membro do arquivo pode ser extraído).

Você pode ter alguma sorte com o funzip.

David Pashley
fonte
Se zip tem vários arquivos dentro, então -p pode imprimir um único arquivo usando o nome de arquivo como um parâmetro: unzip -p temp.zip file-dentro-zip
Taavi Ilves
7

O que você quer fazer é unzippegar um arquivo ZIP em sua entrada padrão e não como argumento. Isso geralmente é facilmente suportado por gzipe tartipo de ferramentas com um -argumento. Mas o padrão unzipnão faz isso (no entanto, ele suporta extração para um tubo). No entanto, nem tudo está perdido...

Veja a página de manual do funzip .

o funzip sem um argumento de arquivo atua como um filtro; isto é, assume que um arquivo ZIP (ou um arquivo com gzip) está sendo canalizado para a entrada padrão e extrai o primeiro membro do arquivo para stdout. Quando o stdin vem de um dispositivo tty, o funzip assume que este não pode ser um fluxo de dados compactados (binários) e mostra um texto de ajuda breve. Se houver um argumento de arquivo, a entrada será lida no arquivo especificado em vez de em stdin.

Dada a limitação na extração de um único membro, o funzip é mais útil em conjunto com um programa secundário de arquivamento, como o tar (1). A seção a seguir inclui um exemplo que ilustra esse uso no caso de backups de disco em fita.

Isso vai bem com a idéia de que a maioria dos arquivos linux são geralmente TAR'ed e depois zipados de alguma forma (gzip, bzip, et al). Isso funcionará para você se você tiver um tar.ZIP.


Vale ressaltar que funzipfoi escrito pelo autor original do Info-ZIP Mark Adler. Ele escreve na página de manual do funzip,

this functionality should be incorporated into unzip itself (future release).

no entanto, nenhuma atualização é vista por aí. Suspeito que Mark tenha achado desnecessário, pois outros métodos de arquivamento funcionaram facilmente com o TAR.

nik
fonte
Apenas um comentário; algumas pessoas gostariam de python ou qualquer idioma como uma opção para descompactar. Um excelente exemplo é o Heroku, que não inclui tar ou descompactar em seu sistema. Uma solução alternativa é usar jar instalando o Java que é permitido.
Nick
Há mais sobre como lidar com limitações de funzip e ferramentas semelhantes (em particular só ser capaz de mostrar o primeiro membro de um arquivo) nesta resposta: unix.stackexchange.com/a/211286/77539
Joshua Goldberg
6

Eu gosto de usar o curl porque ele é instalado por padrão (o -Lnecessário para redirecionamentos que geralmente ocorrem):

curl -L http://example.com/file.zip | bsdtar -xvf - -C /path/to/directory/

No entanto, bsdtarnão está instalado por padrão e não consegui funziptrabalhar.

Todd Partridge
fonte
Também funciona bem com vários arquivos
jonnor
5

Esta é uma resposta da minha resposta a uma pergunta semelhante:

O formato do arquivo ZIP inclui um diretório (índice) no final do arquivo morto. Esse diretório diz onde, dentro do arquivo, cada arquivo está localizado e, portanto, permite acesso rápido e aleatório, sem a leitura de todo o arquivo.

Isso parece representar um problema ao tentar ler um arquivo ZIP por meio de um canal, pois o índice não é acessado até o final e, portanto, os membros individuais não podem ser extraídos corretamente até depois que o arquivo foi totalmente lido e não está mais disponível . Como tal, não surpreende que a maioria dos descompressores ZIP simplesmente falhe quando o arquivo é fornecido através de um tubo.

O diretório no final do arquivo morto não é o único local em que as meta informações do arquivo são armazenadas no arquivo morto. Além disso, entradas individuais também incluem essas informações em um cabeçalho de arquivo local, para fins de redundância.

Embora nem todos os descompactadores ZIP usem cabeçalhos de arquivos locais quando o índice estiver indisponível, os front-ends tar e cpio para libarchive (também conhecidos como bsdtar e bsdcpio) podem e o fazem ao ler através de um canal , o que significa que o seguinte é possível:

wget -qO- http://example.org/file.zip | bsdtar -xvf-
ruario
fonte
4

Não é possível com o Info-Zip, que é a implementação OSS mais comum. Mais importante, porém, não é recomendado devido às construções dos arquivos ZIP.

Se uma mudança de formato for viável para você, considere usar tar (1). É bastante feliz com a entrada / saída transmitida e, de fato, espera isso por padrão.

Além disso, muitas vezes você pode dizer se os aplicativos esperam entrada / saída em fluxo contínuo especificando "-" para um nome de arquivo. O Info-Zip, como você pode imaginar, não trata isso como um argumento válido.

Dan Carley
fonte
4

No zsh, você pode fazer o seguinte:

unzip =( curl http://example.com/someZipFile.zip )
Ian Robertson
fonte
3

O utilitário comum mais simples disponível que fará isso é o jarque presumirá que STDIN está sendo usado se você não passar nenhum argumento de arquivo. Também aceita argumentos semelhantes ao tarprograma de operações.

por exemplo, liste o conteúdo de um arquivo

curl https://my.example.com/file.zip | jar t

Embora o Java nem sempre esteja instalado, nas máquinas onde está, jaré definitivamente o método mais conveniente de fazer isso.

Adrian
fonte
3

Repost da minha resposta :

O BusyBox unzippode pegar o stdin e extrair todos os arquivos.

wget -qO- http://downloads.wordpress.org/plugin/akismet.2.5.3.zip | busybox unzip -

O ponto a seguir unzipé usar stdin como entrada.

Você também pode,

cat file.zip | busybox unzip -

Mas isso é apenas redundante unzip file.zip.

Se sua distribuição usar o BusyBox por padrão (por exemplo, Alpine), basta executar unzip -.

Saftever
fonte
1

Na verdade, eu precisava de algo um pouco mais complexo - extraia um arquivo específico, se existir. A dificuldade é que o fluxo do arquivo de entrada pode não ser um arquivo zip e, nesse caso, eu precisava que ele continuasse no canal. Aqui está a minha solução (graças principalmente à solução Jason R. Coombs)

python -c "import zipfile,sys,StringIO
data=sys.stdin.read()
try:
    z=zipfile.ZipFile(StringIO.StringIO(data))
    z.open(\"$1\")
    sys.stdout.write(z.read(\"$1\"))
except (RuntimeError, zipfile.BadZipfile):
    sys.stdout.write(data)"

Salvei isso como um arquivo chamado "effpoptp" (não é um nome simples) na pasta "/ bin" da minha máquina, para testar da seguinte forma:

cat defaultModel.mwb|effpoptp "document.mwb.xml"

O objetivo é controlar os arquivos do MySQL Workbench, onde o arquivo pode ser o arquivo xml nomeado como arquivo do workbench ou o arquivo completo do workbench.

SEoF
fonte