Извлечь сжатый файл по его заголовку, как он передается из stdout (локально или из удаленного расположения)?

Извлечь сжатый файл по его заголовку, как он передается из stdout (локально или из удаленного расположения)?

Я отправляю сжатый файл с помощью конвейера локально или из сетевого расположения. И на принимающей стороне я хотел бы определить тип сжатия и использовать соответствующую утилиту декомпрессии (gzip, bzip2, xz..etc) для его извлечения. Команды выглядят следующим образом:

Местный:

cat misteryCompressedFile | [compressionUtility] -d -fc > /opt/files/uncompressedfile

По сети:

ssh user@ipaddr "cat misteryCompressedFile" | [compressionUtility] -d -fc > /opt/files/uncompressedfile

Можно определить тип используемого сжатия, даже если расширение не указано (например, .gz или .bz2), посмотрев на первые несколько шестнадцатеричных значений файла. Например, если я использую xxdдля просмотра первых нескольких шестнадцатеричных значений двух сжатых файлов, то я буду 1f8b 0808использовать gzip и 425a 6836bzip2.

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

Таким образом, если неизвестный сжатый файл имеет тип gzip, команда будет следующей:

cat misteryCompressedFile | gzip -d -fc > /opt/files/uncompressedfile

а если неизвестный сжатый файл имеет тип bzip2, команда будет такой:

cat misteryCompressedFile | bzip2 -d -fc > /opt/files/uncompressedfile

Возможно ли принять такое решение с помощью конвейеризации на лету, не загружая весь файл и не принимая затем решения, что использовать для распаковки?

решение1

Да, это можно сделать в процессе разработки, не читая весь файл.

Этот первый фрагмент скрипта иллюстрирует механизм, с помощью которого мы перехватим и проверим заголовок и передадим его дальше. Обратите внимание, что мы выводим заголовок в stderr (>&2), но он продолжает появляться в выводе:

$ echo 0123456789ABCDEF |
(
    HEADER=$(dd bs=1 count=4);
    printf 'HEADER:%s\n' "$HEADER" >&2;
    printf '%s\n' "$HEADER";
    cat 
)
4+0 records in
4+0 records out
4 bytes (4 B) copied, 8.4293e-05 s, 47.5 kB/s
HEADER:0123
0123456789ABCDEF
$

Ключевым моментом является использование ddутилиты преобразования файлов с небольшим размером блока bs=1.

Расширяя это, это рабочее решение. Мы будем использовать временный файл для хранения двоичного заголовка. Если он не видит один из двух 4-байтовых заголовков, то он ничего не делает:

#!/bin/sh

trap "rm -f /tmp/$$; exit 1" 1 2 3 15

# grab the 1st 4 bytes off the input stream,
# store them in a file, convert to ascii,
# and store in variable:
HEADER=$(
    dd bs=1 count=4 2>/dev/null |
    tee /tmp/$$ |
    od -t x1 |
    sed '
        s/^00* //
        s/ //g
        q
    '
)

case "$HEADER" in
    1f8b0800)
        UNCOMPRESS='gzip -d -fc'
    ;;
    425a6839)
        UNCOMPRESS='bzip2 -d -fc'
    ;;
    *)
        echo >&2 "$0: unknown stream type for header '$HEADER'"
        exit 2
    ;;
esac

echo >&2 "$0: File header is '$HEADER' using '$UNCOMPRESS' on stream."
cat /tmp/$$ - | $UNCOMPRESS
rm /tmp/$$

решение2

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

например

#! /bin/sh

filetype=$(file misteryCompressedFile)

case "$filetype" in
   *gzip*) CMD='gzip' ; ARGS='-d -fc' ;;
   *bzip2*) CMD='bzip2' ; ARGS='-d -fc' ;;
   *) echo "error: unknown compression type" ; exit 1 ;;
esac

cat misteryCompressedFile | ssh user@ipaddr "$CMD $ARGS > /opt/files/uncompressedfile"

В показанном примере команды ARGS gzipи bzip2одинаковы, но для других инструментов декомпрессии они могут отличаться.

Вот версия, которая распаковывает файл, полученный с удаленного хоста:

#! /bin/sh

# set up an anonymous fifo on fd 3 so we can pass the 
# output of `file` to the second subshell without risking
# corruption of stdout/stdin
FIFO=$(mktemp -u)
mkfifo "$FIFO"
exec 3<>"$FIFO"
rm -f "$FIFO"

ssh user@ipaddr 'cat misteryCompressedFile' | 
(
    HEADER=$(dd bs=1 count=20 2> /dev/null | 
             od -A none -t o1 -w512 | 
             sed -e 's: :\\:g')

    printf "$HEADER" | file --mimetype - | cut -d/ -f2 >&3
    printf "$HEADER"
    cat
) | (
    read -u 3 -r filetype
    case "$filetype" in
       gzip) CMD='gzip' ; ARGS='-d -fc' ;;
       x-bzip2) CMD='bzip2' ; ARGS='-d -fc' ;;
       x-xz) CMD='unxz' ; ARGS='' ;;
       x-lzma) CMD='lzcat' ; ARGS='' ;;
       x-compress) CMD='uncompress' ; ARGS='' ;;
       x-lrzip) CMD='lrzcat' ; ARGS='' ;;
       *) echo "error: unknown compression type" >&2 ; exit 1 ;;
    esac

    $CMD $ARGS > /opt/files/uncompressedfile
)

Связанный контент