开发教程

rclone copy && sleep 到底等不等复制完成?答案与验证方法

编辑部阅读约 5 分钟

一条 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,才执行右侧命令;否则右侧被跳过。它不关心里面做了多少工作,只关心进程何时退出、以什么状态退出。

于是问题可以拆成两个具体问题:

  1. rclone copy 这个进程什么时候退出?
  2. 它退出时的状态码是多少?

对常规用法,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 兜底。

定时任务rcloneShellbash退出码