1. cron 基础语法
一句话总结: cron 一行五字段(分 时 日 月 周)+ 命令,分与周同时设置时是 OR 语义,这是最常见的语法误读。
cron 表每行一个任务:分 时 日 月 周 命令。五个时间字段各有取值范围。
# 每分钟
* * * * * echo "tick" >> /tmp/cron.log
# 每天 2:30
30 2 * * * /backup/backup.sh
# 每周一凌晨 3:00
0 3 * * 1 /backup/weekly.sh
# 每月 1 号 4:00
0 4 1 * * /report/monthly.sh
1.1 字段取值与特殊字符
| 字段 | 范围 | 特殊字符 |
|---|---|---|
| 分 | 0-59 | * , - / |
| 时 | 0-23 | * , - / |
| 日 | 1-31 | * , - ? |
| 月 | 1-12 | * , - / |
| 周 | 0-7 | * , -(0 与 7 都指周日) |
# 每 15 分钟
*/15 * * * * cmd
# 工作日上午 9 点到 11 点
0 9-11 * * 1-5 cmd
# 每月 1、15 号
0 2 1,15 * * cmd
一句话总结:
*/n步长、a,b枚举、a-b范围;注意「日 OR 周」——两者都非*时任一匹配即触发。
1.2 日与周的 OR 语义
# 本意「每月 1 号 或 每周一」,实为 OR:
0 3 1 * 1 cmd # 每月 1 号 与 每周一 都会执行
# 要「每月 1 号且是周一」需在命令里判断
0 3 1 * * test "$(date +%u)" = 1 && cmd
2. crontab 管理
一句话总结:
crontab -e编辑个人表、-l查看、-r删除;系统任务放/etc/crontab或/etc/cron.d/,脚本放cron.daily等目录更规范。
# 查看当前用户任务
crontab -l
# 编辑
crontab -e
# 从文件安装(适合脚本化管理)
crontab install_cron.txt
# 删除全部
crontab -r
2.1 系统级 cron
# /etc/cron.d/ 下带用户字段的任务
echo '0 2 * * * root /opt/cleanup.sh' > /etc/cron.d/cleanup
chmod 644 /etc/cron.d/cleanup
# 按小时/天/周分桶
ls /etc/cron.hourly /etc/cron.daily /etc/cron.weekly
# 脚本放进桶目录即可被 anacron/cron 调度
cp backup.sh /etc/cron.daily/backup
chmod +x /etc/cron.daily/backup
一句话总结: 个人任务用
crontab -e,系统级任务用/etc/cron.d/或分桶目录,后者由 run-parts 统一执行,管理更清晰。
2.2 脚本化管理 crontab
#!/usr/bin/env bash
set -euo pipefail
# 生成 crontab 内容并安装
{
echo "# 备份任务"
echo "30 2 * * * /opt/backup.sh"
echo "0 3 * * 1 /opt/weekly.sh"
} | crontab -
# 校验当前配置
crontab -l | grep -v '^#' | grep -v '^$'
3. 环境与路径陷阱
一句话总结: cron 环境只有最小 PATH,HOME 也不一定对;任务里用绝对路径、显式 source 环境、日志输出到文件,否则脚本「手动能跑、定时不跑」。
# 常见故障:PATH 缺失
# 0 2 * * * docker ps # 找不到 docker
# 0 2 * * * /usr/bin/docker ps # 绝对路径可用
# 在 crontab 顶部设置 PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 脚本开头 source 环境
# source /etc/profile
# source ~/.bashrc
3.1 输出重定向与日志
# 全部输出进日志
30 2 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
# 错误单独记录
30 2 * * * /opt/backup.sh >> /var/log/backup.log 2>> /var/log/backup.err
一句话总结: cron 任务默认无终端输出,必须手动
>> log 2>&1;日志挂载到独立文件,排查才有一手证据。
3.2 时间与时区
# 检查 cron 使用的时区
date
grep CRON /var/log/syslog | head
# 统一时区(对容器很重要)
timedatectl set-timezone Asia/Shanghai
# 脚本内显式指定 TZ 避免歧义
TZ=Asia/Shanghai /opt/report.sh
4. 日志与错误处理
一句话总结: cron 自身日志在
/var/log/syslog或 journald,任务内先set -euo pipefail、失败发告警、最后cron邮件或外部通知兜底。
# 查看 cron 触发记录
grep CRON /var/log/syslog | tail -20
# 用 systemd 的 cron 服务日志
journalctl -u cron --since today
4.1 任务内错误处理模板
#!/usr/bin/env bash
set -euo pipefail
LOG=/var/log/myjob.log
exec >> "$LOG" 2>&1
echo "===== 开始 $(date) ====="
if /opt/do_work; then
echo "成功"
else
echo "失败 exit=$?"
# 告警:邮件或 webhook
curl -fsS -X POST https://alerts.example.com/notify \
-d "job=myjob&status=fail"
exit 1
fi
一句话总结: 定时任务脚本三件套:
set -euo pipefail、输出全部落日志、失败调外部告警——让机器自己报错而不是人肉盯日志。
4.2 命令退出码与 MAILTO
# crontab 里设置收件人(需本地 MTA)
MAILTO=ops@example.com
0 2 * * * /opt/backup.sh
# 只对失败发邮件:命令非零退出才发
0 2 * * * /opt/backup.sh >/dev/null 2>&1 || mail -s 'backup failed' ops@example.com
5. at 一次性任务与 Anacron
一句话总结:
at处理「某时刻跑一次」,Anacron 处理「错过时间也要补跑」,它们是 cron 之外的两块拼图。
# at:明早 3 点执行一次
echo '/opt/cleanup.sh' | at 03:00
# 查看队列与删除
atq
atrm 5
# 表达式形式
at now + 5 minutes
at 2pm tomorrow
5.1 Anacron 补跑错过的任务
cron 不保证笔记本/关机的任务补跑,Anacron 记住上次执行时间,开机后补跑。
# /etc/anacrontab 格式:周期 延迟 任务名 命令
# 7 15 backup.daily /etc/cron.daily/backup
# 手动触发补跑
sudo anacron -f
# 查看上次运行记录
sudo anacron -d -n
一句话总结: 笔记本或不常开的机器用 Anacron 按「距上次执行的间隔」补跑,比 cron 的固定时刻更贴合。
5.2 atd 服务与权限
# 确保 atd 运行
systemctl enable --now atd
systemctl status atd
# 黑名单/白名单文件
ls /etc/at.allow /etc/at.deny
6. systemd timer
一句话总结: systemd timer 用
.timer+.service两个单元声明定时任务,支持OnCalendar日历表达式与Persistent补跑、RandomizedDelaySec抖动,是现代 cron 的替代。
# /etc/systemd/system/backup.service
# [Unit]
# Description=Backup job
# [Service]
# Type=oneshot
# ExecStart=/opt/backup.sh
# /etc/systemd/system/backup.timer
# [Unit]
# Description=Run backup daily
# [Timer]
# OnCalendar=daily
# Persistent=true
# RandomizedDelaySec=300
# [Install]
# WantedBy=timers.target
systemctl enable --now backup.timer
systemctl list-timers --all
6.1 OnCalendar 表达式
| 写法 | 含义 |
|---|---|
OnCalendar=daily | 每天 00:00 |
OnCalendar=*-*-* 02:30:00 | 每天 2:30 |
OnCalendar=Mon..Fri 09:00 | 工作日 9 点 |
OnCalendar=*-*-1..7 04:00 | 每月前 7 天 4 点 |
# 校验日历表达式
systemd-analyze calendar 'Mon..Fri 09:00'
# 最近 5 次触发时间
systemd-analyze calendar --iterations=5 'daily'
一句话总结:
systemd-analyze calendar校验表达式、--iterations看未来触发,写错前先算一遍,避免「静默不触发」。
6.2 timer 相比 cron 的优势
# 错过补跑、随机抖动、日志统一、依赖管理
OnCalendar=daily
Persistent=true # 上次没跑开机补跑
RandomizedDelaySec=600 # 随机延迟防风暴
# 任务依赖与超时(service 单元)
# [Service]
# ExecStart=/opt/backup.sh
# TimeoutStartSec=30min
7. 任务重叠防护
一句话总结: 长任务未结束又触发,用
flock锁文件或 PID 文件防重入;日志按日期切分,避免单文件无限膨胀。
# flock 防重入(推荐)
#!/usr/bin/env bash
exec 9>/var/lock/backup.lock
flock -n 9 || { echo "上次任务还在跑"; exit 1; }
# 任务主体...
7.1 PID 文件方案
#!/usr/bin/env bash
set -euo pipefail
PIDFILE=/var/run/myjob.pid
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "任务已在运行,退出"; exit 1
fi
echo $$ > "$PIDFILE"
trap 'rm -f "$PIDFILE"' EXIT
# 任务主体
sleep 60
一句话总结: flock 锁文件最稳(进程崩溃自动释放),PID 文件需配合 kill -0 与 trap 清理;两者二选一即可。
7.2 日志切分与保留
# 按天滚动日志(任务内)
LOG=/var/log/myjob_$(date +%F).log
exec >> "$LOG" 2>&1
# 定期清理旧日志
find /var/log -name 'myjob_*.log' -mtime +30 -delete
# logrotate 接管
# /etc/logrotate.d/myjob
# /var/log/myjob_*.log {
# daily
# rotate 30
# missingok
# }
8. 总结
| 环节 | 要点 |
|---|---|
| cron 语法 | 分时日月周五字段,*/n a-b a,b,日周 OR 语义 |
| crontab 管理 | -e 编辑、-l 查看、文件安装;系统任务用 /etc/cron.d/ |
| 环境陷阱 | PATH/HOME 最小化,绝对路径 + source 环境 + 重定向日志 |
| 错误处理 | set -euo pipefail、输出落日志、失败外部告警 |
| at/Anacron | at 一次性、Anacron 补跑错过的任务 |
| systemd timer | OnCalendar + Persistent + RandomizedDelaySec 替代 cron |
| 重叠防护 | flock 锁文件防重入、日志按日期滚动切分 |
| 校验 | systemd-analyze calendar、crontab -l、journalctl -u cron |
定时任务是运维自动化的"节拍器":cron 管固定时刻、Anacron 管补跑、systemd timer 管现代声明。脚本写好错误处理与重叠防护,定时任务才能真正"无人值守"。下一步进入网络请求与诊断,用 curl 把脚本与外部服务连接起来。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。