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
好吧,顯然輸入不一樣,但從邏輯上講,它應該是一樣的。我在這裡想念什麼?為什麼管道版本不起作用?
這個命令
$ tmp="$(cat tmp.csv.gz)" && echo "$tmp" | gzip -l
將 的內容分配給
tmp.csv.gz
shell 變數並嘗試使用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} ); } }