一条 rclone copy 加 sleep 的串联命令,不同 AI 给出了 20 分钟和 30 分钟两种答案,同一个人在两台服务器上的实测现象还不一致。本文从 Shell 的 && 语义和进程退出状态讲起,解释为什么正常情况下总耗时是复制时间加睡眠时间,列出「删除看起来先执行了」的几种真实原因,并给出可复现的验证脚本和写定时任务的稳妥做法。
一条 rclone copy ... && sleep 1200 的命令,被拿去问了几个不同的 AI,得到的答案分成两派:一派说 20 分钟,认为 sleep 会在 rclone 启动后立刻并行计时;另一派说 30 分钟,认为要等复制结束。更让人困惑的是,同一个人在两台机器上测试,一次看到「删除命令几乎立刻执行」,另一次看到「sleep 确实在复制完成后才开始」。
这类问题其实不需要靠模型投票。Shell 的执行规则是确定的,只要把「进程什么时候退出」和「退出码是多少」这两件事分清楚,答案就唯一了。下面先给结论,再解释容易被误读的地方,最后给一套几分钟就能跑完的验证方法。
结论:正常情况是 30 分钟
如果复制确实需要 10 分钟并且成功了,整条命令耗时是 10 + 20 = 30 分钟。sleep 不会和 rclone 并行计时,它要等 rclone 进程结束、并且以 0 退出之后才开始。
反过来说,只有当 rclone 以非 0 退出码结束时,sleep 根本不会执行,整条命令会提前结束——这恰恰是很多「怎么一眨眼就跑完了」现象的来源。
&& 判断的是退出状态,不是复制进度
&& 是 Shell 的逻辑与,规则很短:左侧命令的进程结束,并且退出码为 0,才执行右侧命令;否则右侧被跳过。它不关心里面做了多少工作,只关心进程何时退出、以什么状态退出。
于是问题可以拆成两个具体问题:
rclone copy这个进程什么时候退出?- 它退出时的状态码是多少?
对常规用法,rclone copy 是前台同步进程:它枚举文件、排队传输、失败重试、校验,全部处理完之后进程才退出;期间出现无法恢复的错误时,退出码非 0。正因为如此,A && B 天然表达了「A 成功才做 B」的依赖关系,也天然保证了 sleep、rm 这类后续动作发生在复制结束之后。
需要留意的例外是:如果你是通过 rclone 的远程控制接口(rc)以异步方式提交复制任务,命令会立刻返回一个任务 id,此时退出码 0 只代表「任务已受理」,不代表传输完成。这种用法下 && 就保护不了你。
为什么第一次测试看起来「删除先执行了」
把 rclone copy 和 rm -rf 写成两行独立命令时,两行之间没有任何依赖,rm 必然在 rclone 进程结束后立刻执行,与文件是否传完无关。除此之外,常见原因还有:
rclone秒退:源路径写错、远端配置不存在、认证失败、网络不可达,进程很快就以非 0 退出,看起来就像「没等就删了」。- 左侧命令拼写或引号有问题:命令根本没跑起来,后面的动作照常执行。
- 用了
--ignore-errors:即使有文件传输失败,rclone 也会返回 0,后续命令照常执行,但数据其实是残缺的。 - 在另一个窗口观察:看到的目录列表可能是缓存或中间状态,容易误判时间点。
- 异步提交任务:如上文所说,rc 接口返回的是 jobid 而不是传输结果。
排查顺序很简单:命令跑完后立刻执行 echo $? 看退出码,或者复制过程中在另一个终端执行 pgrep -a rclone 看进程还在不在。
三条几分钟能跑完的验证方法
1. 用不依赖 rclone 的最小实验确认 && 语义
# 左侧约 5 秒后以 0 退出,右侧应当在这之后才打印时间
( sleep 5; exit 0 ) && echo "B 执行于 $(date +%T)"
# 左侧同样耗时 5 秒但以 1 退出,右侧不应执行
( sleep 5; exit 1 ) && echo "这一行不会出现"
2. 给真实命令加上时间戳和退出码
start=$(date +%s)
rclone copy "/home/SEND7/02_audio" "cc1-1:cc1-1/SEND7/01_audio"
rc=$?
echo "rclone 退出码=$rc 耗时=$(( $(date +%s) - start ))s"
if [ "$rc" -eq 0 ]; then
sleep 1200
echo "sleep 结束于 $(date +%T)"
fi
3. 复制过程中观察进程与统计
rclone copy --progress --stats 30s "/home/SEND7/02_audio" "cc1-1:cc1-1/SEND7/01_audio"
# 另开一个终端
pgrep -a rclone
如果在 --progress 还在滚动时另一个终端就已经能看到 rm 的效果,那基本可以确定是命令写法(缺少 &&)、命令提前失败或异步提交这三类问题之一。
写脚本时的稳妥做法
运算符选择对照
| 写法 | 右侧或后续命令何时执行 | 适用场景 |
|---|---|---|
A && B |
A 退出码为 0 时执行 B | 有依赖关系,A 成功才做 B |
A ; B |
无论 A 成功失败都执行 B | 两条互不依赖的命令 |
A & B |
A 转入后台,立即执行 B | 需要并行 |
检查清单
- 脚本开头加
set -euo pipefail,让出错尽早暴露,而不是继续往下跑。 - 不要用
--ignore-errors掩盖失败;确实需要容错时,改为在命令后用rclone check比对源和目标再决定下一步。 - 删除源目录前先校验:
rclone copy成功后跑一次rclone check,确认无差异再删。 - 更简洁的替代方案是
rclone move,它在传输成功后自行处理源文件;注意配合--delete-empty-src-dirs清理空目录,并先在小范围数据上试跑。 - 加日志:
--log-file=/var/log/rclone.log --log-level INFO,出问题时能回溯是哪个文件失败。 - 长时间任务放到
tmux、screen或nohup里,避免 SSH 一断就被 SIGHUP 杀掉,导致「复制到一半就没了」的假象。 - 把
sleep换成显式的条件判断,比固定睡 20 分钟更可靠——固定睡眠只是拖延,并不能证明传输成功。
如果这类复制加等待的任务需要经常挂着跑几个小时,而本地机器会休眠、网络会抖动,把任务放到一台长期在线的机器上执行会更省心。例如整机放在机房、按月租用、通过远程连接的 Mac mini,供电和网络都稳定,适合承载 rclone 这类长时间同步,也能顺手跑 Claude Code 之类的在线工具。这是可选做法之一,不是必需项。
关于问 AI 的一点建议
Shell 语义是完全确定性的,但模型容易把「命令返回」和「任务完成」混为一谈,也容易套用其他语言里异步、回调的直觉,于是给出两种互相矛盾的结论。遇到这类问题,最快的路径往往是用 5 秒钟写一个最小实验:把左侧换成一个可控耗时的命令,观察右侧打印的时间戳。比对着几份解析推理要可靠得多。
小结
&&等的是左侧进程退出且退出码为 0,不是「复制进度到 100%」。rclone copy是前台同步进程,正常结束才退出,所以 10 分钟复制加 20 分钟睡眠等于 30 分钟。- 看到「没等就删了」,优先怀疑缺少
&&、命令提前失败、--ignore-errors或异步提交任务。 - 涉及删除源数据的脚本,用
rclone check或rclone move做校验,别指望固定时长的sleep兜底。