Descomprimir archivos que vuelan a través de una tubería

40

¿Puedo hacer que descomprimir o algún programa similar funcione en la salida estándar? La situación es que estoy descargando un archivo zip, que se supone que debe descomprimirse sobre la marcha.

Problema relacionado: ¿Cómo canalizo un archivo descargado a la salida estándar en bash?

Alex
fuente
Parecía que debería ser factible, pero parece que solo es posible extraer un archivo zip y canalizar el archivo a otro comando si el archivo zip contiene solo un archivo. Quería extraer un archivo específico de un archivo comprimido de varios archivos. En lugar de canalizar, cambié a encadenar múltiples comandos 'descomprimir archivo.zip / ruta / archivo && dostuff / ruta / archivo && rm -rf / ruta' Mientras no respondía la pregunta original y resultaba en la creación de archivos temporales, satisfizo mi necesitar.
Stan Kurdziel
Mira pigz. Lo usamos en una tubería. andrew.tumblr.com/post/2316602611
dmourati

Respuestas:

22

Si bien un archivo zip es, de hecho, un formato contenedor, no hay ninguna razón por la que no se pueda leer desde una tubería (stdin) si el archivo puede caber en la memoria con la suficiente facilidad. Aquí hay un script de Python que toma un archivo zip como entrada estándar y extrae el contenido al directorio actual o a un directorio específico, si se especifica.

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)

Este script se puede minimizar a una línea y crear como un 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 '.')\""

Ahora descomprima la salida de wget fácilmente.

wget http://your.domain.com/your/file.zip -O - | unzip-stdin target_dir
Jason R. Coombs
fuente
1
¡Tú y Python Rock!
Farid Nouri Neshat
44
Una línea agradable y +1 por mencionar que el archivo tiene que caber en la memoria. (Desafortunadamente no hay forma de descomprimir un archivo pkzip debido a la estructura del formato del archivo).
lxgr
2
tenga en cuenta que esto almacena todo en la memoria antes de extraerlo
William Casarin
1
no hay razón por la que no pueda leerse como una secuencia si el archivo puede caber en la memoria con la suficiente facilidad no es realmente preciso. La razón por la que se ve obligado a almacenar todo el archivo zip en la memoria antes de extraer el contenido es específicamente porque no puede leerse como una secuencia. Por supuesto, aún puede ser útil evitar escribir el archivo zip en un archivo.
Håkan Lindqvist
Esto no es una transmisión, estás leyendo todo el archivo en la memoria usando el .read()método
Romuald Brunet
18

Es poco probable que esto funcione como espera. Zip no es solo un formato de compresión, sino también un formato contenedor. Enrolla los trabajos de tar y gzip.bzip2 en uno. Dicho esto, si su archivo zip tiene un solo archivo, puede usar descomprimir -p para extraer los archivos a stdout. Si tiene más de un archivo, no hay forma de que sepa dónde comienzan y dónde se detienen.

En cuanto a la lectura de stdin, la página de manual de descompresión tiene esta oración:

Los archivos leídos desde la entrada estándar aún no son compatibles, excepto con funzip (y luego solo se puede extraer el primer miembro del archivo).

Puede que tengas suerte con funzip.

David Pashley
fuente
Si zip tiene varios archivos dentro, entonces -p puede imprimir un solo archivo usando el nombre del archivo como parámetro: descomprimir -p temp.zip file-inside-zip
Taavi Ilves
7

Lo que desea hacer es unziptomar un archivo ZIP en su entrada estándar en lugar de como argumento. Esto se hace generalmente fácil por gzipy tartipo de herramientas con un -argumento. Pero el estándar unzipno hace eso (sin embargo, admite la extracción a una tubería). Sin embargo, no todo está perdido...

Mira la página del manual de funzip .

