std::filesystem::copy_file默认不保留文件属性(如权限、时间戳、所有权),仅保证内容一致;需手动调用permissions()和last_write_time()分别设置权限与修改时间,且顺序必须为先复制、再设权限、最后改时间戳。

copy_file 会保留文件属性吗
std::filesystem::copy_file 默认**不保留时间戳、权限位(如可执行位)、所有权等属性**,它只保证内容一致。如果你用 copy_file(src, dst) 而不加 flag,目标文件的 last_write_time 是复制时刻,permissions 则由当前 umask 或系统默认决定,和源文件无关。
常见错误现象:复制后脚本变不可执行、构建系统因时间戳变更触发全量重编译、CI 环境里权限丢失导致运行失败。
- 必须显式传入
copy_options::skip_symlinks或copy_options::overwrite_existing等 flag,否则默认行为是“仅当 dst 不存在时才复制”,容易静默失败 - 要保留权限,得额外调用
permissions(dst, status(src).permissions()) - 要保留修改时间,得手动
last_write_time(dst, last_write_time(src))
完整复制(内容 + 权限 + 时间戳)怎么写
标准库没提供“一键全量复制”的函数,得组合三步操作。注意顺序:先复制内容,再设权限,最后改时间戳——反过来可能导致权限被系统重置(比如某些平台在 chmod 后自动更新 mtime)。
namespace fs = std::filesystem; fs::copy_file(src, dst, fs::copy_options::overwrite_existing); fs::permissions(dst, fs::status(src).permissions()); fs::last_write_time(dst, fs::last_write_time(src));
使用场景:打包工具、配置文件同步、测试 fixture 初始化。如果 src 是符号链接,copy_file 默认会解引用(即复制目标文件),若想复制链接本身,需加 fs::copy_options::copy_symlinks。
立即学习“C++免费学习笔记(深入)”;
-
fs::status(src).permissions()返回的是原始权限位,含owner_read、owner_exec等,不是整数掩码,直接传给permissions()即可 - Windows 上
last_write_time精度为 100ns,Linux 通常是纳秒级,但std::filesystem会做平台适配,不用手动对齐 - 若 src 权限含
fs::perms::set_uid等特殊位,普通用户进程通常无权设置,调用permissions()会抛filesystem_error
为什么 copy_options::recursive 不适用于单文件
copy_options::recursive 只对目录有效,对单个文件传入该 flag 会被忽略,不会报错也不会警告。有人误以为加上它就能“深度保留属性”,其实完全无关。
容易踩的坑:在循环里对每个文件都写 copy_file(f, d, opts | recursive),既无效又误导后续维护者。真正需要递归时,应改用 copy(src_dir, dst_dir, copy_options::recursive | copy_options::overwrite_existing),它内部会对每个条目分别处理权限和时间戳——但注意,copy() 对文件的处理逻辑仍不保证 100% 保留所有属性(例如不设 atime,也不复制扩展属性)。
-
copy()函数对子目录内文件的权限设置依赖于fs::status()结果,但如果源文件被其他进程瞬时修改,可能读到中间状态 - 它不会复制 ACL、xattr、forks(macOS)、reparse points(Windows)等扩展属性,这些需平台特定 API(如
ioctl、GetFileInformationByHandleEx)
跨平台兼容性要注意什么
Linux/macOS 上 permissions() 能正确映射 rwx 位;Windows 用 GENERIC_READ/WRITE 模拟,但无法表达“组可执行”这类概念,且 owner_exec 在 NTFS 上实际映射为“允许执行”,受 UAC 和策略限制。
更隐蔽的问题:last_write_time() 在 FAT32 上精度只有 2 秒,且不能设早于 1980-01-01 的时间;某些嵌入式文件系统甚至不支持修改时间戳。调用后建议检查是否真的生效:
auto t1 = fs::last_write_time(src);
fs::last_write_time(dst, t1);
auto t2 = fs::last_write_time(dst);
if (t2 != t1) { /* 处理不支持场景 */ }
复杂点在于:没有统一方式判断“某个属性是否被真正应用”。权限和时间戳的写入都是尽力而为,失败时抛异常,但异常类型(std::system_error vs filesystem_error)和错误码(EPERM、EROFS、ENOTSUP)需按平台分别处理。


















