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 xxd
mirar los primeros valores hexadecimales de dos archivos comprimidos, entonces buscaré 1f8b 0808
gzip y 425a 6836
bzip2.
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 dd
utilidad 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 file
en 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 gzip
y bzip2
son 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
)