Распаковка файлов, которые летят через трубу

40

Можно ли заставить распаковать или какие-нибудь аналогичные программы работать на стандартном выходе? Ситуация такова, что я загружаю zip-файл, который должен быть распакован на лету.

Проблема, связанная с данной: Как передать загруженный файл на стандартный вывод в bash?

Alex
источник
Казалось, что это должно быть выполнимо, но похоже, что можно извлечь zip-файл и передать файл другой команде, только если zip-файл содержит только один файл. Я хотел извлечь конкретный файл из многофайловой zip. Вместо конвейера я переключился на цепочку из нескольких команд: «разархивировать файл.zip / path / file && dostuff / path / file && rm -rf / path», не отвечая на исходный вопрос и в результате создавая временные файлы, он удовлетворил мои нужно.
Стэн Курджиэль
Проверьте свиней. Мы используем это в трубе. andrew.tumblr.com/post/2316602611
dmourati

Ответы:

22

Хотя zip-файл на самом деле является контейнерным форматом, нет никаких причин, по которым его нельзя прочитать из канала (stdin), если файл достаточно легко помещается в память. Вот скрипт Python, который принимает zip-файл в качестве стандартного ввода и извлекает содержимое в текущий каталог или в указанный каталог, если он указан.

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)

Этот скрипт может быть сведен к одной строке и создан как псевдоним.

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 '.')\""

Теперь легко распакуйте вывод wget.

wget http://your.domain.com/your/file.zip -O - | unzip-stdin target_dir
Джейсон Р. Кумбс
источник
1
Вы и питон рок!
Фарид Нури Нешат
4
Хороший однострочник и +1 за упоминание о том, что файл должен умещаться в памяти. (К сожалению, нет способа разархивировать файл pkzip из-за структуры формата файла).
lxgr
2
имейте в виду, что это буферизует все в памяти перед извлечением
Уильям Казарин
1
нет никаких причин, по которым он не может быть прочитан как поток, если файл может поместиться в память достаточно легко, не очень точно. Причина, по которой вы вынуждены буферизовать весь zip-архив в памяти перед извлечением содержимого, заключается именно в том, что он не может быть прочитан как поток. Конечно, все еще может быть полезно избежать записи zip-архива в файл.
Хокан Линдквист
Это не поток, вы читаете весь файл в памяти, используя .read()метод
Ромуальд Брюне
18

Это вряд ли сработает так, как вы ожидаете. Zip - это не просто формат сжатия, но и формат контейнера. Он объединяет задания tar и gzip.bzip2 в одно целое. Тем не менее, если ваш zip-файл содержит один файл, вы можете использовать unzip -p для распаковки файлов в stdout. Если у вас есть более одного файла, вы не сможете сказать, где они начинаются и останавливаются.

Что касается чтения из stdin, на странице руководства по разархивированию есть следующее предложение:

Архивы, считанные из стандартного ввода, пока не поддерживаются, за исключением funzip (и тогда может быть извлечен только первый элемент архива).

Возможно, вам повезет с funzip.

Дэвид Пашли
источник
Если в zip есть несколько файлов, то -p может распечатать один файл, используя имя файла в качестве параметра: unzip -p temp.zip file-inside-zip
Taavi Ilves
7

То, что вы хотите сделать, это сделать, чтобы unzipвзять ZIP-файл на его стандартный ввод, а не в качестве аргумента. Это, как правило , легко поддерживается gzipи tarвид инструментов с -аргументом. Но стандарт unzipэтого не делает (хотя поддерживает извлечение в трубу). Однако еще не все потеряно ...

Посмотрите на странице руководства funzip .

funzip без аргумента файла действует как фильтр; то есть предполагается, что ZIP-архив (или файл gzip) передается в стандартный ввод, и он извлекает первого члена из архива в стандартный вывод. Когда stdin приходит с устройства tty, funzip предполагает, что это не может быть поток (двоичных) сжатых данных, и вместо этого показывает краткий текст справки. Если есть аргумент файла, то ввод читается из указанного файла, а не из стандартного ввода.

Учитывая ограничение на извлечение из одного члена, funzip наиболее полезен в сочетании со вспомогательной программой-архиватором, такой как tar (1). В следующем разделе приведен пример, иллюстрирующий это использование в случае резервного копирования диска на ленту.

Это хорошо согласуется с идеей о том, что большинство Linux-архивов обычно обрабатываются TAR, а затем каким-то образом ZIP-архивом (gzip, bzip и др.). Это будет работать для вас, если у вас есть tar.ZIP.


