bash 与 python 中的 gzip

gzip in bash vs python

在Bash中,当你gzip一个文件时,原始文件不会被保留,而在Python中,你可以像这样使用gzip库(如here中所示“使用示例”部分):

import gzip
import shutil
with open('/home/joe/file.txt', 'rb') as f_in:
    with gzip.open('/home/joe/file.txt.gz', 'wb') as f_out:
        shutil.copyfileobj(f_in, f_out)

默认情况下,这会保留原始文件。我找不到在压缩时不保留它的方法。我是否必须等到 gzip 完成后才能删除文件?

考虑到 GZip 运行时(在 Bash 或其他任何地方):

  • GZip 需要原始数据才能执行压缩操作
  • GZip 旨在处理基本上任意大小的数据
  • 因此:GZip 不太可能在内存中创建临时文件,而是几乎肯定会在 gzip 完成后删除原始文件。

考虑到这些要点,您的代码的相同策略是执行 gzip,然后 删除文件。

当然,删除文件并不麻烦 — 有几种方法可以做到这一点 — 当然,您可以将整个事情打包在一个过程中,这样您就再也不必担心了。

如果你在类unix系统上,你可以在打开文件后取消链接,这样它就不会再出现在文件系统中。但在您关闭现在匿名的文件之前,它仍会占用磁盘 space。

import gzip
import shutil
import os
with open('deleteme', 'rb') as f_in:
    with gzip.open('deleteme.gz', 'wb') as f_out:
        os.unlink('deleteme') # *after* we knew the gzip open worked!
        shutil.copyfileobj(f_in, f_out)

据我所知,这不适用于 Windows。您需要在压缩过程完成后进行删除。您可以将其名称更改为 "thefile.temporary" 之类的名称,甚至可以将其移动到不同的目录(如果目录是相同的文件系统,则速度很快,但如果目录不同,则复制)。

下面的代码(部分基于 tdelaney 的回答)将执行以下操作:

  • 读取文件,即时压缩,并将所有压缩数据存储在内存中
  • 删除输入文件
  • 然后写入压缩数据

这是针对文件系统已满的用例,这会阻止您在磁盘上存在未压缩文件的同时写入压缩数据。为了解决这个问题,因此有必要将所有数据存储在内存中(除非您可以访问外部存储),但为了尽可能减少内存成本,只有 compressed 数据完全存储在内存中,而 未压缩的 数据以块的形式读取。

如果程序在删除输入文件和完成将压缩数据写入磁盘之间中断,当然存在数据丢失的风险。

如果内存不足也有可能失败,但是输入文件不会被删除,因为MemoryError会在到达os.unlink之前产生。

值得注意的是,这不是具体回答了问题的要求,即在删除输入文件的同时仍然从中读取。这在类 unix 操作系统下是可能的,但与常规命令行 gzip 行为相比,这样做没有实际优势,因为在文件关闭之前释放磁盘 space 仍然不会发生,因此它会在发生故障时牺牲可恢复性,而不会获得任何额外的 space 来处理数据以换取这种牺牲。 (仍然 需要磁盘 space 才能使未压缩和压缩的数据共存。)

import gzip
import shutil
import os
from io import BytesIO

filename = 'deleteme'

buf = BytesIO()

# compress into memory - don't store all the uncompressed data in memory
# but do store all the compressed data in memory
with open(filename, 'rb') as fin:
    with gzip.open(buf, 'wb') as zbuf:
        shutil.copyfileobj(fin, zbuf)

# sanity check for already compressed data
length = buf.tell()
if length > os.path.getsize(filename):
    raise RuntimeError("data *grew* in size - refusing to delete input")

# delete input file and then write out the compressed data
buf.seek(0)
os.unlink(filename)
with open(filename + '.gz', 'wb') as fout:
    shutil.copyfileobj(buf, fout)