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

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

로컬 또는 네트워크 위치에서 파이프를 사용하여 압축 파일을 보내고 있습니다. 그리고 수신 측에서는 압축 유형을 감지하고 적절한 압축 해제 유틸리티(gzip, bzip2, xz..etc)를 사용하여 압축을 풀고 싶습니다. 명령은 다음과 같습니다.

현지의:

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명령에 대한 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
)

관련 정보