就地压缩文件 - “gzip -c file | dd of=file”真的有效吗?

sle*_*ske 6 linux compression dd

在问题如何在不使用额外磁盘空间的情况下在 Linux 上就地压缩文件?,一个答案建议简单地使用

gzip -c file | dd of=file
Run Code Online (Sandbox Code Playgroud)

我尝试过(在 Debian Linux 上),它似乎确实有效。然而,我不太明白为什么。

dd在写入之前不截断其输出文件吗?这不会在 . 下“拉出地毯” gzip,从而带走 gzip 想要读取的数据吗?

或者是否涉及一些竞争条件,这意味着该命令通常会起作用,但有时可能会失败?或者它是否以某种方式取决于命令用于 I/O 的块大小?

我知道打开文件的进程可以继续读取它,即使另一个进程删除它(一旦进程关闭该文件将被丢弃)。如果文件在某个进程打开时被截断,是否有类似的机制?

Kei*_*son 4

实验表明这是行不通的

我从 中创建了一个 2 兆字节的文件/dev/urandom,然后对其尝试了上述命令。结果如下:

% 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
$ 
Run Code Online (Sandbox Code Playgroud)

显然,2 兆字节的随机文件不会压缩到 25 字节,事实上,gunzip在压缩文件上运行会生成一个空文件。

对于较小的随机文件(100 字节),我得到了类似的结果。

所以发生了什么事?

在这种情况下,命令在开始写入之前dd被截断为零字节;开始从新的空文件中读取并生成 25 个字节的输出,然后将其附加到空的. (空文件“压缩”到非零大小;理论上任何压缩器都不可能使所有输入变小)。filegzipddfile

gzip也可能出现其他结果,具体取决于、和 shell 进程的计时dd,所有这些进程都是并行运行的。

存在竞争条件,因为一个进程gzip从中读取file,而另一个并行进程(shell)向其中写入。

应该可以实现一个就地文件压缩器来读取和写入同一文件,使用任何必要的内部缓冲来避免破坏数据。但我从未听说过有人真正实现这一点,可能是因为它通常是不必要的,而且如果压缩器中途发生故障,文件将被永久损坏。