备份完一批配置,ls -l 一看修改时间全变成刚才,第二天 rsync 同步时又把整个目录重传了一遍。原因就是复制时少写了一个 -p。

-p 保住的是三样东西

cp -p 会尽量保留源文件的权限位、属主属组和三个时间戳。不加这个参数,新文件的修改时间就是复制那一刻,备份工具靠时间戳判断文件有没有变,自然会把整批都当成新的,重传一遍。

  • cp -p app.conf /backup/app.conf
  • cp -rp config/ /backup/config/

目录要连内容一起复制,得配 -r,单独 -p 只作用在目录本身,里面的文件还是新时间。顺序无所谓,写成 -rp 或 -pr 都对。

普通用户改不动属主

用非 root 账号执行 cp -p,权限位和时间能保住,属主却会变成当前用户,因为改别人文件的 owner 需要特权。脚本里如果靠属主判断归属,跨用户复制后就全错了。真要原样保留,用 root 执行,或者改用 rsync -a,它对权限、时间、属主的处理更完整。

ACL 和 SELinux 标签不在 -p 的范围里

开了 ACL 的目录,-p 不会带过去,要显式加 --preserve=all。CentOS 上还有 SELinux 上下文,复制到站点目录后页面报 403,就是上下文没跟上,用 cp -Z,或者复制完补一次 restorecon。macOS 自带的是 BSD 版 cp,参数和 GNU 版有出入,跨平台脚本别依赖 -a,写 -p 更保险。

什么时候干脆别用 cp

要镜像整个目录、下次还要增量同步的,直接 rsync -a 更合适,它默认保留这些属性,还能断点续传、排除指定文件。只在临时拷一两个文件、又想留住时间的时候,cp -p 才最顺手。

递归复制时的软链接要留意

目录里如果有指向其他位置的符号链接,cp -r 默认会把链接当成普通文件去读,目标不存在就直接报错,跨文件系统时还会把链接展开成实体文件,体积翻倍。加 -d 可以保留链接本身,配合 -p 写成 cp -dp 更接近原样。批量迁移前先 find 一下有多少符号链接,心里有数。

结论:备份脚本里 cp 一律带 -p,跨用户或跨文件系统时换成 rsync -a 更稳。