Python:使用tarfile从TAR归档中删除文件

4

是否可以使用tarfile从TAR归档中删除某些文件?

例如:

如果一个x.tar文件包含文件a.txtb.txtc.txt,是否可以删除a.txt

换句话说:是否存在任何Python解决方案来实现类似于以下命令的操作:

tar -vf x.tar --delete a.txt

3个回答

2
实际上,这是可能的...但有很大的限制。您只能删除存档的末尾/尾部,而不能删除开头或中间的文件。
我曾经有过类似的需求,需要从一个巨大的tar(450G)中提取文件,但空间不足以同时存放tar和提取后的文件。我不得不逐个提取文件,并在提取后立即从.tar中删除它们。
命令tar -vf x.tar --delete a.txt并不能解决这个问题,因为它并没有从x.tar中删除a.txtx.tar的大小保持不变),它只是将其从包含文件列表中删除(当解压缩x.tar时,a.txt将不会被提取)。
由于.tar文件是顺序的,因此您可以做的唯一一件事情就是截断它们。因此,唯一的解决方案是从末尾提取文件。
首先,您需要获取tar文件的所有成员列表:
with tarfile.open(name=tar_file_path, mode="r") as tar_file:
     tar_members = tar_file.getmembers()

然后你可以从末尾提取你想要的文件:

with tarfile.open(name=tar_file_path, mode="r") as tar_file:
     tar_file.extractall(path = extracting_dir, members = tar_members[first_of_files_to_extract:])

你需要计算在哪里(以字节为单位)截断文件:
truncate_size = tar_members[first_of_files_to_extract].offset

然后添加“文件结束”标记,即两个连续的空块。在.tar中,每个块长512字节,因此您需要在末尾有1024个Null字节。这里,仅供记录,您可以添加512字节(一个块),因为上一个tar_member已经以512字节的Null块(tar_member结束标记)结束。
new_file_size = truncate_size + 1024 # 2 blocs of 512 Null bytes 

最后,您需要进行截断操作,首先是为了删除最后的成员,其次是为了添加空字节(这里不再使用tarfile.open()打开.tar文件,截断只是常规的文件操作):

with open(tar_file_path) as tar_file:
    tar_file.truncate(truncate_size)
    tar_file.truncate(new_file_size)

你从 .tar 文件的末尾提取了文件,并得到了一个新的有效的 .tar 文件,它比之前的文件小了提取文件的大小加上一些块字节大小,并且你限制了额外的内存使用量来适应提取文件的大小:我个人是逐个文件执行此操作的(提取最后一个文件,截断,提取最后一个文件,截断等等)。


太棒了!非常感谢! - undefined

2

直接使用tarfile是不行的,不过可能有其他库可以实现。一个快速的方法是先解压文件,然后重新创建tar文件时排除你想要删除的文件。


2
另一个快速的 hack:os.system('tar -vf x.tar --delete a.txt') - Andrei Cioara
1
我考虑过这种方法,但我更愿意先检查其他选项。无论如何 - 非常感谢你。 - jwalkiew

1
我曾经遇到类似的问题,最终使用了7z命令行(7za.exe),因为它支持比Python的tarfile更多的功能,包括从归档中删除文件。
这种解决方案的缺点是需要将7za.exe文件与程序一起携带。
在你的情况下,可以使用类似于以下内容的东西:
os.system("7za d x.tar a.txt")

请注意,os.system已被弃用,您应该使用subprocess。我从未使用过它,所以无法提供更多帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接