systemctl list-timers下次何时触发、上次何时跑过,一屏看全。
约 1348 字大约 4 分钟
2026-05-14
写好了脚本,下一步是“让它按时自动跑”。传统办法是 crontab,systemd 提供了另一个选择——timer。它们都能定时触发任务,但在“日志、失败状态、依赖关系”上差别很大。这篇讲清两者怎么选,以及 timer 怎么用。
理解 systemd timer 由 timer unit 触发 service unit 的模型,对比它和 crontab 的取舍,掌握定时任务的排查入口。
systemctl list-timers下次何时触发、上次何时跑过,一屏看全。
systemctl status job.timer确认 timer 启用了、调度计划是什么。
journalctl -u job.service定时任务跑了什么、成没成、报了什么错,全在这。
systemctl start job.service不等定时,立即跑一次任务,用来测试。
systemd timer 不是一个文件,而是一对 unit:
job.service —— 描述“要做什么”(执行哪个脚本)job.timer —— 描述“什么时候做”(调度计划)job.timer 到点了就触发 job.service。一个最小例子:
# /etc/systemd/system/job.timer
[Timer]
OnCalendar=*-*-* 03:00:00 # 每天凌晨 3 点
Persistent=true # 错过了(比如关机)开机后补跑
[Install]
WantedBy=timers.target# /etc/systemd/system/job.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh启用:systemctl enable --now job.timer(注意是 enable timer,不是 service)。改完 unit 文件记得 systemctl daemon-reload(见 systemd 基础)。
| 维度 | systemd timer | crontab |
|---|---|---|
| 日志 | 自动进 journald,journalctl -u 就能查 | 要自己重定向到文件,否则输出基本丢失 |
| 失败状态 | service 失败会进 failed 状态,能监控到 | cron 默认只发本地邮件,常被忽略 |
| 依赖关系 | 可以 After=、Requires= 等待网络 / 挂载就绪 | 没有依赖概念,到点就跑 |
| 运行环境 | 在 unit 里显式声明,可控 | 极简环境,PATH 等常和你的终端不同 |
| 配置成本 | 要写两个 unit 文件 | 一行 crontab 搞定 |
| 错过补跑 | Persistent=true 可补 | 不补,错过就错过 |
经验法则:重要的、需要日志和失败告警的任务用 timer;临时的、简单的、个人用的小任务,crontab 更快。crontab 的具体写法见 crontab 定时任务。
如果你选 crontab,一定要知道它最大的坑:cron 的运行环境和你的登录终端完全不同。PATH 极简、不加载 ~/.bashrc、当前目录是家目录。所以“手动跑好好的脚本,放进 cron 就 command not found 或找不到文件”是经典翻车——这跟 Shell 是什么 里讲的非交互环境是同一回事。对策:脚本里命令用绝对路径、文件用绝对路径、需要的环境变量在脚本里显式设置。
不管 timer 还是 cron,定时任务出问题时的排查顺序:
systemctl list-timers # timer:下次/上次什么时候,确认它在调度
journalctl -u job.service # timer:看任务实际执行的日志和报错
systemctl start job.service # 手动触发一次,把“定时”这个变量排除掉systemctl start job.service 这一步很关键——它能让你手动复现,把问题从“定时没触发”和“脚本本身有 bug”里区分开。
定时任务“没跑”和“跑了但失败”是两件事
排查定时任务,先分清两种情况:① 根本没触发——systemctl list-timers 看它在不在、下次什么时候,或 cron 的 crontab -l 确认条目还在;② 触发了但失败——journalctl -u 或脚本自己的日志里找报错。最常见的是第二种,而且因为定时任务在你看不见的地方跑、又常常没配日志(见 脚本日志与错误处理),可能默默失败很久没人发现。上线定时任务前,务必确认它有日志、失败能被发现。
| 误区 | 更稳妥的做法 |
|---|---|
enable 了 service 而不是 timer | 定时任务要 enable --now job.timer,触发靠 timer |
| 以为 cron 的环境和登录终端一样 | cron 环境极简,脚本里命令和文件都用绝对路径 |
| 定时任务出问题只盯“为什么没跑” | 先分清是没触发还是触发了失败,后者更常见 |
| 改完 unit 文件直接等它生效 | 先 systemctl daemon-reload 再 restart timer |
版权归属:Shuo Liu