Стоит отметить, что funzipавтор Info-ZIP написал оригинальный автор Марк Адлер. Он пишет на странице руководства funzip:

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

однако такого обновления не наблюдается. Я подозреваю, что Марк посчитал это ненужным, поскольку другие методы архивации легко работали с TAR.

Nik
источник
Просто комментарий; Некоторые люди хотели бы, чтобы Python или любой другой язык был расстегнут. Ярким примером является Heroku, который не включает в себя tar или unzip в своей системе. Обходной путь - использовать jar, установив Java, что разрешено.
Ник
Подробнее об ограничениях funzip и аналогичных инструментов (в частности, о возможности показать только первого члена архива) в этом ответе: unix.stackexchange.com/a/211286/77539
Джошуа Голдберг,
6

Мне нравится использовать curl, потому что он установлен по умолчанию ( -Lнеобходим для редиректов, которые часто происходят):

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

Однако bsdtarпо умолчанию не устанавливается, и я не смог приступить funzipк работе.

Тодд Партридж
источник
Также отлично работает с несколькими файлами
Джоннор
5

Это перепост моего ответа на похожий вопрос:

Формат файла ZIP включает в себя каталог (индекс) в конце архива. В этом каталоге указывается, где в архиве находится каждый файл, и, таким образом, обеспечивается быстрый произвольный доступ без чтения всего архива.

Это может создать проблему при попытке чтения ZIP-архива через канал, поскольку доступ к индексу не осуществляется до самого конца, и поэтому отдельные элементы не могут быть правильно извлечены до тех пор, пока файл не будет полностью прочитан и больше не доступен , Таким образом, неудивительно, что большинство декомпрессоров ZIP просто терпят неудачу, когда архив подается по каналу.

Каталог в конце архива - не единственное место, где метаинформация файла хранится в архиве. Кроме того, отдельные записи также включают эту информацию в локальный заголовок файла для целей резервирования.

Хотя не каждый ZIP-распаковщик будет использовать локальные заголовки файлов, когда индекс недоступен, внешние интерфейсы tar и cpio для libarchive (также известные как bsdtar и bsdcpio) могут и будут делать это при чтении через канал, что означает следующее:

wget -qO- http://example.org/file.zip | bsdtar -xvf-
ruario
источник
4

Это невозможно с Info-Zip, который является наиболее распространенной реализацией OSS. Что еще более важно, это не рекомендуется из-за конструкции архивов ZIP.

Если вам необходимо изменить формат, рассмотрите возможность использования tar (1). Он вполне доволен потоковым вводом / выводом и, по сути, ожидает его по умолчанию.

Кроме того, вы часто можете определить, ожидают ли приложения потокового ввода / вывода, указав «-» для имени файла. Info-Zip, как вы можете себе представить, не воспринимает это как веский аргумент.

Дэн Карли
источник
4

В zsh вы можете сделать следующее:

unzip =( curl http://example.com/someZipFile.zip )
Ян Робертсон
источник
3

Простейшая распространенная утилита, которая сделает это jar, будет предполагать, что STDIN используется, если вы не передадите ей аргументы файла. Он также принимает аргументы, аналогичные tarпрограмме для операций.

например, список содержимого архива

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

Хотя Java не всегда устанавливается, на тех машинах, где она есть, jarэто определенно самый удобный способ сделать это.

Адриан
источник
3

Репост моего ответа :

BusyBox unzipможет взять stdin и извлечь все файлы.

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

Тире после unzip- использовать stdin в качестве входных данных.

Вы можете даже,

cat file.zip | busybox unzip -

Но это просто избыточно unzip file.zip.

Если ваш дистрибутив по умолчанию использует BusyBox (например, Alpine), просто запустите unzip -.

Saftever
источник
1

Мне действительно нужно что-то более сложное - извлечь конкретный файл, если он существует. Сложность в том, что поток входного файла может быть не zip-файлом, и в этом случае мне нужно было продолжить через канал. Вот мое решение (в основном благодаря решению Джейсона Р. Кумбса)

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)"

Я сохранил это как файл с именем «effpoptp» (не простое имя) в папке «/ bin» на моей машине, поэтому тестирование это выглядит так:

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

Цель состоит в том, чтобы управлять версиями файлов MySQL Workbench, где файл может быть файлом XML, названным как файл рабочей среды, или полным файлом рабочей среды.

SEoF
источник