计划任务:cron 与 systemd 定时器
如何运行计划任务,为什么任务手动执行正常但在 cron 中却毫无动静,以及何时改用 systemd 定时器。
更新于 2026年8月23日
备份、证书续期、临时文件清理、夜间报告——这些都是计划任务。在 Linux 上,有两种方式可以解决:经典的 cron 和 systemd 定时器。
Cron:快速上手
您可以这样编辑某个用户的计划:
crontab -e
crontab -l
每行的格式是五个字段加一条命令:
# min hour day month day-of-week command
0 4 * * * /usr/local/bin/backup.sh
*/15 * * * * /usr/local/bin/check.sh
30 3 * * 0 /usr/bin/systemctl restart myapp
实用快捷方式:@daily、@hourly、@reboot。要检查任务是否真的执行过:
grep CRON /var/log/syslog | tail -30
journalctl -u cron --since "1 hour ago"
为什么脚本手动能跑,在 cron 里却不行
这是最常见的问题,原因几乎总是以下三种之一。
环境不同。 Cron 的 PATH 极简,也拿不到您 .bashrc 里的任何变量。请使用程序的完整路径,并在脚本开头或 crontab 中显式设置变量:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
目录不同。 任务从用户的主目录启动,因此相对路径会指向别处。请始终使用绝对路径。
输出无处可去。 出错了,但没人看到。请写日志:
0 4 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
还有两个小细节:cron 行中的 % 字符必须转义为 \%,并且 crontab 文件必须以换行符结尾——否则最后一条任务会被忽略。
您需要从外部验证任务是否执行过。静默的 cron 和成功的 cron 看起来一模一样。在 Uptime Kuma 中设置推送检查:脚本在结束时 ping 一个地址,如果一天都没有 ping,您就会收到告警。
systemd 定时器
当任务比单条命令更复杂时,定时器更方便:它有日志、依赖关系、资源限制,还能防止同时运行两份副本。
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run nightly backup
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers
journalctl -u backup.service -n 50
Persistent=true 会在机器关机期间错过运行时补跑——cron 做不到这一点。RandomizedDelaySec 可以分散负载,避免十几个任务在同一秒启动。
需要注意的事项
- 机器时间默认是 UTC。在设置“凌晨 4 点”之前,请先检查
timedatectl。 - 繁重任务(归档、备份校验)受磁盘性能限制。请参阅磁盘速度,并将它们安排在夜间执行。
- 每五分钟运行一次的长任务会与自身重叠。在 systemd 中,
Type=oneshot有帮助;在 cron 中,请通过flock加锁:
*/5 * * * * /usr/bin/flock -n /tmp/job.lock /usr/local/bin/job.sh