Linux 定时任务怎么选:cron、systemd timer 和一次性任务

举报
yd_242458115 发表于 2026/10/28 13:12:15 2026/10/28
【摘要】 同一个"每天早上跑一次"的需求,cron 和 systemd timer 到底有什么区别?这篇按四个真实场景给出选择依据,并说清 cron 最容易踩的三个坑。

"每天早上备份一次"这件事,Linux 上至少有三种做法。很多人条件反射写 crontab,但这不总是最优解。

这篇按场景给结论,不做无意义的全面罗列。

先说结论:四种场景怎么选

场景 | 选什么 | 理由

简单的周期性任务 | cron | 最短、最熟、无依赖

任务要等服务就绪才能跑 | systemd timer | 能声明 After= 依赖

机器可能关机,错过要补跑 | systemd timer | Persistent=true

只在某个时刻跑一次 | systemd timer(OnCalendar + 自删) 或 at | 不用记得清理

判断标准其实就一句:这个任务"错过之后要不要补"? 要补,就别用 cron。

cron 的三个坑

坑一:环境变量几乎没有

你在终端里 echo $PATH 看到的是一长串,cron 里可能只有 /usr/bin:/bin。

所以脚本里调用了装在 /usr/local/bin 下的命令,手动跑没问题,cron 跑就报"command not found"。

排查方法:在 crontab 里加一行把环境打出来。

* * * * * env > /tmp/cron-env.txt

等一分钟看那个文件,就知道 cron 到底给了你什么环境。

根治办法:脚本里一律用绝对路径,或者在 crontab 开头显式声明:

PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

坑二:时区和你以为的不一样

cron 用的是系统时区。服务器如果是 UTC,你写的 0 3 * * * 实际是北京时间 11 点。

先确认:

timedatectl | grep "Time zone"
date

要么把系统时区改对,要么在 crontab 里写 CRON_TZ=Asia/Shanghai(部分 cron 实现支持,Debian 系可以)。

坑三:没有日志,出问题只能猜

cron 默认把输出发邮件,而大多数服务器没配邮件,于是输出直接扔进黑洞。

必须显式重定向:

0 3 * * * /path/to/job.sh >> /var/log/job.log 2>&1

注意 2>&1 不能省——很多脚本只把正常输出写文件,错误信息跑到 stderr,结果日志里一片空白,你还以为它成功了。

一个额外提醒

crontab 里 % 是特殊字符(表示换行),写在命令里会被截断。日期格式化那种 date +%Y%m%d 必须转义成 \%Y\%m\%d,或者干脆把逻辑写进脚本文件里。

systemd timer 补了什么

补上依赖关系

cron 不管你依赖的服务起没起。systemd 可以声明:

[Unit]
After=network-online.target
Wants=network-online.target

意思是"等网络真的可用了再跑",而不是"开机就跑然后失败"。

补上错过补偿

[Timer]
OnCalendar=daily
Persistent=true

Persistent=true 会在开机后检查:上次计划的时间点有没有跑过?没跑过就立刻补一次。

这个特性对家用服务器、经常重启的机器极其重要。 cron 做不到——错过了就是错过了。

补上统一日志

journalctl -u myjob.service --since "1 hour ago"

不用管日志文件在哪、有没有被切走。

一个容易踩的细节:Type 写错

systemd timer 配的 service 通常是一次性任务,必须写:

[Service]
Type=oneshot

如果误写成 Type=simple,systemd 会以为进程一直在跑(因为它不会主动退出),后续的 timer 调度就乱了——表现为"第一次跑了,后面再也不跑"。

这个坑很隐蔽,因为 systemctl status 看起来还挺正常。

相比之下 cron 的优势在哪

说了 systemd 这么多好处,但 cron 仍然有价值的场景是:

  • 极简需求:一条 0 3 * * * /backup.sh 就够了,没必要建两个 unit 文件
  • 用户级任务:crontab -e 不需要 root,systemd 的 user unit 要开 loginctl enable-linger,麻烦
  • 容器内:很多精简镜像里 systemd 根本不存在,只能靠 cron 或语言的调度库

小结

记住两条判断线:

1. 错过要补吗? 要补 → systemd timer(Persistent=true)

2. 有依赖顺序吗? 有 → systemd timer(After=)

两条都不涉及,就老老实实写 cron,别为了"先进"给自己加配置负担。

工具没有高下,只有匹不匹配。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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