有效地从大 .tgz 中删除文件

Aks*_*ert 14 tar gzip

假设我有一个 gzip 压缩的 tar-ballcompressedArchive.tgz(+100 个文件,总计 +5gb)。

删除与给定文件名模式匹配的所有条目(例如 prefix*.jpg),然后将剩余部分再次存储在 gzip:ed tar-ball 中的最快方法是什么?

替换旧存档或创建新存档并不重要,以最快的为准。

Sté*_*las 15

使用 GNU tar,您可以执行以下操作:

pigz -d < file.tgz |
  tar --delete --wildcards -f - '*/prefix*.jpg' |
  pigz > newfile.tgz
Run Code Online (Sandbox Code Playgroud)

bsdtar

pigz -d < file.tgz |
  bsdtar -cf - --exclude='*/prefix*.jpg' @- |
  pigz > newfile.tgz
Run Code Online (Sandbox Code Playgroud)

pigz作为 的多线程版本gzip)。

您可以覆盖文件本身,例如:

{ pigz -d < file.tgz |
    tar --delete --wildcards -f - '*/prefix*.jpg' |
    pigz &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file.tgz
Run Code Online (Sandbox Code Playgroud)

但这相当危险,特别是如果结果最终比原始文件压缩得少(在这种情况下,第二个pigz可能最终覆盖第一个尚未读取的文件区域)。


Gil*_*il' 9

不要轻视简单的方法:它可能足够快满足您的目的。使用avfs将存档作为目录访问:

cd ~/.avfs/path/to/original.tar.gz\#
pax -w -s '/^.*\.jpg$//' | gzip >/path/to/filtered.tar.gz        # POSIX
tar -czf /path/to/filtered.tar.gz -s '/^.*\.jpg$//' .            # BSD
tar -czf /path/to/filtered.tar.gz --transform '/^.*\.jpg$//' .   # GNU
Run Code Online (Sandbox Code Playgroud)

使用更原始的工具,首先提取不包括.jpg文件的文件,然后创建一个新的存档。

mkdir tmpdir && cd tmpdir
<original.tar.gz gzip -d | pax -r -pe -s '/^.*\.jpg$//'
pax -w . | gzip >filtered.tar.gz
cd .. && rm -rf tmpdir
Run Code Online (Sandbox Code Playgroud)

如果您的 tar 有--exclude

mkdir tmpdir && cd tmpdir
tar -xzf original.tar.gz --exclude='*.jpg'
tar -czf filtered.tar.gz .
cd .. && rm -rf tmpdir
Run Code Online (Sandbox Code Playgroud)

但是,如果您不以 root 身份运行它,这可能会破坏文件所有权和模式。为获得最佳效果,请在快速文件系统上使用临时目录 — tmpfs 如果您有足够大的文件系统。

对存档器作为传递(即读取存档和写入存档)的支持往往是有限的。GNU tar 可以使用--delete操作选项从存档中删除成员(“--delete据报告,该选项tar作为过滤器从stdinstdout.”时可以正常工作),这可能是您最好的选择。

您可以使用几行 Python 代码创建强大的存档过滤器。它的tarfile库可以从不可搜索的流中读取和写入,你可以使用 Python 中的任意代码来过滤、重命名、修改……

#!/usr/bin/python
import re, sys, tarfile
source = tarfile.open(fileobj=sys.stdin, mode='r|*')
dest = tarfile.open(fileobj=sys.stdout, mode='w|gz')
for member in source:
    if not (member.isreg() and re.match(r'.*\.jpg\Z', member.name)):
        sys.stderr.write(member.name + '\n')
        dest.addfile(member, source.extractfile(member))
dest.close()
Run Code Online (Sandbox Code Playgroud)