Gzip

gzip相同的輸入不同的輸出

  • June 23, 2016

查看:

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

好吧,顯然輸入不一樣,但從邏輯上講,它應該是一樣的。我在這裡想念什麼?為什麼管道版本不起作用?

這個命令

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

將 的內容分配給tmp.csv.gzshell 變數並嘗試使用echo管道將其傳遞給gzip. 但是 shell 的功能妨礙了(省略了空字元)。您可以通過測試腳本看到這一點:

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

在此輸出的第一行中刪除一個空值:

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 將報告非 gzip-1格式的文件。但是,輸入不再是任何格式的壓縮文件,因此手冊頁在某種意義上具有誤導性:它沒有將其歸類為錯誤處理。

進一步閱讀:

@wildcard 指出其他字元(例如反斜杠)可能會增加問題,因為某些版本echo會將反斜杠解釋為轉義並產生不同的字元(或不,取決於對不在其曲目中的字元的轉義處理) . 對於 gzip(或大多數形式的壓縮),各種字節值的可能性相同,因為所有空值都將被省略,而一些反斜杠將導致數據被修改。

防止這種情況的方法是不要嘗試將壓縮文件的內容分配給 shell 變數。如果您想這樣做,請使用更適合的語言。這是一個可以計算字元頻率的 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} );
   }
}

引用自:https://unix.stackexchange.com/questions/291562