¿Extraer el archivo comprimido por su encabezado tal como se canaliza desde la salida estándar (localmente o desde una ubicación remota)?

¿Extraer el archivo comprimido por su encabezado tal como se canaliza desde la salida estándar (localmente o desde una ubicación remota)?

Estoy enviando un archivo comprimido con tubería local o desde una ubicación de red. Y en el lado receptor, me gustaría detectar el tipo de compresión y utilizar la utilidad de descompresión adecuada (gzip, bzip2, xz, etc.) para extraerlo. Los comandos tienen el siguiente aspecto:

Local:

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

A través de la red:

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

Se puede saber el tipo de compresión utilizada incluso si no se proporciona ninguna extensión (por ejemplo, .gz o .bz2) observando los primeros valores hexadecimales del archivo. Por ejemplo, si suelo xxdmirar los primeros valores hexadecimales de dos archivos comprimidos, entonces buscaré 1f8b 0808gzip y 425a 6836bzip2.

Sin embargo, para seguir usando tuberías, ¿cómo puedo verificar el primer byte entrante para seleccionar la utilidad de descompresión adecuada para el primero del archivo?

Entonces, si el archivo comprimido desconocido es del tipo gzip, el comando será este:

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

y si el archivo comprimido desconocido es del tipo bzip2, el comando será este:

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

¿Es posible tomar esa decisión con la tubería sobre la marcha sin tener que descargar el archivo completo y luego decidir qué usar para la descompresión?

Respuesta1

Sí, puede hacerlo en proceso, sin tener que leer el archivo completo.

Este primer fragmento de script ilustra el mecanismo mediante el cual interceptaremos e inspeccionaremos el encabezado y lo transmitiremos. Observe que imprimimos el encabezado en stderr (>&2), pero continúa apareciendo en el resultado:

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

La clave es utilizar la ddutilidad de conversión de archivos con un tamaño de bloque pequeño bs=1.

Ampliando eso, esta es una solución funcional. Usaremos un archivo temporal para almacenar el encabezado binario. Si no ve uno de los dos encabezados de 4 bytes, no hace 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/$$

Respuesta2

Úselo fileen la máquina emisora ​​y use esa información para decidir qué comando de descompresión ejecutar en el host remoto.

p.ej

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

En el ejemplo mostrado, los ARGS para los comandos gzipy bzip2son los mismos... pero pueden ser diferentes para otras herramientas de descompresión.

Aquí hay una versión que descomprimirá un archivo obtenido del 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
)

información relacionada