使用单个文件名过滤 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.add
的exclude
参数来跳过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测试的)
在使用 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.add
的exclude
参数来跳过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测试的)