前言
备份、日志切割、证书续期、数据同步……运维一半的自动化都靠定时任务。这篇把 cron 用法与环境陷阱一次讲清。
一、crontab 基础
crontab -e # 编辑当前用户的定时任务
crontab -l # 列出
crontab -r # 删除全部(危险! 慎用)
ls /var/spool/cron/ # 各用户的任务文件
五段表达式:
┌──────── 分钟 (0-59)
│ ┌────── 小时 (0-23)
│ │ ┌──── 日 (1-31)
│ │ │ ┌── 月 (1-12)
│ │ │ │ ┌ 星期 (0-7, 0和7都是周日)
│ │ │ │ │
* * * * * 要执行的命令
# 常用示例
*/5 * * * * /opt/scripts/health.sh # 每 5 分钟
0 2 * * * /opt/scripts/backup.sh # 每天凌晨 2 点
0 3 * * 1 /opt/scripts/clean.sh # 每周一 3 点
0 0 1 * * /opt/scripts/report.sh # 每月 1 号
30 8 * * 1-5 /opt/scripts/workday.sh # 工作日早 8:30
0 */2 * * * /opt/scripts/sync.sh # 每两小时
二、系统级 /etc/crontab 与 cron.d
区别于用户 crontab,系统任务多一个用户字段:
# 文件: /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin # ← 可以定义 PATH!
# 分 时 日 月 周 用户 命令
0 2 * * * root /opt/scripts/backup.sh
软件包和运维脚本推荐放 /etc/cron.d/xxx(一个服务一个文件,便于管理与卸载):
cat > /etc/cron.d/backup <<'EOF'
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
0 2 * * * root /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
EOF
chmod 644 /etc/cron.d/backup # 权限必须是 644, 否则被忽略
三、两大经典陷阱
陷阱一:PATH 不一致
cron 执行环境不是登录 shell,PATH 通常只有 /usr/bin:/bin。所以你手动跑得好好的脚本,cron 里报 command not found。
解法(任选其一):
# 1. 脚本里全部用绝对路径
/usr/local/bin/rsync -a ...
# 2. 脚本开头自己定义 PATH
export PATH=/usr/local/bin:/usr/bin:/bin:$PATH
# 3. crontab 文件顶部写 PATH=...
陷阱二:% 是特殊字符
cron 里 % 会被解释成换行,date +%F 直接废掉:
# ❌ 错误
0 2 * * * /opt/backup.sh > /backup-$(date +%F).log
# ✅ 正确: % 用反斜杠转义
0 2 * * * /opt/backup.sh > /backup-$(date +\%F).log
排查方法
# 看 cron 是否执行过你的任务
grep CRON /var/log/cron | tail
# 手动模拟 cron 环境
env -i /bin/sh -c '/opt/scripts/backup.sh' # 干净环境跑一遍
四、任务必须"幂等 + 有日志"
好的定时任务脚本三要素:
#!/usr/bin/env bash
set -euo pipefail
LOG=/var/log/backup.log
exec >>"$LOG" 2>&1 # 所有输出进日志
echo "=== $(date '+%F %T') 开始备份 ==="
# 防重复执行: 拿不到锁就退出
exec 9>/var/run/backup.lock
flock -n 9 || { echo "上一次任务还没跑完, 跳过"; exit 0; }
# ... 备份逻辑
echo "=== 备份完成 ==="
五、常用模板
每日数据库备份(保留 7 天):
0 2 * * * root mysqldump --single-transaction mydb | gzip > /backup/mydb-$(date +\%F).sql.gz && find /backup -name "mydb-*.sql.gz" -mtime +7 -delete
日志切割(其实更推荐 logrotate,这里给个 cron 思路):
0 0 * * * root find /var/log/myapp -name "*.log" -size +100M -exec gzip {} \;
证书到期监控(剩余 30 天告警):
0 9 * * 1 root for d in /etc/nginx/certs/*.crt; do end=$(openssl x509 -enddate -noout -in "$d" | cut -d= -f2); left=$(( ($(date -d "$end" +%s) - $(date +%s)) / 86400 )); [ "$left" -lt 30 ] && echo "$d 将在 ${left} 天后过期"; done
六、anacron 与 systemd timer
| 特性 | cron | anacron | systemd timer |
|---|---|---|---|
| 粒度 | 分钟 | 天 | 秒级 |
| 错过的任务 | 跳过 | 开机补跑 | 可配置 |
| 依赖管理 | 无 | 无 | 可依赖服务/网络 |
| 日志 | /var/log/cron | 自带 | journalctl -u xx.timer |
服务器 7×24 在线用 cron;笔记本/会关机的机器用 anacron;现代发行版中,logrotate、man-db 等已迁到 systemd timer:
systemctl list-timers | head
journalctl -u myapp.timer # 看 timer 日志
小结
- 记住五段表达式 +
*/n步进即可覆盖 90% 场景 - 绝对路径、转义
%、输出重定向到日志是保命三件套 grep CRON /var/log/cron是排查第一现场- 系统级任务放
/etc/cron.d/,一个场景一个文件
本文是「Linux 运维」系列第 4 篇。