Extrai o arquivo compactado pelo seu cabeçalho, pois ele é canalizado da stdout (local ou remotamente)?

3

Estou enviando arquivo compactado com tubulação local ou de um local de rede. E no final do recebimento, gostaria de detectar o tipo de compactação e usar o utilitário de descompactação apropriado (gzip, bzip2, xz..etc) para extraí-lo. Comandos tem a seguinte aparência:

Local:

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

Através da rede:

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

Pode-se dizer o tipo de compactação usado mesmo se não houver extensão fornecida (por exemplo, .gz ou .bz2) olhando os primeiros valores hexadecimais do arquivo. Por exemplo, se eu usar xxd para ver os primeiros valores hexadecimais de dois arquivos compactados, então eu vou 1f8b 0808 para gzip e 425a 6836 para bzip2.

No entanto, para continuar usando 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á:

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

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

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

É possível tomar essa decisão com a tubulação sem ter que baixar o arquivo inteiro e depois decidir o que usar para a descompressão?

    
por Armen 03.11.2015 / 03:32

2 respostas

4

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

Este primeiro fragmento de script ilustra o mecanismo pelo qual vamos interceptar e inspecionar o cabeçalho e passá-lo adiante. Observe que imprimimos o cabeçalho para 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 está usando o dd do utilitá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 ele 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/$$
    
por 03.11.2015 / 04:18
1

Use file na máquina de envio e use essa informação para decidir qual comando de descompactação será executado 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 gzip e bzip2 são os mesmos ... mas podem ser diferentes para outras ferramentas de descompressão.

Aqui está uma versão que 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
)
    
por 03.11.2015 / 04:16