當壓縮檔案從標準輸出(本地或遠端位置)通過管道傳輸時,透過其標頭提取壓縮檔案?

當壓縮檔案從標準輸出(本地或遠端位置)通過管道傳輸時,透過其標頭提取壓縮檔案?

我正在透過本地或網路位置的管道發送壓縮檔案。在接收端,我想檢測壓縮類型並使用適當的解壓縮實用程式(gzip、bzip2、xz 等)來提取它。命令如下圖所示:

當地的:

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

透過網路:

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

即使沒有提供副檔名(例如 .gz 或 .bz2),人們也可以透過查看檔案的前幾個十六進位值來判斷所使用的壓縮類型。例如,如果我用來xxd查看兩個壓縮檔案的前幾個十六進位值,那麼我將 1f8b 0808查看 gzip 和425a 6836bzip2。

但是,要仍然使用管道,如何檢查第一個傳入位元組以便為檔案的第一個檔案選擇正確的解壓縮實用程式?

因此,如果未知壓縮檔案是 gzip 類型,命令將是這樣的:

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

如果未知壓縮檔是 bzip2 類型,指令將是這樣的:

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

是否可以透過動態管道做出這樣的決定,而不必下載整個文件,然後決定使用什麼來解壓縮?

答案1

是的,您可以在管道中執行此操作,而無需讀取整個文件。

第一個腳本片段說明了我們攔截和檢查標頭並將其傳遞的機制。請注意,我們將標頭印到 stderr (>&2),但它仍然出現在輸出中:

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

關鍵是使用dd小塊大小的檔案轉換實用程式bs=1

擴充一下,這是一個可行的解決方案。我們將使用臨時檔案來儲存二進位頭。如果它沒有看到兩個 4 位元組標頭之一,那麼它什麼都不做:

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

答案2

在發送電腦上使用file並使用該資訊來決定在遠端主機上執行哪個解壓縮命令。

例如

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

在所示的範例中,gzipbzip2命令的 ARGS 是相同的...但對於其他解壓縮工具來說它們可能不同。

這是一個解壓縮從遠端主機取得的檔案的版本:

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

相關內容