圧縮されたファイルを、stdout (ローカルまたはリモートの場所) からパイプされたヘッダーで抽出しますか?

圧縮されたファイルを、stdout (ローカルまたはリモートの場所) からパイプされたヘッダーで抽出しますか?

圧縮ファイルをローカルまたはネットワークの場所からパイプで送信しています。受信側では、圧縮の種類を検出し、適切な解凍ユーティリティ (gzip、bzip2、xz など) を使用してファイルを解凍したいと思います。コマンドは次のようになります。

地元:

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

ネットワーク経由:

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

拡張子が指定されていない場合でも (.gz や .bz2 など)、ファイルの最初のいくつかの 16 進数値を確認することで、使用されている圧縮の種類がわかります。たとえば、2 つの圧縮ファイルの最初のいくつかの 16 進数値を確認する場合は、 gzip とbzip2 にxxdなります 。1f8b 0808425a 6836

ただし、パイピングを引き続き使用するには、最初の受信バイトをチェックして、ファイルの先頭に適切な解凍ユーティリティを選択するにはどうすればよいでしょうか?

したがって、不明な圧縮ファイルが 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

これを拡張すると、これは実用的なソリューションです。バイナリ ヘッダーを保存するために一時ファイルを使用します。2 つの 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"

示されている例では、およびコマンドの ARGS はgzip同じbzip2ですが、他の解凍ツールでは異なる場合があります。

リモート ホストから取得したファイルを解凍するバージョンを次に示します。

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

関連情報