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} );
    }
}

おすすめ記事