rsync 同步代码,我一个斜杠没写对,目标目录差点被清空

举报
yd_242783495 发表于 2026/11/10 14:25:35 2026/11/10
【摘要】 一次深夜同步代码,rsync 命令里源目录末尾少了个斜杠,结果目标目录里多套了一层不说,配合 --delete 差点把线上正在跑的文件清掉。把这个斜杠的区别、--delete 的危险和我现在每次同步前的固定检查记下来。

上周半夜,我要把一台测试机上的代码同步到一台华为云 ECS 上。rsync 我用了好几年了,命令随手就敲了:

rsync -avz --delete /data/www/myapp root@10.0.0.5:/data/www/myapp

回车,等输出刷完,感觉没什么不对。到目标机器上一看,傻眼了:/data/www/myapp 下面多了一层 myapp,变成了 /data/www/myapp/myapp。服务读的还是老目录,等于这次同步白干了。

更吓人的是后面的事。发现目录套错之后,我第一反应是把命令“纠正”一下再跑一遍,结果手一抖又把源和目标写反了方向——幸好那次没加 --delete,不然线上目录能被清空一半。这两次失误凑在一起,让我把 rsync 这两个坑刻进脑子里了。

斜杠到底差在哪

rsync 对源目录末尾有没有斜杠,处理方式是完全不同的两套逻辑:

# 写法 A:源目录末尾带斜杠
rsync -av /data/www/myapp/ root@10.0.0.5:/data/www/
# 结果:myapp 目录里的内容,同步到 /data/www/ 下面

# 写法 B:源目录末尾不带斜杠
rsync -av /data/www/myapp root@10.0.0.5:/data/www/
# 结果:myapp 这个目录本身,连同里面的内容,一起同步到 /data/www/ 下面

等等,这里容易看晕,换个更直白的说法:

  • /data/www/myapp/(带斜杠)= 同步目录里的东西
  • /data/www/myapp(不带斜杠)= 同步这个目录本身

我那天敲的是不带斜杠的源,目标又写了 /data/www/myapp,rsync 就把整个 myapp 目录塞进了目标的 myapp 里面,于是多了一层。命令本身没报错,退出码也是 0,不仔细看输出根本发现不了。

# 同步完在目标机器上数一层,别急着走
ls /data/www/myapp | head
ls /data/www/myapp/myapp 2>/dev/null && echo "套了一层,同步错了"

–delete 是个双刃剑

--delete 的意思是:目标目录里有、源目录里没有的文件,删掉。听着合理,实际用的时候非常危险,因为它只认“源里有没有”,不认“这个文件重不重要”。

我吃过一次小亏:源目录里的 .env 被我顺手加进了 .gitignore,同步时用了 --exclude 又加了 --delete,结果目标机器上的 .env 被当成“源里没有的文件”删掉了。服务第二天启动直接报错,查了半天才发现是配置文件没了。

从那之后,我给 --delete 定了三条规矩:

第一,第一次对一个新目标用 --delete,先加 --dry-run 看一遍它到底要删什么:

rsync -avz --delete --dry-run /data/www/myapp/ root@10.0.0.5:/data/www/myapp/

输出里带 deleting 的行就是它准备删的,逐行看一遍,没误伤再去掉 --dry-run 真跑。

第二,重要目录永远配合 --backup 和 --backup-dir,删掉的文件先挪到备份目录,而不是直接消失:

rsync -avz --delete --backup --backup-dir=/data/backup/rsync-$(date +%Y%m%d) \
  /data/www/myapp/ root@10.0.0.5:/data/www/myapp/

这样就算删错了,文件还在备份目录里躺着,能捞回来。

第三,.env、上传目录、日志目录这种目标机器上独有的东西,写进 --exclude,并且每次同步都从同一个脚本里跑,不要手敲:

rsync -avz --delete \
  --exclude '.env' \
  --exclude 'uploads/' \
  --exclude '*.log' \
  /data/www/myapp/ root@10.0.0.5:/data/www/myapp/

手敲命令最容易漏参数,写成脚本固定下来,错一次改脚本,以后都不会再错。

我现在每次同步前的固定动作

吃过两次亏之后,我现在同步代码只跑一个固定脚本,两步雷打不动:

#!/bin/bash
set -euo pipefail

SRC="/data/www/myapp/"
DST="root@10.0.0.5:/data/www/myapp/"
BACKUP_DIR="/data/backup/rsync-$(date +%Y%m%d)"

# 第一步:dry-run 看一遍要删的文件
rsync -avz --delete --dry-run \
  --exclude '.env' --exclude 'uploads/' --exclude '*.log' \
  "$SRC" "$DST" | grep -E '^deleting' || true

echo "上面是要删除的文件,确认没问题后按回车继续,Ctrl+C 取消"
read -r

# 第二步:正式同步,删掉的文件进备份目录
rsync -avz --delete --backup --backup-dir="$BACKUP_DIR" \
  --exclude '.env' --exclude 'uploads/' --exclude '*.log' \
  "$SRC" "$DST"

set -euo pipefail 是必须的,不然哪一步失败了脚本还会往下跑。grep -E '^deleting' || true 后面那个 || true 也别省,不然 grep 没找到匹配就直接退出了。

还有一个细节:同步之前先把目标机器上的服务停掉。rsync 不是原子操作,大文件同步到一半,服务去读一个写了一半的文件,报错的样子千奇百怪,查起来特别费时间。

最后再说一次

rsync -a 是归档模式,保留权限、时间戳、软链接,日常同步代码基本都用它;-z 是压缩,公网同步加上能省不少流量。

斜杠的事再说一次,因为它真的太容易错了:源目录末尾带斜杠,同步的是里面的内容;不带斜杠,同步的是目录本身。每次敲命令之前,盯着那个斜杠看一眼,能省掉半夜爬起来修目录的麻烦。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。