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 xxd
ersten paar Hex-Werte zweier komprimierter Dateien ansehe, dann schaue ich mir die 1f8b 0808
für gzip und 425a 6836
die 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 dd
Dateikonvertierungsprogramms 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 file
auf 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 gzip
und bzip2
Befehle 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
)