crontab 的四个老毛病
crontab 用了几十年,但它身上的毛病一直没改:
- 日志靠自己重定向:不写
>> /var/log/xxx.log 2>&1就等于没日志,排查全靠猜; - 关机错过的任务直接丢:笔记本合盖一晚上,早上的备份任务就无声无息地没了;
- PATH 环境变量玄学:cron 的环境和你登录 shell 不一样,
command not found是家常便饭; - 秒级不支持:最小粒度 1 分钟,想 30 秒跑一次得写 sleep Hack。
systemd timer 把这些问题一次性解决了:日志进 journald(journalctl -u 直接看),Persistent=true 开机补跑错过的任务,环境变量在 service 文件里写死,最小粒度精确到秒。
写法:两个文件分工
一个 .service 定义做什么,一个 .timer 定义何时做。比如每天凌晨 3 点跑备份:
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 03:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
OnCalendar 的语法比 cron 直观:*-*-* 03:00:00 就是每天 3 点。Persistent=true 是精髓——机器关机错过的任务,开机后会自动补跑一次。
启用就三行:
systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers # 看所有 timer 的下次触发时间
迁移对照:cron 那行怎么写成 timer
| cron | timer 的 OnCalendar |
|---|---|
0 3 * * * | *-*-* 03:00:00 |
*/30 * * * * | *:0/30 |
0 0 * * 0(周日午夜) | Sun *-*-* 00:00:00 |
@reboot | service 里加 [Install] WantedBy=multi-user.target |
最常忘的一步:改完 timer 文件一定要 systemctl daemon-reload,不然 systemd 读的还是旧配置。这个坑我踩过三次。
结论
新任务直接写 timer,别再开 cron 的历史倒车。老 cron 也不用急着全迁——迁一个试试,你会发现回不去了:journalctl -u backup.service 查日志的爽快感,crontab 这辈子给不了。
