Как DEFLATE с помощью инструмента командной строки, чтобы извлечь объект git?

Я ищу оболочку командной строки для алгоритма DEFLATE.

У меня есть файл (git blob), который сжат с помощью DEFLATE, и я хочу распаковать его. Команда gzip, похоже, не имеет возможности напрямую использовать алгоритм DEFLATE, а не формат gzip.

В идеале я ищу стандартный инструмент Unix/Linux, который может сделать это.

редактировать: это вывод, который я получаю при попытке использовать gzip для моей проблемы:

$ cat .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 | gunzip

gzip: stdin: not in gzip format

22 ответа

Решение

ОБНОВЛЕНИЕ: Марк Адлер отметил, что Git BLOB-объектов - это не необработанные потоки DEFLATE, а потоки zlib. Они могут быть распакованы pigz инструмент, который поставляется в комплекте с несколькими дистрибутивами Linux:

$ cat foo.txt 
file foo.txt!

$ git ls-files -s foo.txt
100644 7a79fc625cac65001fb127f468847ab93b5f8b19 0   foo.txt

$ pigz -d < .git/objects/7a/79fc625cac65001fb127f468847ab93b5f8b19 
blob 14file foo.txt!

Мой оригинальный ответ, сохраненный по историческим причинам:

Если я понимаю подсказку в статье Википедии, упомянутой Марком ван Кемпеном, вы можете использовать puff.c из злиба напрямую.

Это небольшой пример:

#include <assert.h>
#include <string.h>
#include "puff.h"

int main( int argc, char **argv ) {
    unsigned char dest[ 5 ];
    unsigned long destlen = 4;
    const unsigned char *source = "\x4B\x2C\x4E\x49\x03\x00";
    unsigned long sourcelen = 6;    
    assert( puff( dest, &destlen, source, &sourcelen ) == 0 );
    dest[ 4 ] = '\0';
    assert( strcmp( dest, "asdf" ) == 0 );
}

Вы можете сделать это с помощью инструмента командной строки OpenSSL:

openssl zlib -d < $IN > $OUT

К сожалению, по крайней мере на Ubuntu, zlib подкоманда отключена в конфигурации сборки по умолчанию (--no-zlib--no-zlib-dynamic), так что вам нужно будет скомпилировать openssl из источника, чтобы использовать его. Но это включено по умолчанию на Arch, например.

