使用shutil.copytree时如何过滤目录?

21 投票
5 回答
19681 浏览
提问于 2025-04-17 04:43

有没有办法可以用绝对路径来过滤一个目录呢?

shutil.copytree(directory,
                target_dir,
                ignore = shutil.ignore_patterns("/Full/Path/To/aDir/Common")) 

当我尝试过滤位于“aDir”下的“Common”目录时,这似乎不起作用。如果我这样做:

shutil.copytree(directory,
                target_dir,
                ignore = shutil.ignore_patterns("Common"))

这样是可以的,但在那个“树”结构中,所有叫做Common的目录都会被过滤掉,这不是我想要的结果。

有什么建议吗?

谢谢。

5 个回答

3

你需要自己写一个忽略的函数,这个函数会检查当前正在处理的目录,如果这个目录是'/Full/Path/To/aDir',那么就返回一个只包含'Common'的列表。

def ignore_full_path_common(dir, files):
    if dir == '/Full/Path/To/aDir':
        return ['Common']
    return []

shutil.copytree(directory, target_dir, ignore=ignore_full_path_common)
4

shutil.ignore_patterns()这个功能不支持绝对路径,但其实自己写一个类似的功能非常简单。

作为起点,可以看看*ignore_patterns*的源代码:

def ignore_patterns(*patterns):
    """Function that can be used as copytree() ignore parameter.

    Patterns is a sequence of glob-style patterns
    that are used to exclude files"""
    def _ignore_patterns(path, names):
        ignored_names = []
        for pattern in patterns:
            ignored_names.extend(fnmatch.filter(names, pattern))
        return set(ignored_names)
    return _ignore_patterns

你会发现它返回一个函数,这个函数接受一个路径和一组名称,然后返回一组要忽略的名称。为了满足你的需求,可以创建一个类似的函数,利用path这个参数。然后把你的函数传给copytree()中的ignore参数。

另外,你也可以不直接使用shutil。它的源代码很简短,所以剪切、粘贴并进行自定义并不难。

19

你可以自己写一个忽略的函数:

shutil.copytree('/Full/Path', 'target',
              ignore=lambda directory, contents: ['Common'] if directory == '/Full/Path/To/aDir' else [])

或者,如果你想用相对路径来调用 copytree

import os.path
def ignorePath(path):
  def ignoref(directory, contents):
    return (
        f for f in contents
        if os.path.abspath(os.path.join(directory, f)) == path)
  return ignoref

shutil.copytree('Path', 'target', ignore=ignorePath('/Full/Path/To/aDir/Common'))

根据文档说明:

如果提供了忽略功能,它必须是一个可以调用的函数,这个函数会接收两个参数:一个是当前正在被 copytree() 访问的目录,另一个是这个目录下的内容列表,这个列表是通过 os.listdir() 得到的。因为 copytree() 是递归调用的,所以每复制一个目录,这个忽略函数就会被调用一次。这个函数必须返回一个相对于当前目录的目录和文件名的列表(也就是它第二个参数中的一部分);这些名字在复制过程中会被忽略。你可以使用 ignore_patterns() 来创建这样一个函数,它可以根据特定的模式来忽略文件名。

撰写回答