web-dev-qa-db-fra.com

Décompression des fichiers qui volent dans un tuyau

Puis-je faire décompresser ou tout programme similaire fonctionne sur la sortie standard? La situation est que je télécharge un fichier Zip, qui est censé être décompressé à la volée.

Problème connexe: Comment diriger un fichier téléchargé vers la sortie standard dans bash?

43
Alex

Alors qu'un fichier Zip est en fait un format de conteneur, il n'y a aucune raison pour qu'il ne puisse pas être lu à partir d'un canal (stdin) si le fichier peut tenir assez facilement en mémoire. Voici un script Python qui prend un fichier Zip comme entrée standard et extrait le contenu dans le répertoire courant ou dans un répertoire spécifié s'il est spécifié.

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)

Ce script peut être réduit à une ligne et créé en tant qu'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 '.')\""

Décompressez maintenant facilement la sortie de wget.

wget http://your.domain.com/your/file.Zip -O - | unzip-stdin target_dir
22
Jason R. Coombs

Il est peu probable que cela fonctionne comme prévu. Zip n'est pas seulement un format de compression, mais aussi un format de conteneur. Il regroupe les tâches de tar et gzip.bzip2 en une seule. Cela dit, si votre Zip a un seul fichier, vous pouvez utiliser unzip -p pour extraire les fichiers vers stdout. Si vous avez plusieurs fichiers, vous ne pouvez pas savoir où ils commencent et s'arrêtent.

En ce qui concerne la lecture depuis stdin, la page de manuel de décompression contient cette phrase:

Les archives lues à partir de l'entrée standard ne sont pas encore prises en charge, sauf avec funzip (et alors seul le premier membre de l'archive peut être extrait).

Vous pourriez avoir de la chance avec funzip.

18
David Pashley

Ce que vous voulez faire, c'est que unzip prenne un fichier ZIPpé sur son entrée standard plutôt que comme argument. Ceci est généralement facilement pris en charge par les types d'outils gzip et tar avec un - argument. Mais la norme unzip ne fait pas cela (cependant, elle prend en charge l'extraction vers un tuyau). Cependant, tout n'est pas perdu...

Regardez funzip page de manuel.

funzip sans argument de fichier agit comme un filtre; c'est-à-dire qu'il suppose qu'une archive Zip (ou un fichier gzip'd) est acheminée vers l'entrée standard, et il extrait le premier membre de l'archive vers stdout. Lorsque stdin provient d'un périphérique tty, funzip suppose qu'il ne peut pas s'agir d'un flux de données compressées (binaires) et affiche un court texte d'aide à la place. S'il y a un argument de fichier, l'entrée est lue à partir du fichier spécifié au lieu de stdin.

Étant donné la limitation de l'extraction d'un seul membre, funzip est très utile en conjonction avec un programme d'archivage secondaire tel que tar (1). La section suivante comprend un exemple illustrant cette utilisation dans le cas de sauvegardes sur disque sur bande.

Cela va bien avec l'idée que la plupart des archives Linux sont généralement tarées puis ZIPpées d'une manière ou d'une autre (gzip, bzip, et al). Cela fonctionnera pour vous si vous avez un tar.Zip.


Il convient de noter que funzip est écrit par l'auteur original d'Info-Zip, Mark Adler. Il écrit dans la page de manuel funzip,

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

cependant, aucune mise à jour de ce type n'est visible. Je soupçonne que Mark l'a trouvé inutile car d'autres méthodes d'archivage fonctionnaient facilement avec TAR.

7
nik

J'aime utiliser curl car il est installé par défaut (le -L est nécessaire pour les redirections qui se produisent souvent):

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

Cependant, bsdtar n'est pas installé par défaut et je n'ai pas pu faire fonctionner funzip.

7
Todd Partridge

Ceci est une rediffusion de ma réponse à une question similaire:

Le format de fichier Zip comprend un répertoire (index) à la fin de l'archive. Ce répertoire indique où, dans l'archive, chaque fichier est situé et permet ainsi un accès rapide et aléatoire, sans lire l'archive entière.

Cela semblerait poser un problème lors de la tentative de lecture d'une archive Zip via un canal, dans la mesure où l'index n'est accessible qu'à la toute fin et que les membres individuels ne peuvent être extraits correctement qu'après que le fichier a été entièrement lu et n'est plus disponible . En tant que tel, il ne semble pas surprenant que la plupart des décompresseurs Zip échouent simplement lorsque l'archive est fournie via un canal.

Le répertoire à la fin de l'archive n'est pas seulement l'emplacement où les méta-informations du fichier sont stockées dans l'archive. De plus, les entrées individuelles incluent également ces informations dans un en-tête de fichier local, à des fins de redondance.

Bien que tous les décompresseurs Zip n'utilisent pas les en-têtes de fichiers locaux lorsque l'index n'est pas disponible, les frontaux tar et cpio de libarchive (aka bsdtar et bsdcpio) peuvent et vont le faire lors de la lecture d'un tube, ce qui signifie que ce qui suit est possible:

wget -qO- http://example.org/file.Zip | bsdtar -xvf-
5
ruario

Dans zsh, vous pouvez effectuer les opérations suivantes:

unzip =( curl http://example.com/someZipFile.Zip )
4
Ian Robertson

L'utilitaire commun le plus simple disponible qui le fera est jar, qui supposera que STDIN est utilisé si vous ne lui passez aucun argument de fichier. Il prend également des arguments similaires au programme tar pour les opérations.

par exemple. lister le contenu d'une archive

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

Alors que Java n'est pas toujours installé, sur les machines où il se trouve, jar est certainement la méthode la plus pratique pour ce faire.

4
Adrian

Ce n'est pas possible avec Info-Zip qui est l'implémentation OSS la plus courante. Plus important encore, ce n'est pas recommandé en raison des constructions d'archives Zip.

Si un changement de format est viable pour vous, envisagez d'utiliser tar (1) à la place. Il est assez satisfait de l'entrée/sortie en streaming et, en fait, l'attend par défaut.

De plus, vous pouvez souvent dire si les applications attendent une entrée/sortie en streaming en spécifiant "-" pour un nom de fichier. Info-Zip, comme vous pouvez l'imaginer, ne considère pas cela comme un argument valide.

4
Dan Carley

Republication de ma réponse :

unzip de BusyBox peut prendre stdin et extraire tous les fichiers.

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

Le tiret après unzip est d'utiliser stdin comme entrée.

Vous pouvez même,

cat file.Zip | busybox unzip -

Mais c'est tout simplement redondant de unzip file.Zip.

Si votre distribution utilise BusyBox par défaut (par exemple Alpine), exécutez simplement unzip -.

3
Saftever

En fait, j'avais besoin de quelque chose d'un peu plus complexe - extraire un fichier spécifique s'il existe. La difficulté est que le flux du fichier d'entrée peut ne pas être un fichier Zip, et dans ce cas, j'en avais besoin pour continuer à travers le tuyau. Voici ma solution (merci surtout à la solution 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)"

J'ai enregistré cela sous la forme d'un fichier nommé "effpoptp" (pas un simple nom) dans le dossier "/ bin" sur ma machine, donc le tester est comme ceci:

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

Le but est de contrôler les versions des fichiers MySQL Workbench, où le fichier peut être le fichier xml nommé fichier Workbench ou le fichier Workbench complet.

1
SEoF