使用单个文件名过滤 tarfile.add

Filtering tarfile.add using individual file names

在使用 tarfile.add 添加目录时,是否可以访问 filter lambda 中的各个文件名?

我正在使用 tarfile 模块来创建项目目录的存档。其中一些文件我不再需要,我想忽略:

myproj/  # example; actual project directory structure much deeper
    importantfile.txt
    semi-importantfile.doc
    useless-file.exe  # ignore this one

我现在正在做的是使用tarfile.addexclude参数来跳过useless-file.exe

import tarfile

with tarfile.open('mytar.tar', 'w') as mytar:
    mytar.add('myproj', exclude=lambda x: os.path.basename(x) == 'useless-file.exe')

我知道 exclude 现已弃用,为了面向未来,我正在尝试改用新的 filter 参数。

    mytar.add('myproj', filter=lambda x: (
                                x if x.name != 'useless-file.exe'
                                else None))

但是,这样做最终会将 useless-file.exe 添加到压缩包中。通过一些测试,我发现这是因为,虽然 exclude 被递归地提供目录的名称及其所有内容,但 filter 仅获得显式添加的文件的 TarInfo(在此案例,目录myproj)

那么有没有一种方法可以使用 filter 来复制我在 exclude 中的行为?如果可能的话,我真的宁愿不递归地遍历我的所有目录只是为了检查我没有添加任何不需要的文件。

解决方案说明

请参阅@larsks 的回答以获得对该问题的完整解释。我的问题是在使用 exclude 时,我在 x 上调用了 os.path.basename(请参阅上面编辑的代码),但在使用 filter 时我忘记在 x.name 上执行此操作。

我认为 filter 方法的行为与您认为的不同。例如,如果我的目录结构如下所示:

example/
  file0.1
  file0.2
  dir1/
    file1.1
    file1.2

我运行下面的代码:

import tarfile

def myfilter(thing):
    print('myfilter called for {thing.name}'.format(thing=thing))
    return thing

t = tarfile.open('archive.tar', mode='w')
t.add('example', recursive=True, filter=myfilter)

我看到输出:

myfilter called for example
myfilter called for example/file0.1
myfilter called for example/file0.2
myfilter called for example/dir1
myfilter called for example/dir1/file1.1
myfilter called for example/dir1/file1.2

也就是说,每个添加到存档的项目都会调用一次过滤器。如果想排除 example/dir1/file1.1,我会写一个看起来像这样的过滤函数:

def exclude_file1(thing):
    if thing.name != 'example/dir1/file1.1':
        return thing

在上面的示例中使用它作为过滤器时,生成的存档包含:

$ tar tf archive.tar 
example/
example/file0.1
example/file0.2
example/dir1/
example/dir1/file1.2

(编辑:上面的例子是用Python 3.5测试的)