web-dev-qa-db-ja.com

ファイルをその場で圧縮する-「gzip-cfile | dd of = file」は実際に機能しますか?

質問 追加のディスクスペースを使用せずに、Linuxでファイルをインプレースで圧縮するにはどうすればよいですか? 、1つの答えは単に使用することを提案します

gzip -c file | dd of=file

(Debian Linuxで)試してみましたが、動作しているようです。しかし、その理由はよくわかりません。

ddは、書き込む前に出力ファイルを切り捨てませんか?これはgzipの下の「敷物を引き抜く」ので、gzipが読み取りたいデータを削除しませんか?

または、何らかの競合状態が関係していますか?つまり、コマンドは通常は機能しますが、失敗する場合がありますか?それとも、コマンドがI/Oに使用するブロックサイズに何らかの形で依存しますか?

ファイルを開いたプロセスは、別のプロセスがファイルを削除しても、そのファイルを読み続けることができることを知っています(プロセスがファイルを閉じると、ファイルは破棄されます)。一部のプロセスでファイルが開かれているときにファイルが切り捨てられた場合、同様のメカニズムはありますか?

6
sleske

実験は、これがnot機能することを示しています。

/dev/urandomから2メガバイトのファイルを作成し、上記のコマンドを試してみました。結果は次のとおりです。

% ls -l
total 41008
-rw-r--r-- 1 kst kst 20971520 2012-01-18 03:47 file
-rw-r--r-- 1 kst kst 20971520 2012-01-18 02:48 orig
% gzip -c file | dd of=file
0+1 records in
0+1 records out
25 bytes (25 B) copied, 0.000118005 s, 212 kB/s
% ls -l
total 20508
-rw-r--r-- 1 kst kst       25 2012-01-18 03:47 file
-rw-r--r-- 1 kst kst 20971520 2012-01-18 02:48 orig
$ 

明らかに、2メガバイトランダムファイルは25バイトに圧縮されず、実際、圧縮ファイルでgunzipを実行すると、空のファイルが生成されます。

はるかに小さいランダムファイル(100バイト)でも同様の結果が得られました。

どうしたの?

この場合、ddコマンドは書き込みを開始する前にfileをゼロバイトに切り捨てました。 gzipは、新しく空になったファイルからの読み取りを開始し、25バイトの出力を生成し、それをddが空のfileに追加しました。 (空のファイルはゼロ以外のサイズに「圧縮」されます。どのコンプレッサーでもall入力を小さくすることは理論的に不可能です)。

gzipdd、およびシェルプロセスのタイミングによっては、他の結果が生じる可能性があります。これらはすべて並行して実行されます。

1つのプロセスgzipfileから読み取り、別の並列プロセスであるShellがそれに書き込むため、競合状態が発生します。

データの破壊を回避するために必要な内部バッファリングを使用して、同じファイルの読み取りと書き込みを行うインプレースファイルコンプレッサーを実装できるはずです。しかし、実際にそれを実装している人のことは聞いたことがありません。おそらく、通常は必要ないため、コンプレッサーが途中で失敗すると、ファイルが永続的に破損するためです。

4
Keith Thompson