1. 为什么要把scp、rsync、inotify+rsync放一起讲
聊这个话题的起因,是我之前帮一个朋友迁移服务器。那台机器跑了好几年,上面有几十个G的站点资源、日志和备份文件。他最开始用scp往新机器上拷,拷到一半卡住了,进度条半天不动,最后Ctrl+C放弃,跑来问我有没有更快更稳的办法。我说你早该换rsync的,rsync十分钟能搞定的事,scp可能得折腾一个小时还不一定成功。他当时将信将疑,直到我用rsync把整个目录同步过去,还顺手做了个断点续传给他看,他才真正理解两者之间的差距。
其实这背后是一个完整的文件传输与同步的进阶链路:scp解决的是“临时往远程拷个文件”的场景,简单直接但效率一般;rsync解决的是“大规模、增量、可靠地同步目录”的场景,是运维和开发手里最常用的远程同步利器;而inotify+rsync则更进一步,把“定时同步”变成了“事件触发、秒级同步”,做到文件一改动就自动推到远端。很多Linux教程把这几个工具拆成零散的知识点,但实际工作中它们是递进关系,越往后越接近生产环境的真实需求。
这篇文章我会以实际可复现的方式,把这四个环节全部过一遍:scp的日常用法和容易踩的坑,源码编译安装rsync的完整过程和编译环境常见报错,rsync的增量同步原理和生产级参数怎么组合,最后是inotify+rsync的实时同步脚本、守护方式以及生产环境里的取舍。适合已经掌握Linux基础命令、想往系统运维或后端部署方向深入的同学,也适合需要经常在服务器之间搬数据的开发人员参考。
之所以把源码编译安装单独拎出来一章,是因为rsync在不同发行版上的预装版本新老不一,有些老系统自带的rsync版本太低,很多高级参数不支持,这时候就需要自己动手编译一个新版本出来。学会了rsync的编译安装,以后编译安装Python、nginx、其它开源软件,思路是完全相通的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. scp远程拷贝:日常用法与绕不开的几个坑
2.1 scp的基本形态:三种传输方向
scp全称是Secure Copy,基于SSH协议加密传输,语法非常贴近cp,只不过多了一个“远端主机”的概念。最简单的用法有三种方向。
第一种,本机拷到远端:
bash复制scp /home/user/test.txt user@192.168.1.100:/data/
第二种,远端拷回本机:
bash复制scp user@192.168.1.100:/data/test.txt /home/user/
第三种,远端到远端。注意,这种方式的流量会先经过本地中转,不是两台远端机器直连:
bash复制scp user1@host1:/data/file user2@host2:/tmp/
拷目录要加-r:
bash复制scp -r /home/user/project user@192.168.1.100:/data/
指定端口用大写-P,比如SSH跑在22022端口:
bash复制scp -P 22022 /home/user/test.txt user@192.168.1.100:/data/
提示:scp的端口参数是大写
-P,而普通ssh命令的端口参数是小写-p。这个细节我见过无数人搞混,在脚本里写错后会看到“Bad port”之类的报错,排查半天才发现是大小写错了。
如果传输的是大文件且网络带宽有限,可以用-l限速,单位是Kbit/s。比如限制为1MB/s,即8192Kbit/s:
bash复制scp -l 8192 /home/user/bigfile.tar.gz user@192.168.1.100:/data/
2.2 scp实测中最容易踩的坑
scp虽然简单,但使用中有些坑非常隐蔽。
第一个坑是远程路径里的通配符。比如我想把远程目录下所有.log文件拷回来:
bash复制scp user@192.168.1.100:/data/logs/*.log /home/user/
这个*.log到底在本地展开还是在远端展开,取决于本地shell的判断。大多数情况下,冒号后面的路径不会被本地展开,会交给远端的shell去处理,但不同版本的scp/SSH对通配符的处理行为并不完全一致。我在旧版本OpenSSH上就遇到过远端通配符不自动展开的情况。保险的做法是给远程路径加引号,明确让远端shell处理:
bash复制scp 'user@192.168.1.100:/data/logs/*.log' /home/user/
第二个坑是scp不支持断点续传。传输中断后,没有--partial之类的参数,只能全部重来。大文件传输时一旦网络抖动,前面的进度就白费了。这是scp被rsync取代的最大原因之一。
第三个坑是scp对数据完整性的校验比较弱。它基于SSH加密通道,能保证传输过程不被篡改,但传输完成后没有类似rsync那样的滚动校验机制。换句话说,如果网卡或磁盘本身有问题,造成数据静默损坏,scp不太容易发现。对超大型文件,传完最好自己做一次md5或sha256校验。
第四个坑,scp -J跳板机参数。如果你需要通过跳板机转发,用-J指定跳板机地址:
bash复制scp -J jumpuser@jump_host:22 /home/user/test.txt user@192.168.1.100:/data/
很多老教程还在教-o ProxyCommand组合写法,用-J就省事多了。
2.3 什么时候应该用scp
scp适合的是“临时、少量、小文件”的场景,比如拷个配置文件、传个安装包、从服务器拉一份日志回去看。它无需额外配置,只要有SSH权限就能用,这是它最大的优势。但如果你需要同步整个目录、做定时镜像、处理几十G的数据,或者希望中断后能续传,scp就不合适了,这时候应该直接上rsync。
Windows用户如果装了WSL或新版本PowerShell,也能直接使用scp命令,这个不再像以前那么麻烦。但要注意Windows路径和Linux路径的分隔符差异,建议先在WSL或者PowerShell里用pwd确认当前路径,再执行scp。
3. 源码编译安装rsync:为什么必须亲手编译一次
3.1 什么情况下需要源码编译安装
很多人觉得奇怪,rsync不是系统自带的吗?为什么要自己编译?我在CentOS 7和Ubuntu 18.04这类老版本系统上就遇到过,预装的rsync还是3.1.x,而rsync 3.2.0之后新增了不少实用特性,比如默认支持多线程压缩、对Zstd和LZ4压缩算法的支持、更好的错误处理等。更关键的是,有些发行版的rsync被裁剪过,某些高级参数压根不支持,或者daemon模式的行为跟官方版本有细微差别。
另外,源码编译安装还有一个好处,就是你可以把新版本装到独立目录,比如/usr/local/rsync-3.2.7,跟系统自带的/usr/bin/rsync共存,互不影响。这样既不用冒覆盖系统包的风险,又能随时切换版本。这个思路在编译安装Python、nginx时同样适用。
3.2 configure、make、make install的完整链路
这里我以编译rsync 3.2.7为例,把完整流程走一遍,操作系统以CentOS 7/Rocky Linux为例。
首先安装编译工具和依赖库。rsync 3.2.x需要libzstd、liblz4、libxxhash、openssl、popt这些开发包。在CentOS系上执行:
bash复制yum install -y gcc make autoconf zlib-devel popt-devel openssl-devel libzstd-devel liblz4-devel libxxhash-devel
注意:libxxhash-devel在CentOS 7的默认源里没有,需要先安装EPEL源。没有EPEL源的话,configure阶段会直接报错。Ubuntu/Debian系对应的是
libxxhash-dev、libzstd-dev、liblz4-dev、libpopt-dev。
下载源码包:
bash复制wget https://download.samba.org/pub/rsync/src/rsync-3.2.7.tar.gz
tar -xf rsync-3.2.7.tar.gz
cd rsync-3.2.7
配置编译参数。这里推荐把prefix指定成带版本号的目录,后期管理方便:
bash复制./configure --prefix=/usr/local/rsync-3.2.7
configure这一步会检查编译环境、依赖库和特性支持情况。如果缺库,它会明确告诉你缺什么。比如我在一台最小化安装的机器上跑configure,就遇到过:
text复制checking for XXH3_createState in -lxxhash... no
configure: error: No usable xxhash library found
这就是缺libxxhash-devel导致的,装上对应依赖包重新跑configure即可。整个configure过程本质就是“检查依赖、补依赖、再检查”的循环,首次编译软件时不要急,报错信息其实是很好的指引。
configure通过后,执行编译。用-j$(nproc)可以调用所有CPU核心并行编译,速度快很多:
bash复制make -j$(nproc)
最后安装:
bash复制make install
安装完成后,二进制在/usr/local/rsync-3.2.7/bin/rsync,创建软链接方便调用:
bash复制ln -s /usr/local/rsync-3.2.7/bin/rsync /usr/local/bin/rsync
验证版本:
bash复制rsync --version
如果输出显示rsync version 3.2.7 protocol version 31,说明编译安装成功。
3.3 编译环境下最常见的三类报错及对症处理
第一类是缺编译工具。最小化安装的系统往往没有gcc和make,执行configure时会出现“C compiler cannot create executables”之类的错误。解决方式很直接:
bash复制yum install -y gcc make
第二类是缺开发库。Linux下编译软件时,不只是要装运行库,还需要以-devel或-dev结尾的开发包,里面包含了头文件。比如缺stdio.h,不一定是系统问题,而是没装glibc-devel。这类问题在configure阶段报错时一般会给出提示,按提示补包即可。
第三类是链接阶段报找不到库文件。比如:
text复制/usr/bin/ld: cannot find -lzstd
这说明链接器找不到libzstd库。通常是libzstd-devel没装,或者装了但没在默认搜索路径里。解决方式是安装对应开发包,如果是非标准路径的库,还要在configure时通过LDFLAGS和CPPFLAGS指定路径:
bash复制./configure LDFLAGS="-L/usr/local/lib" CPPFLAGS="-I/usr/local/include"
3.4 编译安装后的“卸载”与善后
源码编译安装的软件,并没有被系统的rpm或dpkg数据库记录,所以“卸载”方式很简单——直接删除安装目录。这是很多从Windows转过来的人不太适应的一点。比如:
bash复制rm -rf /usr/local/rsync-3.2.7
再顺手把软链接删掉即可。如果你在编译时指定了--prefix=/usr或--prefix=/usr/local,安装文件会散落到bin、lib、share等目录里,卸载就得从这些目录里逐个找出来删,麻烦得多。这也是我推荐用独立目录作为prefix的原因。
再补一个细节:如果编译出来的是动态链接的二进制,运行rsync --version可能报找不到libxxhash.so之类的库,这是因为动态库不在系统默认缓存里。解决办法是把库路径写入/etc/ld.so.conf.d/下新建的conf文件中,比如:
bash复制echo "/usr/local/rsync-3.2.7/lib" > /etc/ld.so.conf.d/rsync.conf
ldconfig
4. rsync增量同步的核心机制与生产级参数组合
4.1 rsync为什么比scp快:增量传输与差异校验算法
rsync最大的优势是“增量同步”。它可以对比源端和目标端的文件,只传输发生变化的部分。这个机制可以这样理解:你有一份1000页的文档,某天你改了其中3页。scp的做法是重新复印一整本送过去,而rsync会先对比两端文档每一页的指纹,找出变了的那3页,只把这3页的内容传过去。
具体来说,rsync会把文件切成固定大小的数据块(默认约700字节),对每个数据块计算弱校验和与强校验和。目标端把校验信息发给源端,源端通过滚动校验算法快速定位哪些块在目标端已经存在,哪些块需要传输。对于大文件来说,即便只改了一个字节,rsync也能精确识别并只传那部分差异数据。
此外,rsync默认对传输数据做压缩(-z参数),进一步减少带宽占用。scp也有压缩选项-C,但rsync的压缩策略和差异传输叠加在一起,效率差距非常明显。
4.2 生产级参数组合:-avzP与--delete
rsync参数非常多,但日常使用频率最高的是这套组合:
bash复制rsync -avzP /data/ user@192.168.1.100:/backup/
逐个拆解:
-a,归档模式,是-rlptgoD的合并写法,表示递归并保留符号链接、权限、属主、属组、时间戳和设备文件。这是同步目录时的核心参数。-v,显示详细传输过程。-z,传输时压缩。-P,等价于--partial --progress,保留部分传输的文件(断点续传),同时显示进度条。
如果要做“镜像同步”,也就是让目标端和源端完全一致,加上--delete:
bash复制rsync -avzP --delete /data/ user@192.168.1.100:/backup/
--delete的作用是删除目标端存在但源端没有的文件。注意,这个参数非常关键,一旦用不好会误删数据。我第一次在生产环境用--delete前,就因为在测试环境漏看了效果,差点把目标端的旧备份目录清掉。后来养成了一个习惯:任何涉及删除的rsync命令,永远先加-n参数干跑一遍。
bash复制rsync -avzP --delete -n /data/ user@192.168.1.100:/backup/
-n是模拟执行,配合-v可以看到“将会传输哪些文件、删除哪些文件”,但不会真正执行。确认无误后再去掉-n跑真实同步。
4.3 斜杠语义:源目录加不加斜杠是两回事
这是rsync最经典的细节。源路径末尾加/,表示“同步目录内的内容”;不加/,表示“同步目录本身”。
bash复制rsync -av /data/ /backup/
执行后,/backup下面是/data里的所有文件和子目录,而不是/data目录本身。
bash复制rsync -av /data /backup/
执行后,/backup下面会多出一个data目录,即/backup/data/。
目标路径末尾加不加斜杠,行为基本没有区别。实际使用时,要刻意提醒自己:是想同步“目录里面”还是“目录本身”,然后据此决定源路径末尾是否加斜杠。
4.4 远程daemon模式:rsyncd.conf配置要点
除了通过SSH走22端口,rsync还支持daemon模式,即服务端常驻监听873端口,客户端通过rsync://协议访问。这种模式适合做集中备份服务器,多个客户端往同一个备份服务器同步,也适合在客户端没有SSH账号的情况下开放受限的同步服务。
服务端需要开启rsync daemon,并配置/etc/rsyncd.conf。一份基础配置如下:
ini复制uid = rsync
gid = rsync
use chroot = yes
max connections = 10
pid file = /var/run/rsyncd.pid
log file = /var/log/rsync.log
[backup]
path = /data/backup
comment = backup directory
read only = no
auth users = rsync_backup
secrets file = /etc/rsyncd.secrets
配置好之后,需要创建认证文件和对应系统用户:
bash复制useradd -r -s /sbin/nologin rsync
echo "rsync_backup:yourpassword" > /etc/rsyncd.secrets
chmod 600 /etc/rsyncd.secrets
提示:rsyncd.secrets的权限必须设置为600,否则rsync会拒绝读取,客户端同步时提示“auth failed on module”,但密码明明是对的。这个坑我排查过很久。
启动daemon:
bash复制rsync --daemon --config=/etc/rsyncd.conf
客户端访问时,不再用user@host:/path格式,而是用模块名:
bash复制rsync -avzP rsync_backup@192.168.1.100::backup/ /local/backup/
注意这里的::backup是配置文件中定义的模块名,不是真实的服务器路径。很多第一次用daemon模式的人会下意识写成::/data/backup,结果报错。模块名跟实际路径的对应关系,完全由服务端rsyncd.conf决定。
另外,走daemon模式时默认使用873端口,要在防火墙里放行:
bash复制firewall-cmd --permanent --add-port=873/tcp
firewall-cmd --reload
daemon模式同步时数据是明文传输的,不适合跨越不可信网络传输敏感数据。如需加密,可以通过SSH隧道或stunnel间接实现,这部分按需扩展。
4.5 rsync中容易被忽略的“-p”与参数冗余
有个热词是rsync -p,很多新手会单独加-p来保留权限。但前面提到,-a归档模式已经包含了-p,并且额外保留了属主、属组、时间戳、软链接等属性。所以rsync -avzp里的-p其实是冗余的。在脚本里看到这种写法倒也没毛病,但如果想真正理解参数体系,得知道-a背后到底包含了什么。用rsync --help看详细说明,-a等价于:
text复制-rlptgoD
也就是递归、软链接、权限、时间戳、属组、属主、设备文件。理解了这层,你就能明白为什么-a被称为归档模式,也能更精确地拼装自己的参数组合。
5. inotify+rsync实时同步:从定时任务升级到事件驱动
5.1 inotify内核机制与inotifywait的监听方式
前面说的rsync,无论是手动执行还是通过crontab定时执行,本质上都是“定期拉齐”模式。定时同步有一个明显的短板:两次同步之间的空窗期里,源端可能产生大量数据变化,而目标端还停留在上一次的状态。如果业务对数据一致性要求很高,比如多个应用共用同一份配置文件、或者需要把一台服务器的上传目录实时备份到另一台,就需要“事件驱动”的同步方案。
Linux内核从2.6.13开始提供了inotify机制,可以监控文件系统里的创建、删除、修改、移动、属性变化等事件。你可以把它理解成小区门口装了一台摄像头,任何人进出都会被记录,你不需要每隔几分钟去门口看一眼,而是有人一出现摄像头就通知你。
inotify-tools是封装了内核接口的用户态工具,其中最常用的命令是inotifywait。安装方式:
bash复制yum install -y inotify-tools
Ubuntu/Debian:
bash复制apt install -y inotify-tools
基本监听命令:
bash复制inotifywait -mrq /data/www
参数解释:
-m,持续监听,不退出。没有这个参数,inotifywait只监听一次就退出。-r,递归监听子目录。-q,安静模式,减少不必要的输出。-e,指定监听事件类型,比如modify,create,delete,move,attrib。--format,自定义输出格式。--timefmt,指定时间格式。
常用事件类型:
| 事件 | 触发时机 |
|---|---|
| create | 文件或目录被创建 |
| delete | 文件或目录被删除 |
| modify | 文件内容被修改 |
| move | 文件或目录被移动/重命名 |
| attrib | 文件的元数据发生变化,例如权限、时间戳 |
| close_write | 文件在写入模式下被关闭,常用于判断写入完成 |
监听指定事件并输出格式化信息:
bash复制inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w %f %e' -e create,delete,modify,move,attrib /data/www
5.2 从事件到同步:监听脚本怎么写才稳
inotifywait本身只负责“看”,真正干活的是它输出事件后触发rsync。常见的脚本思路是用while read循环逐行读取inotifywait的输出,每收到一条事件就执行一次rsync:
bash复制#!/bin/bash
SRC=/data/www
DEST=rsync_backup@192.168.1.100::backup
inotifywait -mrq --format '%w %f %e' -e create,delete,modify,move,attrib "$SRC" |
while read -r dir file event; do
echo "$(date '+%F %T') $dir$file $event" >> /var/log/inotify_events.log
rsync -avzP --delete "$SRC" "$DEST"
done
这样确实能实现“有变化就同步”,但有一个明显的隐患:如果有人批量拷贝大量文件进来,inotifywait会在短时间内持续输出大量事件,脚本就会频繁执行rsync。每一次rsync都要扫描整个目录,这样既消耗CPU,也可能造成网络拥塞,甚至导致事件堆积、来不及处理。
更稳妥的做法是“事件防抖”,也就是事件触发后不立刻同步,而是先把事件记到一个队列里,等一批事件攒到一定程度、或者过了几秒的安静期后再执行一次rsync。可以这样实现:
bash复制#!/bin/bash
SRC=/data/www
DEST=rsync_backup@192.168.1.100::backup
DELAY=5
do_sync() {
rsync -avzP --delete "$SRC" "$DEST"
}
inotifywait -mrq --format '%w %f %e' -e create,delete,modify,move,attrib "$SRC" |
while read -r dir file event; do
echo "$(date '+%F %T') $dir$file $event" >> /var/log/inotify_events.log
# 有事件就触发一次同步,但用sleep做简单聚合
do_sync
sleep "$DELAY"
done
这样每次事件触发后,同步完就休息几秒,把中间密集到达的事件合并到下一次同步中。当然这种写法是简化版,更完善的方案可以引入时间窗口:事件到达后启动计时器,窗口期内如果有新事件就重置计时器,直到窗口期结束才执行同步。看实际业务对实时性的要求,如果要求秒级响应,就把防抖窗口设为2到3秒;如果只是备份场景,窗口可以放宽到几十秒。
脚本写好后,用chmod +x赋予执行权限,然后前台跑一下测试,再放进后台或交给systemd管理。
5.3 systemd守护:让同步脚本长期稳定跑起来
业务脚本最怕“跑到一半进程退出”而没人知道。如果要长期运行,建议用systemd把它托管起来,设置自动重启。
创建一个unit文件/etc/systemd/system/inotify-sync.service:
ini复制[Unit]
Description=inotify rsync realtime sync
After=network.target remote-fs.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/inotify_rsync.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
然后执行:
bash复制systemctl daemon-reload
systemctl enable --now inotify-sync
用journalctl -u inotify-sync -f可以实时看日志,确认脚本在正常监听和同步。
5.4 inotify监听上限:大目录下最容易遇到的坑
监控超大目录时,inotifywait会报一个很经典的错误:
text复制Failed to watch /data/www: No space left on device
注意,这里并不一定是磁盘满了,很可能是inotify实例可监控的watch数量达到了上限。内核参数fs.inotify.max_user_watches控制了单个用户可创建的watch数量,默认值通常只有65536或8192。每监听一个目录或文件就需要一个watch,目录下文件数量一多,很容易触顶。
查看当前上限:
bash复制cat /proc/sys/fs/inotify/max_user_watches
临时调整:
bash复制sysctl -w fs.inotify.max_user_watches=819200
永久生效,写入/etc/sysctl.conf:
bash复制echo "fs.inotify.max_user_watches=819200" >> /etc/sysctl.conf
sysctl -p
还有max_user_instances,限制每个用户可创建的inotify实例数量,一般不会触顶,但如果一台机器上跑了多个监听脚本,也可能遇到。
提示:inotify事件本身是易失的。内核在事件队列溢出时会丢弃事件,并发出IN_Q_OVERFLOW标志。如果监听期间有大量并发操作,单靠inotify驱动rsync并不能保证100%不漏事件。这也是我在生产环境坚持“inotify实时同步 + 每天凌晨crontab全量同步”双保险的原因——实时链路负责尽快把变化传过去,定时全量负责把可能漏掉的差异兜底补上。
5.5 该不该用inotify+rsync:先看清这几种边界
inotify+rsync适合单主到单从、单主到多从的“单向同步”场景,比如Web前端机发布代码后自动同步到后端机、将测试环境的配置中心同步到多台应用服务器。
但它不适合所有场景,需要在引入前想清楚:
- 不适合多主互备、双向同步的场景。两台机器互相监听、互相rsync,一旦出现网络延迟或文件循环修改,很快出现“你删我建、我建你删”的回环抖动。
- 不适合海量文件的目录。监听几万、几十万个文件时会占用大量内存,而且每次事件都全量rsync,性能会很难看。
- 如果对实时性要求没那么高,比如只是每天备份一次,直接用crontab配rsync就行,完全没必要引入额外的常驻进程。
- 如果需要更强的文件一致性保证,可以看lsyncd这类封装工具,它内部就是inotify+rsync,但加了更完善的事件聚合和故障恢复机制。再往上层走,那就不是rsync能覆盖的范围了,得考虑分布式存储或专门的同步系统。
如果网络环境安全可控、数据敏感度不高,inotify+rsync加上双保险策略基本够用;如果文件量极大或要求严格的强一致性,就应该换思路了。
6. scp、rsync、inotify+rsync怎么配合使用
回顾一开始那个迁移服务器的场景,其实最终用了三步方案:先用rsync做全量同步,把几十个G的目录一次拉齐;再写了一个inotify脚本,在核心上传目录上做实时增量同步;最后设置crontab,每天凌晨跑一次全量rsync,把实时同步可能漏掉的文件兜底补上。
这就是scp、rsync、inotify+rsync在实际工作中的配合方式:
bash复制# 1. 首次全量同步
rsync -avzP /data/ user@192.168.1.100:/data/
# 2. 实时同步监听(后台脚本运行)
/usr/local/bin/inotify_rsync.sh
# 3. 定时兜底同步
# crontab -e
# 0 2 * * * rsync -avzP --delete /data/ user@192.168.1.100:/data/ >> /var/log/rsync_cron.log 2>&1
scp用来处理一次性、零散文件的传输,不需要任何额外配置,随用随走。rsync负责目录级、全量或增量的同步,尤其适合定时任务和首次迁移。inotify+rsync补上定时同步的时间差,把“准实时”变成现实,适合对及时性有要求的场景。
如果只是应急拷贝一两个文件,用scp完全没问题,没必要上rsync,杀鸡用牛刀反而多写一堆参数。但一旦涉及目录、批量文件、跨服务器部署,哪怕是临时用一次,我都建议先用rsync,万一中途断了还能续传,这个优势太实际了。
源码编译安装这条技能链也一样,学会了ssync编译,之后再遇到nginx版本太老、Python版本太旧、某个开源软件需要定制编译参数的情况,思路都是同一套:下载源码、装依赖、configure、make、make install、软链接、配环境变量。
最后再分享一个我个人的习惯:凡是涉及rsync的删除操作,永远先加-n干跑一遍,确认输出里要删除的文件列表符合预期,再真正执行。涉及inotify持久化脚本,永远用systemd托管而不是nohup后台裸跑,否则进程哪天没了你都不知道。这些都是吃了几次亏之后才形成的肌肉记忆,希望你们跳过这些坑。
