Cron & Scheduling
Schedule and manage recurring tasks. Covers cron syntax, crontab management, systemd timers, one-off scheduling, timezone handling, monitoring, and common failure patterns.
When to Use
- - Running scripts on a schedule (backups, reports, cleanup)
- Setting up systemd timers (modern cron alternative)
- Debugging why a scheduled job didn't run
- Handling timezones in scheduled tasks
- Monitoring and alerting on job failures
- Running one-off delayed commands
Cron Syntax
The five fields
CODEBLOCK0
Common schedules
CODEBLOCK1
Special strings (shorthand)
CODEBLOCK2
Crontab Management
CODEBLOCK3
Crontab best practices
CODEBLOCK4
Systemd Timers
Create a timer (modern cron replacement)
CODEBLOCK5
CODEBLOCK6
CODEBLOCK7
OnCalendar syntax
CODEBLOCK8
Advantages over cron
CODEBLOCK9
One-Off Scheduling
at (run once at a specific time)
CODEBLOCK10
sleep-based (simplest)
CODEBLOCK11
Timezone Handling
CODEBLOCK12
DST pitfalls
CODEBLOCK13
Monitoring and Debugging
Why didn't my cron job run?
CODEBLOCK14
Job wrapper with logging and alerting
CODEBLOCK15
CODEBLOCK16
Lock to prevent overlap
CODEBLOCK17
Idempotent Job Patterns
CODEBLOCK18
Tips
- - Always redirect output in cron jobs:
>> /var/log/job.log 2>&1. Without this, output goes to mail (if configured) or is silently lost. - Test cron jobs by running them with
env -i to simulate cron's minimal environment. Most failures are caused by missing PATH or environment variables. - Use
flock to prevent overlapping runs when a job might take longer than its schedule interval. - Make all scheduled jobs idempotent. If a job runs twice (DST, manual trigger, crash recovery), it should produce the same result.
- INLINECODE4 is invaluable for verifying timer schedules before deploying.
- Never schedule critical jobs between 1:00 AM and 3:00 AM if DST applies. Use UTC schedules instead.
- Log the start time, end time, and exit code of every cron job. Without this, debugging failures after the fact is guesswork.
- Prefer systemd timers over cron for production services: you get journald logging, missed-run catchup (
Persistent=true), and resource limits for free.
Cron & Scheduling
安排和管理重复性任务。涵盖cron语法、crontab管理、systemd定时器、一次性调度、时区处理、监控和常见故障模式。
何时使用
- - 按计划运行脚本(备份、报告、清理)
- 设置systemd定时器(现代cron替代方案)
- 调试计划任务未运行的原因
- 处理计划任务中的时区问题
- 监控和告警任务失败
- 运行一次性延迟命令
Cron语法
五个字段
┌───────── 分钟 (0-59)
│ ┌─────── 小时 (0-23)
│ │ ┌───── 日 (1-31)
│ │ │ ┌─── 月 (1-12 或 JAN-DEC)
│ │ │ │ ┌─ 星期 (0-7, 0和7=星期日, 或 SUN-SAT)
│ │ │ │ │
常见调度
bash
每分钟执行
每5分钟执行
/5 * /path/to/script.sh
每小时整点执行
0
/path/to/script.sh
每天凌晨2:30执行
30 2
* /path/to/script.sh
每周一上午9:00执行
0 9
1 /path/to/script.sh
每个工作日早上8:00执行
0 8
1-5 /path/to/script.sh
每月第一天午夜执行
0 0 1
/path/to/script.sh
工作时间内每15分钟执行(周一至周五9-17点)
/15 9-17 * 1-5 /path/to/script.sh
每天两次(上午9点和下午5点)
0 9,17
* /path/to/script.sh
每季度执行(1月、4月、7月、10月)第一天午夜
0 0 1 1,4,7,10 * /path/to/script.sh
每周日凌晨3点执行
0 3
0 /path/to/script.sh
特殊字符串(简写)
bash
@reboot /path/to/script.sh # 启动时运行一次
@yearly /path/to/script.sh # 0 0 1 1 *
@monthly /path/to/script.sh # 0 0 1
@weekly /path/to/script.sh # 0 0 0
@daily /path/to/script.sh # 0 0 *
@hourly /path/to/script.sh # 0
Crontab管理
bash
编辑当前用户的crontab
crontab -e
列出当前crontab
crontab -l
编辑其他用户的crontab(root权限)
sudo crontab -u www-data -e
删除所有cron任务(小心操作!)
crontab -r
从文件安装crontab
crontab mycrontab.txt
备份crontab
crontab -l > crontab-backup-$(date +%Y%m%d).txt
Crontab最佳实践
bash
显式设置PATH(cron的PATH最小)
PATH=/usr/local/bin:/usr/bin:/bin
设置MAILTO用于错误通知
MAILTO=admin@example.com
显式设置shell
SHELL=/bin/bash
完整crontab示例
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
SHELL=/bin/bash
备份
0 2
* /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
清理旧日志
0 3
0 find /var/log/myapp -name *.log -mtime +30 -delete
健康检查
/5 * /opt/scripts/healthcheck.sh || /opt/scripts/alert.sh 健康检查失败
Systemd定时器
创建定时器(现代cron替代方案)
ini
/etc/systemd/system/backup.service
[Unit]
Description=每日备份
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
StandardOutput=journal
StandardError=journal
ini
/etc/systemd/system/backup.timer
[Unit]
Description=每天凌晨2点运行备份
[Timer]
OnCalendar=--* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
bash
启用并启动定时器
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
检查定时器状态
systemctl list-timers
systemctl list-timers --all
检查上次运行
systemctl status backup.service
journalctl -u backup.service --since today
手动运行(用于测试)
sudo systemctl start backup.service
禁用定时器
sudo systemctl disable --now backup.timer
OnCalendar语法
ini
Systemd日历表达式
每天午夜
OnCalendar=daily
或:OnCalendar=--* 00:00:00
每周一上午9点
OnCalendar=Mon
--* 09:00:00
每15分钟
OnCalendar=*:0/15
工作日早上8点
OnCalendar=Mon..Fri
--* 08:00:00
每月第一天
OnCalendar=
--01 00:00:00
每6小时
OnCalendar=0/6:00:00
特定日期
OnCalendar=2026-02-03 12:00:00
测试日历表达式
systemd-analyze calendar Mon
--* 09:00:00
systemd-analyze calendar *:0/15
systemd-analyze calendar --iterations=5 Mon..Fri
--* 08:00:00
相比cron的优势
Systemd定时器 vs cron:
+ 日志记录在journald中(journalctl -u service-name)
+ 持久化:重启后能补上错过的运行
+ RandomizedDelaySec:防止惊群效应
+ 依赖关系:可以依赖网络、挂载等
+ 资源限制:CPUQuota、MemoryMax等
+ 无丢失邮件问题(MAILTO经常配置错误)
- - 需要创建更多文件(service + timer)
- 配置更冗长
一次性调度
at(在特定时间运行一次)
bash
调度命令
echo /opt/scripts/deploy.sh | at 2:00 AM tomorrow
echo reboot | at now + 30 minutes
echo /opt/scripts/report.sh | at 5:00 PM Friday
交互模式(输入命令,Ctrl+D结束)
at 10:00 AM
/opt/scripts/task.sh
echo 完成 | mail -s 任务完成 admin@example.com
列出待处理任务
atq
查看任务详情
at -c <任务编号>
删除任务
atrm <任务编号>
基于sleep(最简单)
bash
延迟后运行
(sleep 3600 && /opt/scripts/task.sh) &
使用nohup(退出登录后继续运行)
nohup bash -c sleep 7200 && /opt/scripts/task.sh &
时区处理
bash
Cron默认使用系统时区运行
检查系统时区
timedatectl
date +%Z
为特定cron任务设置时区
方法1:在crontab中使用TZ变量
TZ=America/New_York
0 9
* /opt/scripts/report.sh
方法2:在脚本中设置
#!/bin/bash
export TZ=UTC
所有日期操作现在使用UTC
方法3:包装器
TZ=Europe/London date +%Y-%m-%d %H:%M:%S
列出可用时区
timedatectl list-timezones
timedatectl list-timezones | grep America
夏令时陷阱
问题:在夏令时转换期间,安排在凌晨2:30的任务可能运行两次或根本不运行。
春季向前:凌晨2:30不存在(时钟从2:00跳到3:00)
秋季回退:凌晨2:30出现两次
缓解措施:
- 1. 将关键