표준 출력(로컬 또는 원격 위치)에서 파이프될 때 헤더로 압축 파일을 추출하시겠습니까?

표준 출력(로컬 또는 원격 위치)에서 파이프될 때 헤더로 압축 파일을 추출하시겠습니까?

로컬로 또는 네트워크 위치에서 파이프를 통해 압축 파일을 보내고 있습니다. 수신 측에서는 압축 유형을 감지하고 적절한 압축 해제 유틸리티(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진수 값을 보면 사용된 압축 유형을 알 수 있습니다. 예를 들어 xxd두 개의 압축 파일의 처음 몇 개의 16진수 값을 살펴 보려면 1f8b 0808gzip과 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명령의 인수는 동일하지만 다른 압축 해제 도구에서는 다를 수 있습니다.

다음은 원격 호스트에서 얻은 파일의 압축을 푸는 버전입니다.

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

관련 정보