本文要点
要理解 copy 和 copy2 的差别,先看它们共同的底座 copyfile
copy 和 copy2 的差别,本质上是 copymode 和 copystat 的差别
即使你用 copy2,也不能指望元数据全部保留

本文目录
先看结论:copy保权限位,copy2再多保时间
如果你只想记住一句话:copy复制文件内容和权限位,copy2在copy的基础上再尝试保留更多元数据。
按 Python 官方 shutil 文档的说明,copy() 复制文件数据和文件的权限模式(permission mode),但创建时间和修改时间这类其他元数据不保留。文档还专门给了一句指引:要保留原文件的全部元数据,用 copy2()。
copy2() 的说明是:与 copy() 相同,区别在于它还会尝试保留文件元数据。它调用 copystat() 来复制元数据。
所以两者的差别不在文件内容,内容两者都复制;差别在元数据这一步做不做、做到什么程度。
一个前提:本文所有示例都是自建的练习文件,只操作练习目录,不碰你正在用的文件。动手前建议在练习目录里新建文件,例如 exercise/ 下面,不要拿重要文件直接试。
copyfile只搬内容:权限位和时间都不管
要理解 copy 和 copy2 的差别,先看它们共同的底座 copyfile。
按文档说明,copyfile(src, dst) 复制 src 的内容到 dst,括号里明确写了“no metadata”,也就是不复制元数据。它返回 dst,并且要求 dst 是完整的文件名,不能是一个目录路径。如果 src 和 dst 指向同一个文件,会抛出 SameFileError。
这里有两个容易踩的点。第一,dst 已经存在时会被替换,不是报错。第二,字符设备、块设备、管道这类特殊文件不能用这个函数复制。
所以三者的层次关系是:copyfile 只搬内容;copy 在搬内容之外再复制权限位;copy2 在 copy 之外再复制更多元数据。你选哪个函数,取决于你要不要保留权限位、要不要保留时间。
一个自建示例(练习数据,不是真实文件):在 exercise/ 下新建 src.txt,写入几行文字,然后分别用 copyfile、copy、copy2 复制到三个不同的目标文件名,观察三者的差别。
copymode和copystat:单独复制元数据的两个函数
copy 和 copy2 的差别,本质上是 copymode 和 copystat 的差别。
copymode(src, dst) 的说明是:把 src 的权限位复制到 dst。文件内容、所有者和组不受影响。也就是说,它只动权限位这一项。
copystat(src, dst) 的说明是:复制权限位、最后访问时间、最后修改时间和标志位(flags)。在 Linux 上,copystat() 还会尽可能复制扩展属性(extended attributes)。同样,文件内容、所有者和组不受影响。
把这两句和前面的 copy、copy2 对上:copy 做的是 copyfile 加 copymode;copy2 做的是 copyfile 加 copystat。copystat 覆盖的范围比 copymode 大,多出了访问时间、修改时间和标志位。
所以“权限位与修改时间分别保不保留”这个问题,可以拆成两问:权限位——copy 和 copy2 都保留;修改时间——只有 copy2 保留,copy 不保留。
一个边界:copystat 复制的是权限位、时间、标志位,它不复制文件内容,也不复制所有者和组。文档在 copystat 条目里明确写了 owner 和 group 不受影响。
跨平台会丢什么:所有者、ACL、资源分支、备用数据流
即使你用 copy2,也不能指望元数据全部保留。文档开头有一段警告,专门讲这件事。
警告的原文大意是:即使是 shutil.copy() 和 shutil.copy2() 这样的高层复制函数,也无法复制所有文件元数据。
具体到各平台,文档列了三组:
在 POSIX 平台上,文件所有者和组会丢失,ACL 也会丢失。
在 Mac OS 上,资源分支(resource fork)和其他元数据不被使用,这意味着资源会丢失,文件类型和创建者代码也不会正确。
在 Windows 上,文件所有者、ACL 和备用数据流(alternate data streams)不会被复制。
这三组信息回答的是同一个问题的不同侧面:copy2 能保住权限位和时间,但保不住所有者、ACL 这类与账户和权限体系绑定的信息,也保不住平台特有的附加数据。
一个自建示例(练习数据):在 exercise/ 下建一个文件,用 copy2 复制一份,然后对比两份文件的权限位和修改时间。这一步只用来观察 copy2 保住了什么,不用来推断所有者和 ACL 是否被复制,因为文档已经说明它们不会被复制。
符号链接怎么处理:follow_symlinks参数影响复制对象
copy、copy2、copyfile、copymode、copystat 都带一个 follow_symlinks 参数,默认是 True。它决定复制的是链接本身还是链接指向的文件。
按文档说明,copyfile 在 follow_symlinks 为 false 且 src 是符号链接时,会创建一个新的符号链接,而不是复制 src 指向的文件。copy 和 copy2 的说明里也有对应表述:follow_symlinks 为 false 且 src 是符号链接时,dst 会被创建为符号链接;为 true 时,dst 是 src 指向文件的副本。
copystat 在 follow_symlinks 为 false 且 src、dst 都指向符号链接时,会操作符号链接本身,从 src 链接读信息、写到 dst 链接。
这里有一个重要的限制:不是所有平台都支持检查和修改符号链接。文档给了一个自查方法——如果 os.chmod 在 os.supports_follow_symlinks 里为 True,copystat 就能修改符号链接的权限位;os.utime 和 os.chflags 同理。在不支持这些功能的平台上,copystat 会复制它能复制的部分,并且文档明确写了 copystat 从不返回失败。
一个边界:copy2 在无法保留元数据时不会抛异常,它会保留能保留的部分。所以“copy2 一定保住全部元数据”这个理解是不对的。
怎么选:按你要不要保留修改时间来决定
回到最初的问题,选择依据可以整理成一张对照。
如果你只需要文件内容对得上,用 copyfile。它不复制权限位,也不复制时间。
如果你需要目标文件的权限位和源文件一致,用 copy。它复制内容和权限位,不保留修改时间。
如果你还需要目标文件的修改时间和访问时间与源文件一致,用 copy2。它在 copy 的基础上调用 copystat,多复制了时间、标志位,在 Linux 上还尽可能复制扩展属性。
如果你只想单独调整某个已有文件的权限位,用 copymode。如果你只想单独把权限位、时间、标志位搬到另一个文件上,用 copystat。
一个自建示例(练习数据,不是真实操作记录):在 exercise/ 下建 src.txt,用 copy 复制成 a.txt,用 copy2 复制成 b.txt,然后分别查看 a.txt 和 b.txt 的权限位和修改时间。这一步只用来观察两个函数在元数据上的差别,不涉及删除或覆盖你原有的文件。
一个提醒:dst 已存在时会被替换,所以动手前先确认目标文件名没有被占用,或者先在练习目录里操作。
观看知识要点视频 18 秒
18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。
参考资料
以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。
请结合原始资料的适用条件阅读。
返回文件整理 →