Изменить: похоже на zlib команда больше не поддерживается в Arch. Этот ответ может быть бесполезен:(

Что-то вроде следующего напечатает необработанный контент, включая заголовок "$type $length\0":

perl -MCompress::Zlib -e 'undef $/; print uncompress(<>)' \
     < .git/objects/27/de0a1dd5a89a94990618632967a1c86a82d577

Питонический однострочник:

$> python -c "import zlib,sys;print \
           repr(zlib.decompress(sys.stdin.read()))" < $IN

Вы можете использовать zlib-flate, например так:

cat .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 \
    | zlib-flate -uncompress; echo

Это по умолчанию на моей машине, но это часть qpdf - tools for and transforming and inspecting PDF files если вам нужно установить его.

Я выскочил echo в конце команды, так как легче прочитать вывод таким образом.

Попробуйте следующую команду:

printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 | gunzip

Никаких внешних инструментов не требуется.

Источник: Как распаковать данные zlib в UNIX? в Unix SE

Вот один строковый Ruby ( cd .git/ first и укажите путь к любому объекту):

ruby -rzlib -e 'print Zlib::Inflate.new.inflate(STDIN.read)' < ./74/c757240ec596063af8cd273ebd9f67073e1208

Я устал от отсутствия хорошего решения для этого, поэтому я положил что-то на NPM:

https://github.com/jezell/zlibber

Теперь можно просто трубу, чтобы надуть / выкачать команду.

Вот пример взлома объекта коммита в Python:

$ git show
commit 0972d7651ff85bedf464fba868c2ef434543916a
# all the junk in my commit...
$ python
>>> import zlib
>>> file = open(".git/objects/09/72d7651ff85bedf464fba868c2ef434543916a")
>>> data = file.read()
>>> print data
# binary garbage
>>> unzipped_data = zlib.decompress(data)
>>> print unzipped_data
# all the junk in my commit!

То, что вы увидите там, почти идентично выводу 'git cat-file -p [hash]', за исключением того, что команда не печатает заголовок ('commit', сопровождаемый размером содержимого и нулевым байтом).

Git объекты сжимаются zlib скорее, чем gzipтак что либо используя zlib распаковать его или команду git, т.е. git cat-file -p <SHA1>, для печати контента.

Похоже, Марк Адлер имеет в виду нас и написал пример того, как это сделать, с помощью: http://www.zlib.net/zpipe.c

Он компилируется только с gcc -lz и заголовки zlib установлены. Я скопировал полученный бинарный файл в мой /usr/local/bin/zpipe во время работы с мерзавцами.

// save this as deflate.go

package main

import (
    "compress/zlib"
    "io"
    "os"
    "flag"
)

var infile = flag.String("f", "", "infile")

func main() {
    flag.Parse()
    file, _ := os.Open(*infile)

    r, err := zlib.NewReader(file)
    if err != nil {
        panic(err)
    }
    io.Copy(os.Stdout, r)

    r.Close()
}

$ go build deflate.go
$ ./deflate -f .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7

Объекты git - потоки zlib (не необработанный deflate). Свинья будет распаковывать тех, кто с -dz вариант.

Свинья может сделать это:

apt-get install pigz
unpigz -c .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7

Я неоднократно сталкивался с этой проблемой, и мне кажется, что почти все ответы в Интернете либо неверны, либо требуют компиляции не идеального кода, либо загрузки целого ряда зависимостей, не отслеживаемых системой! Но я нашел реальное решение. Он использует PERL, поскольку PERL доступен в большинстве систем.

Из оболочки, похожей на Bash:

      perl -mIO::Uncompress::RawInflate=rawinflate -erawinflate'"-","-"'

Или, если вы выполняете / разветвляете вручную (без кавычек оболочки, но с разделением строк):

  • perl
  • -mIO::Uncompress::RawInflate=rawinflate
  • -erawinflate"-","-"

Большое предостережение: если поток не запускается как допустимый поток DEFLATE (например, несжатые данные), тогда эта команда с радостью передаст все данные в нетронутом виде. Только если поток начинается как допустимый поток DEFLATE (я полагаю, с допустимым словарем? Я не слишком уверен ...), тогда эта команда каким-то образом приведет к ошибке. Однако в некоторых ситуациях это может быть желательно.

Надеюсь, это поможет любым путешественникам во времени.

Использованная литература:

PERL IO::Uncompress::RawInflate::rawinflate

Чтобы добавить в коллекцию, вот однострочники perl для deflate/inflate/raw deflate/raw inflate.

Сдувать

perl -MIO::Compress::Deflate -e 'undef $/; my ($in, $out) = (<>, undef); IO::Compress::Deflate::deflate(\$in, \$out); print $out;'

Надуть

perl -MIO::Uncompress::Inflate -e 'undef $/; my ($in, $out) = (<>, undef); IO::Uncompress::Inflate::inflate(\$in, \$out); print $out;'

Необработанный дефлят

perl -MIO::Compress::RawDeflate -e 'undef $/; my ($in, $out) = (<>, undef); IO::Compress::RawDeflate::rawdeflate(\$in, \$out); print $out;'

Сырье надуть

perl -MIO::Uncompress::RawInflate -e 'undef $/; my ($in, $out) = (<>, undef); IO::Uncompress::RawInflate::rawinflate(\$in, \$out); print $out;'

Почему бы вам просто не использовать инструменты git для доступа к данным? Это должно быть в состоянии прочитать любой объект git:

git show --pretty=raw <object SHA-1>

См. http://en.wikipedia.org/wiki/DEFLATE

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

Откуда вы знаете, что он сжат с помощью DEFLATE? Какой инструмент был использован для сжатия файла?

Однострочник Python3:

      python3 -c "import zlib,sys; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read()))" < infile > outfile

Таким образом, содержимое обрабатывается как двоичные данные, избегая преобразования в/из юникода.

Вот как я это делаю с Powershell.

      $fs = New-Object IO.FileStream((Resolve-Path $Path), [IO.FileMode]::Open, [IO.FileAccess]::Read)
$fs.Position = 2
$cs = New-Object IO.Compression.DeflateStream($fs, [IO.Compression.CompressionMode]::Decompress)
$sr = New-Object IO.StreamReader($cs)
$sr.ReadToEnd()

Затем вы можете создать псевдоним, например:

      function func_deflate{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline = $true)]
        [ValidateScript({Test-Path $_ -PathType leaf})]
        [string]$Path
    )
    $ErrorActionPreference = 'Stop'    
    $fs = New-Object IO.FileStream((Resolve-Path $Path), [IO.FileMode]::Open, [IO.FileAccess]::Read)
    $fs.Position = 2
    $cs = New-Object IO.Compression.DeflateStream($fs, [IO.Compression.CompressionMode]::Decompress)
    $sr = New-Object IO.StreamReader($cs)
    return $sr.ReadToEnd()
}

Set-Alias -Name deflate -Value func_deflate

Я нашел этот вопрос в поисках обходного пути с ошибкой с -text утилита в новой версии hadoop dfs клиент я только что установил. -text Утилита работает как catЗа исключением случаев, когда читаемый файл сжимается, он прозрачно распаковывает и выводит простой текст (отсюда и название).

Уже опубликованные ответы были определенно полезными, но у некоторых из них есть одна проблема при работе с объемами данных Hadoop- они считывают все в память перед распаковкой.

Итак, вот мои вариации на Perl а также Python Ответы выше, которые не имеют этого ограничения:

Python:

hadoop fs -cat /path/to/example.deflate |
  python -c 'import zlib,sys;map(lambda b:sys.stdout.write(zlib.decompress(b)),iter(lambda:sys.stdin.read(4096),""))'

Perl:

hadoop fs -cat /path/to/example.deflate |
  perl -MCompress::Zlib -e 'print uncompress($buf) while sysread(STDIN,$buf,4096)'

Обратите внимание на использование -cat подкоманда вместо -text, Это сделано для того, чтобы мой обход не сломался после того, как они исправили ошибку. Извинения за читаемость версии Python.

const zlib = require("zlib");
const adler32 = require("adler32");
const data = "hello world~!";
const chksum = adler32.sum(new Buffer(data)).toString(16);
console.log("789c",zlib.deflateRawSync(data).toString("hex"),chksum);
// or
console.log(zlib.deflateSync(data).toString("hex"));
Другие вопросы по тегам