Назад | Перейти на главную страницу

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

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

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

Хотя 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

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

Что касается чтения из стандартного ввода, на странице руководства по распаковке есть это предложение:

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

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

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

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

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

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

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


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

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

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

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

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

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

Это репост мой ответ на аналогичный вопрос:

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

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

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

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

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

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

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

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

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

unzip =( curl http://example.com/someZipFile.zip )

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

например перечислить содержимое архива

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

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

Репост мой ответ:

BusyBox's unzip может взять стандартный ввод и извлечь все файлы.

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

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

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

cat file.zip | busybox unzip -

Но это просто лишнее unzip file.zip.

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

Мне действительно нужно было что-то посложнее - извлечь конкретный файл, если он существует. Сложность в том, что поток входного файла может не быть 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-файлом, названным как файл workbench, или полным файлом workbench.