funzip sin un argumento de archivo actúa como un filtro; es decir, supone que se está canalizando un archivo ZIP (o un archivo gzip'd) a la entrada estándar, y extrae el primer miembro del archivo a stdout. Cuando stdin proviene de un dispositivo tty, funzip supone que esto no puede ser una secuencia de datos comprimidos (binarios) y, en cambio, muestra un breve texto de ayuda. Si hay un argumento de archivo, la entrada se lee desde el archivo especificado en lugar de desde stdin.

Dada la limitación en la extracción de un solo miembro, funzip es más útil junto con un programa de archivador secundario como tar (1). La siguiente sección incluye un ejemplo que ilustra este uso en el caso de copias de seguridad de disco en cinta.

Esto va bien con la idea de que la mayoría de los archivos de Linux suelen estar TAR'ed y luego comprimidos de alguna manera (gzip, bzip, et al). Esto funcionará para usted si tiene un tar.ZIP.


Vale la pena señalar que funzipestá escrito por el autor original de Info-ZIP, Mark Adler. Él escribe en la página de manual de funzip,

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

sin embargo, no se ve tal actualización alrededor. Sospecho que Mark lo consideró innecesario ya que otros métodos de archivo funcionaron fácilmente con TAR.

nik
fuente
Solo un comentario; Algunas personas desean Python o cualquier idioma como opción para descomprimir. Un buen ejemplo es Heroku, que no incluye tar ni descomprimir en su sistema. Una solución es usar jar instalando Java, que está permitido.
Nick
En esta respuesta, encontrará más información sobre las limitaciones de funzip y herramientas similares (en particular, solo es capaz de mostrar al primer miembro de un archivo): unix.stackexchange.com/a/211286/77539
Joshua Goldberg
6

Me gusta usar curl porque está instalado de forma predeterminada ( -Les necesario para los redireccionamientos que a menudo ocurren):

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

Sin embargo, bsdtarno está instalado de manera predeterminada y no pude ponerme funzipa trabajar.

Perdiz de Todd
fuente
También funciona bien con varios archivos
jonnor
5

Esta es una nueva publicación de mi respuesta a una pregunta similar:

El formato del archivo ZIP incluye un directorio (índice) al final del archivo. Este directorio dice dónde, dentro del archivo, se encuentra cada archivo y, por lo tanto, permite un acceso rápido y aleatorio, sin leer todo el archivo.

Esto parecería plantear un problema al intentar leer un archivo ZIP a través de una tubería, ya que no se accede al índice hasta el final y, por lo tanto, los miembros individuales no se pueden extraer correctamente hasta que el archivo se haya leído por completo y ya no esté disponible . Como tal, no parece sorprendente que la mayoría de los descompresores ZIP simplemente fallen cuando el archivo se suministra a través de una tubería.

El directorio al final del archivo no es la única ubicación donde se almacena la información meta del archivo en el archivo. Además, las entradas individuales también incluyen esta información en un encabezado de archivo local, con fines de redundancia.

Aunque no todos los descompresores ZIP usarán encabezados de archivos locales cuando el índice no esté disponible, los front-end tar y cpio para libarchive (también conocido como bsdtar y bsdcpio) pueden y lo harán al leer a través de una tubería, lo que significa que lo siguiente es posible:

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

No es posible con Info-Zip, que es la implementación de OSS más común. Sin embargo, lo más importante es que no se recomienda debido a las construcciones de los archivos ZIP.

Si un cambio de formato es viable para usted, considere usar tar (1) en su lugar. Está bastante contento con la entrada / salida transmitida y, de hecho, lo espera de forma predeterminada.

Además, a menudo puede saber si las aplicaciones esperan entrada / salida transmitida especificando "-" para un nombre de archivo. Info-Zip, como puedes imaginar, no trata esto como un argumento válido.

Dan Carley
fuente
4

En zsh, puede hacer lo siguiente:

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

La utilidad común más simple disponible que hará esto es jar, lo que supondrá que se está utilizando STDIN si no le pasa ningún argumento de archivo. También toma argumentos similares al tarprograma para las operaciones.

por ejemplo, enumerar el contenido de un archivo

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

Si bien Java no siempre está instalado, en esas máquinas donde está, jares definitivamente el método más conveniente para hacerlo.

Adrian
fuente
3

Publicación de mi respuesta :

BusyBox unzippuede tomar stdin y extraer todos los archivos.

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

El guión después unzipes usar stdin como entrada.

Usted puede incluso,

cat file.zip | busybox unzip -

Pero eso es redundante unzip file.zip.

Si su distribución usa BusyBox de forma predeterminada (por ejemplo, Alpine), simplemente ejecute unzip -.

Saftever
fuente
1

De hecho, necesitaba algo un poco más complejo: extraer un archivo específico si existe. La dificultad es que la secuencia del archivo de entrada puede no ser un archivo zip, y en ese caso, lo necesitaba para continuar a través de la tubería. Aquí está mi solución (gracias principalmente a la solución 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)"

Lo guardé como un archivo llamado "effpoptp" (no un nombre simple) en la carpeta "/ bin" en mi máquina, por lo que probarlo es así:

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

El propósito es controlar la versión de los archivos de MySQL Workbench, donde el archivo podría ser el archivo xml nombrado como el archivo de workbench o el archivo completo de workbench.

SEoF
fuente