shutil.copy2() 比 shutil.copy() 多复制文件时间戳(atime/mtime/ctime),而 copy() 仅复制内容和权限;copy2() 在目标不支持或权限不足时会静默丢弃时间戳,且不修改 owner/group。

shutil.copy() 和 shutil.copy2() 的核心区别在哪
关键就一条:shutil.copy() 只复制文件内容和权限(mode),不碰修改时间、访问时间这些元数据;shutil.copy2() 会把 st_atime、st_mtime、st_ctime 也一并拷过去——前提是源文件系统支持且 Python 有权限读取。
常见错误现象:用 copy() 备份日志或配置后,发现 ls -l 时间全变成当前时间,导致依赖 mtime 的脚本(比如 Makefile 或 rsync 增量判断)失效。
- 如果只是临时搬运、不关心时间戳,
copy()足够,略快一点点 - 做备份、归档、CI 构建产物分发,必须用
copy2() - Windows 上 ctime 是“创建时间”,Linux 是“状态变更时间”,
copy2()都会尽力保留,但目标文件的 ctime 总是会被更新为复制时刻(这是 OS 限制,不是函数问题)
copy2() 在哪些情况下会静默丢掉元数据
不是所有元数据都能“复制成功”。copy2() 底层调用 os.stat() + os.utime(),一旦后者失败,它不会报错,而是直接跳过时间戳设置——你根本不知道丢了。
典型场景:
- 目标文件系统是 FAT32 或 exFAT(不支持纳秒级时间戳,甚至不存 atime)
- 目标路径在 NFS 或某些容器挂载卷上,
utime()权限被服务端拒绝 - 以普通用户身份向 root-owned 目录复制,没权限改 atime/mtime
验证方法:复制前后分别对源和目标运行 stat -c "%y %x %z" filename(Linux)或 Get-Item filename | Select LastWriteTime,LastAccessTime,CreationTime(PowerShell),对比输出。
替代方案:什么时候该绕开 shutil,改用 subprocess 调用 cp
当你要 100% 确保元数据完整,且运行环境可控(比如 Linux 服务器),subprocess.run(["cp", "-p", src, dst]) 比 copy2() 更可靠。“-p” 参数等价于 “--preserve=mode,ownership,timestamps”,还额外保留 owner/group(shutil.copy2() 默认不碰 uid/gid)。
-
shutil.copy2()永远不会改 owner/group,即使你有权限 -
cp -p在目标目录权限允许时,会尝试还原 uid/gid;失败也不报错,只丢 ownership - 注意:Python 3.8+ 的
shutil.copy2()加了follow_symlinks=False参数,但依然不处理 symlink 自身属性;cp -p对软链本身也能 preserve
跨平台复制时容易忽略的细节
Windows 和 Linux 对“可执行权限”的理解完全不同。Linux 下 copy2() 会复制 mode(含 x 位),但 Windows 文件系统(NTFS)没有对应概念——结果就是目标文件在 WSL 或 Cygwin 里可能莫名丢失执行权限。
- 如果目标是给 Git Bash 或 WSL 用,建议复制完手动
os.chmod(dst, 0o755) - Windows 原生 Python 调用
copy2()时,st_atime可能被强制设为 0(取决于 NTFS mount 选项),这时copy2()实际没生效 - 用
pathlib.Path(src).copyfile(dst)(Python 3.8+)本质还是调shutil.copy(),别以为换了 API 就更高级
元数据这事,从来不是“调对函数就完事”。操作系统、文件系统、挂载参数、用户权限,层层叠叠漏一点,时间戳就悄悄变了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











