gzip 동일한 입력 다른 출력

gzip 동일한 입력 다른 출력

확인해 보세요:

data/tmp$ gzip -l tmp.csv.gz
     compressed        uncompressed  ratio uncompressed_name
           2846               12915  78.2% tmp.csv
data/tmp$ cat tmp.csv.gz | gzip -l
     compressed        uncompressed  ratio uncompressed_name
             -1                  -1   0.0% stdout
data/tmp$ tmp="$(cat tmp.csv.gz)" && echo "$tmp" | gzip -l

gzip: stdin: unexpected end of file

좋아, 분명히 입력은 동일하지 않지만 논리적으로는 동일했어야 합니다. 내가 여기서 무엇을 놓치고 있는 걸까요? 파이프 버전이 작동하지 않는 이유는 무엇입니까?

답변1

이 명령

$ tmp="$(cat tmp.csv.gz)" && echo "$tmp" | gzip -l

의 내용을 tmp.csv.gz쉘 변수에 할당하고 echo이를 으로 파이프하는 데 사용하려고 합니다 gzip. 그러나 쉘의 기능이 방해가 됩니다(널 문자는 생략됨). 테스트 스크립트를 통해 이를 확인할 수 있습니다.

#!/bin/sh
tmp="$(cat tmp.csv.gz)" && echo "$tmp" |cat >foo.gz
cmp foo.gz tmp.csv.gz

추가 작업을 통해 od(또는 hexdump)을 사용하고 두 파일을 자세히 살펴봅니다. 예를 들어:

0000000 037 213 010 010 373 242 153 127 000 003 164 155 160 056 143 163
        037 213  \b  \b 373 242   k   W  \0 003   t   m   p   .   c   s
0000020 166 000 305 226 141 157 333 066 020 206 277 367 127 034 012 014
          v  \0 305 226   a   o 333   6 020 206 277 367   W 034  \n  \f
0000040 331 240 110 246 145 331 362 214 252 230 143 053 251 121 064 026
        331 240   H 246   e 331 362 214 252 230   c   + 251   Q   4 026

이 출력의 첫 번째 줄에 null이 삭제됩니다.

0000000 037 213 010 010 373 242 153 127 003 164 155 160 056 143 163 166
        037 213  \b  \b 373 242   k   W 003   t   m   p   .   c   s   v
0000020 305 226 141 157 333 066 020 206 277 367 127 034 012 014 331 240
        305 226   a   o 333   6 020 206 277 367   W 034  \n  \f 331 240
0000040 110 246 145 331 362 214 252 230 143 053 251 121 064 026 152 027
          H 246   e 331 362 214 252 230   c   + 251   Q   4 026   j 027

데이터가 변경되었으므로 더 이상 유효한 gzip 파일이 아니므로 오류가 발생합니다.

@coffemug가 언급했듯이 매뉴얼 페이지에서는 gzip이 -1gzip 형식이 아닌 파일에 대해 보고한다고 지적합니다. 그러나 입력은 더 이상 압축 파일이 아닙니다.어느형식이므로 매뉴얼 페이지는 어떤 의미에서는 오해의 소지가 있습니다. 매뉴얼 페이지는 이를 오류 처리로 분류하지 않습니다.

추가 자료:

@wildcard는 백슬래시와 같은 다른 문자가 문제를 더할 수 있다고 지적합니다. 의 일부 버전은 echo백슬래시를 이스케이프로 해석하여 다른 문자를 생성하기 때문입니다(또는 해당 레퍼토리에 없는 문자에 적용되는 이스케이프 처리에 따라 다름). . gzip(또는 대부분의 압축 형식)의 경우 다양한 바이트 값이 동일할 가능성이 높으며모두null은 생략됩니다.일부백슬래시를 사용하면 데이터가 수정됩니다.

이를 방지하는 방법은 압축 파일의 내용을 쉘 변수에 할당하지 않는 것입니다. 그렇게 하고 싶다면 더 적합한 언어를 사용하세요. 다음은 예를 들어 문자 빈도를 계산할 수 있는 Perl 스크립트입니다.

