Komprimierte Datei anhand ihres Headers extrahieren, während sie von stdout (lokal oder von einem Remote-Standort) weitergeleitet wird?

Komprimierte Datei anhand ihres Headers extrahieren, während sie von stdout (lokal oder von einem Remote-Standort) weitergeleitet wird?

Ich sende eine komprimierte Datei mit Piping entweder lokal oder von einem Netzwerkstandort. Und auf der Empfängerseite möchte ich die Art der Komprimierung erkennen und das entsprechende Dekomprimierungsprogramm (gzip, bzip2, xz usw.) verwenden, um sie zu extrahieren. Die Befehle sehen wie folgt aus:

Lokal:

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

Über das Netzwerk:

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

Man kann den verwendeten Komprimierungstyp erkennen, auch wenn keine Erweiterung angegeben ist (z. B. .gz oder .bz2), indem man sich die ersten paar Hex-Werte der Datei ansieht. Wenn ich mir beispielsweise die xxdersten paar Hex-Werte zweier komprimierter Dateien ansehe, dann schaue ich mir die 1f8b 0808für gzip und 425a 6836die für bzip2 an.

Um dennoch die Piping-Funktion zu verwenden, wie kann ich das erste eingehende Byte prüfen, um das richtige Dekomprimierungsprogramm für das erste Byte der Datei auszuwählen?

Wenn also die unbekannte komprimierte Datei vom Typ gzip ist, lautet der Befehl wie folgt:

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

und wenn die unbekannte komprimierte Datei vom Typ bzip2 ist, lautet der Befehl wie folgt:

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

Ist es möglich, eine solche Entscheidung mithilfe von Piping spontan zu treffen, ohne die gesamte Datei herunterladen und dann entscheiden zu müssen, was zur Dekomprimierung verwendet werden soll?

Antwort1

Ja, das können Sie in der Pipeline tun, ohne die gesamte Datei lesen zu müssen.

Dieses erste Skriptfragment veranschaulicht den Mechanismus, mit dem wir den Header abfangen, untersuchen und weitergeben. Beachten Sie, dass wir den Header auf stderr drucken (>&2), er aber trotzdem weiterhin in der Ausgabe erscheint:

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

Der Schlüssel liegt in der Verwendung des ddDateikonvertierungsprogramms mit einer kleinen Blockgröße bs=1.

Wenn wir das weiter ausführen, ist dies eine funktionierende Lösung. Wir verwenden eine temporäre Datei, um den binären Header zu speichern. Wenn einer der beiden 4-Byte-Header nicht angezeigt wird, geschieht nichts:

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

Antwort2

Verwenden Sie es fileauf dem sendenden Computer und entscheiden Sie anhand dieser Informationen, welcher Dekomprimierungsbefehl auf dem Remote-Host ausgeführt werden soll.

z.B

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

Im gezeigten Beispiel sind die ARGS für gzipund bzip2Befehle gleich … sie können aber für andere Dekomprimierungstools unterschiedlich sein.

Hier ist eine Version, die eine vom Remote-Host abgerufene Datei dekomprimiert:

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

verwandte Informationen