Extrair o arquivo compactado pelo cabeçalho conforme ele é canalizado do stdout (localmente ou de local remoto)?

Extrair o arquivo compactado pelo cabeçalho conforme ele é canalizado do stdout (localmente ou de local remoto)?

Estou enviando um arquivo compactado com tubulação local ou de um local de rede. E no lado receptor, gostaria de detectar o tipo de compactação e usar o utilitário de descompactação apropriado (gzip, bzip2, xz..etc) para extraí-lo. Os comandos são os seguintes:

Local:

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

Pela rede:

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

Pode-se saber o tipo de compactação usada mesmo que não haja nenhuma extensão fornecida (por exemplo, .gz ou .bz2) observando os primeiros valores hexadecimais do arquivo. Por exemplo, se eu xxdobservar primeiro alguns valores hexadecimais de dois arquivos compactados, usarei 1f8b 0808gzip e 425a 6836bzip2.

No entanto, para ainda usar a tubulação, como posso verificar o primeiro byte de entrada para selecionar o utilitário de descompactação adequado para o primeiro arquivo?

Portanto, se o arquivo compactado desconhecido for do tipo gzip, o comando será este:

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

e se o arquivo compactado desconhecido for do tipo bzip2, o comando será este:

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

É possível tomar essa decisão com tubulação em tempo real, sem precisar baixar o arquivo inteiro e depois decidir o que usar para descompactação?

Responder1

Sim, você pode fazer isso no pipeline, sem precisar ler o arquivo inteiro.

Este primeiro fragmento de script ilustra o mecanismo pelo qual interceptaremos e inspecionaremos o cabeçalho e o transmitiremos. Observe que imprimimos o cabeçalho em stderr (>&2), mas ele continua aparecendo na saída:

$ 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
$

A chave é usar o ddutilitário de conversão de arquivos com um tamanho de bloco pequeno bs=1.

Expandindo isso, esta é uma solução funcional. Usaremos um arquivo temporário para armazenar o cabeçalho binário. Se não vir um dos dois cabeçalhos de 4 bytes, não fará nada:

#!/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/$$

Responder2

Use filena máquina remetente e use essas informações para decidir qual comando de descompactação executar no host remoto.

por exemplo

#! /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"

No exemplo mostrado, os comandos ARGS para gzipe bzip2são os mesmos... mas podem ser diferentes para outras ferramentas de descompressão.

Aqui está uma versão que irá descompactar um arquivo obtido do host remoto:

#! /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
)

informação relacionada