本文要点
把 dirs_exist_ok 设为 True,报错条件就解除了
ignore_patterns 不是直接过滤文件,它返回一个函数,这个函数交给 copytree 的 ignore 参数使用
ignore_patterns 接收的是 glob 风格模式,匹配对象是目录里的名字

本文目录
先判断报错来自哪一步:目标已存在还是复制中途出错
如果你在练习目录里运行 copytree 时看到 FileExistsError,先分清它出现在哪个阶段。
按 Python 官方 shutil 文档,copytree 的 dirs_exist_ok 参数默认是 False。当它为 False 且 dst 已经存在时,会抛出 FileExistsError。这是复制开始前就发生的判断,不是复制到一半才出的问题。
所以排查顺序是:先看报错类型是不是 FileExistsError,再看 dst 这个路径是不是已经存在。如果目标目录本来就在,报错属于预期行为,不是源目录有问题。
一个自建情境(示例,不是对真实项目的记录):在练习目录 exercise/ 下建 src_demo/ 和 dst_demo/ 两个目录,先手动建好 dst_demo/,再运行 copytree('exercise/src_demo', 'exercise/dst_demo')。这时会得到 FileExistsError。动手前建议把练习目录单独放,不要拿正在用的目录直接试。
dirs_exist_ok=True 改变的是什么:继续复制并覆盖同名文件
把 dirs_exist_ok 设为 True,报错条件就解除了。按文档说明,此时复制操作遇到已存在的目录会继续,dst 目录树中的文件会被 src 目录树中对应的文件覆盖。
这里要抓住两点。第一,它解除的是“目标目录已存在”这个前置拦截,不是解除所有错误。第二,覆盖发生在文件层面:src 里有同名文件,dst 里那份会被替换。
文档没有说明 dirs_exist_ok=True 会删除 dst 里多出来的文件。所以不要把“继续复制”理解成“让 dst 和 src 完全一致”。dst 中 src 没有的文件,按资料没有写会被删除,本文也不补这个结论。
自建示例(练习数据):
from shutil import copytree
copytree(
'exercise/src_demo',
'exercise/dst_demo',
dirs_exist_ok=True,
)运行前先确认 dst_demo 里没有你需要保留的旧文件,因为同名文件会被覆盖。
ignore_patterns 生成的是什么:一个按目录调用的回调
ignore_patterns 不是直接过滤文件,它返回一个函数,这个函数交给 copytree 的 ignore 参数使用。
按文档说明,ignore_patterns(*patterns) 是一个工厂函数,创建一个可调用对象,用作 copytree 的 ignore 参数,忽略匹配所给 glob 风格模式的文件和目录。
ignore 回调的调用方式也有明确规定:copytree 递归复制时,每访问一个目录就调用一次这个回调,传入两个参数——正在访问的目录,以及该目录内容列表(由 os.listdir 返回)。回调必须返回一组相对于当前目录的文件名和目录名,这些名字会在复制中被忽略。
所以 ignore_patterns 的工作方式是:你给它模式,它生成一个回调;copytree 每进一个目录就问一次这个回调“这里哪些名字要跳过”。
模式怎么写:glob 风格,匹配的是名字不是完整路径
ignore_patterns 接收的是 glob 风格模式,匹配对象是目录里的名字。
官方文档给的例子是:
from shutil import copytree, ignore_patterns
copytree(
source,
destination,
ignore=ignore_patterns('*.pyc', 'tmp*'),
)文档对这个例子的说明是:复制除 .pyc 文件和名字以 tmp 开头的文件或目录之外的所有内容。
从这句说明能确认两件事。第一,模式可以同时匹配文件和目录,tmp* 既可能命中文件也可能命中目录。第二,模式按名字匹配,不是按从源根目录算起的完整路径匹配。
一个自建练习(示例):在 exercise/src_demo/ 下放 a.pyc、keep.txt、tmp_note.txt 三个文件,用上面的 ignore_patterns('*.pyc', 'tmp*') 复制到 exercise/dst_demo/。按文档描述,dst_demo 里应只剩 keep.txt。这是按文档规则推演的练习,不是对某台机器的实测记录。
两个参数一起用:先过忽略回调,再决定是否覆盖
dirs_exist_ok 和 ignore 管的是两件不同的事,可以同时传。
dirs_exist_ok 管的是“目标目录已存在时要不要继续”,ignore 管的是“源目录里哪些名字不参与复制”。两者不冲突:先由 ignore 回调决定每个目录里跳过哪些名字,剩下的内容再按 dirs_exist_ok 的设定处理目标已存在的情况。
自建示例(练习数据):
from shutil import copytree, ignore_patterns
copytree(
'exercise/src_demo',
'exercise/dst_demo',
dirs_exist_ok=True,
ignore=ignore_patterns('*.pyc', 'tmp*'),
)这段代码的含义是:dst_demo 已存在也继续复制;src_demo 里的 .pyc 文件和 tmp 开头的名字不复制;其余同名文件覆盖 dst_demo 里的对应文件。
检查结果时,可以对照两件事:dst_demo 里有没有出现本应被忽略的名字;src_demo 和 dst_demo 同名的文件,内容是不是 src 那一份。
出错时先看 Error 里的三元组,再回头核对参数
如果复制过程中出现异常,文档说明 copytree 会抛出 Error,其参数是一个列表,列表里每项是 (srcname, dstname, exception) 这样的三元组。
排查时按这个结构读:先看 srcname 和 dstname 指向哪一对路径,再看 exception 是什么类型。这样能分清问题出在某个具体文件上,还是出在参数设置上。
两个常见方向:
1. 报 FileExistsError 且没有传 dirs_exist_ok=True,说明目标目录已存在这个前置条件没处理。
2. 某个文件没被复制,先确认它的名字是否被 ignore_patterns 的模式命中。
文档还提到,如果 symlinks 为 false 且符号链接指向的文件不存在,异常会被加入 Error 列表;ignore_dangling_symlinks 设为 true 可以消除这个异常。这一点只在你处理符号链接时相关,普通文件复制不涉及。
本文只讲目标目录已存在时的报错条件、dirs_exist_ok 的作用和 ignore_patterns 生成回调的方式,不涉及删除策略,也不涉及其他文件操作函数。
观看知识要点视频 18 秒
18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。
参考资料
以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。
请结合原始资料的适用条件阅读。
返回文件整理 →