#!/usr/bin/perl -w

use strict;

our %counts;

sub doit() {
    my $file = shift;
    my $fh;
    open $fh, "$file" || die "cannot open $file: $!";
    my @data = <$fh>;
    close $fh;
    for my $n ( 0 .. $#data ) {
        for my $o ( 0 .. ( length( $data[$n] ) - 1 ) ) {
            my $c = substr( $data[$n], $o, 1 );
            $counts{$c} += 1;
        }
    }
}

while ( $#ARGV >= 0 ) {
    &doit( shift @ARGV );
}

for my $c ( sort keys %counts ) {
    if ( ord $c > 32 && ord $c < 127 ) {
        printf "%s:%d\n", $c, $counts{$c} if ( $counts{$c} );
    }
    else {
        printf "\\%03o:%d\n", ord $c, $counts{$c} if ( $counts{$c} );
    }
}

답변2

압축되지 않은 파일 크기에 대한 정보(실제로 gzip 파일이 함께 연결될 수 있으므로 마지막 청크의 압축되지 않은 크기)는 파일의 마지막 4바이트에 리틀 엔디안 32비트 정수로 저장됩니다.

해당 정보를 출력하기 위해 gzip -l파일 끝을 찾아 해당 4바이트를 읽습니다(실제로 에 따르면 strace마지막 8바이트, 즉 CRC 및 압축되지 않은 크기를 읽습니다).

그런 다음 파일 크기와 해당 숫자를 인쇄합니다. (주어진 정보는 오해의 소지가 있으며 gunzip < file.gz | wc -c연결된 gzip 파일의 경우와 동일한 결과를 제공하지 않는다는 것을 알게 될 것입니다.)

이제 파일을 검색할 수 있으면 작동하지만 파이프의 경우가 아닌 경우에는 작동하지 않습니다. 그리고 gzip그것을 감지하고 파일의 끝 부분에 도달하기 위해 파일을 완전히 읽을 만큼 똑똑하지 않습니다.

이제 다음과 같은 경우:

tmp="$(cat tmp.csv.gz)" && echo "$tmp" | gzip -l

또한 다른 쉘은 zsh변수에 NUL 바이트를 저장할 수 없고, $(...)모든 후행 개행 문자(0xa 바이트)를 제거하고 echo인수를 변환하고( 구현 에 따라 시작 -하거나 포함하는 경우 ) 추가 개행 문자를 추가하는 문제도 있습니다. .\echo

따라서 파이프로 작업할 수 있더라도 gzip -l수신되는 출력은 손상될 수 있습니다.

x86 시스템과 같은 Little Endian 시스템에서는 다음을 사용할 수 있습니다.

tail -c4 < file.gz | od -An -tu4

마지막 청크의 압축되지 않은 크기를 가져옵니다.

tail, 반대로 gzip입력을 찾을 수 없을 때 입력을 읽기 위해 되돌아갈 수 있습니다.

답변3

gzip파이프에서 입력을 받을 때 파일 이름을 인식할 수 없는 것 같습니다 . 저는 다음과 같은 테스트를 했습니다.

$ cat file.tar.gz | gzip -tv 
  OK

$ gzip -tv file.tar.gz
  file.tar.gz: OK

따라서 첫 번째 경우에는 gzip-l 플래그에 필요한 것으로 보이는 파일 이름을 인식할 수 없습니다(출력의 마지막 열에서 uncompressed_name이 stdout임을 볼 수 있음).

gzip매뉴얼 페이지 의 추가 정보(귀하의 질문과 직접적인 관련이 없음) :

압축되지 않은 크기는 압축된 .Z 파일과 같이 gzip 형식이 아닌 파일의 경우 -1로 지정됩니다. 이러한 파일의 압축되지 않은 크기를 얻으려면 다음을 사용할 수 있습니다.

     zcat file.Z | wc -c

관련 정보