凌晨三点,你被电话吵醒:"备份脚本没跑!数据丢了!"你爬起来一看——crontab 里写的路径是相对路径,脚本在终端里跑得好好的,放到定时任务里就找不到文件。这种事我见过不下十次。
基本语法
crontab 的时间表达式有 5 个星号:
分 时 日 月 周 命令
* * * * * /path/to/script.sh
每个星号的含义:
| 位置 | 含义 | 范围 |
| 第1个 | 分钟 | 0-59 |
| 第2个 | 小时 | 0-23 |
| 第3个 | 日 | 1-31 |
| 第4个 | 月 | 1-12 |
| 第5个 | 星期 | 0-7(0和7都是周日) |
几个常用例子:
# 每天凌晨3点执行
0 3 * * * /opt/backup.sh
# 每小时执行一次
0 * * * * /opt/check.sh
# 每周一早上9点
0 9 * * 1 /opt/report.sh
# 每5分钟执行一次
*/5 * * * * /opt/monitor.sh
# 每天8点和20点
0 8,20 * * * /opt/sync.sh
管理命令
# 编辑当前用户的 crontab
crontab -e
# 查看当前用户的定时任务
crontab -l
# 删除所有定时任务(慎用!)
crontab -r
# 编辑指定用户的 crontab(需要 root)
sudo crontab -u username -e
坑1:环境变量
crontab 执行时的环境变量和你终端里完全不同。它只有最基础的 PATH:
PATH=/usr/bin:/bin
你终端里能跑的命令,crontab 里可能找不到。解决办法:
# 方法1:命令用绝对路径
0 3 * * * /usr/bin/python3 /opt/scripts/backup.py
# 方法2:在 crontab 开头设置环境变量
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 3 * * * python3 /opt/scripts/backup.py
坑2:相对路径
crontab 的工作目录是用户家目录,不是脚本所在目录。脚本里写了 ./config.ini 或 data/backup.db,在 crontab 里就会报"找不到文件"。
# ❌ 错误:相对路径
0 3 * * * cd /opt/scripts && python3 backup.py
# ✅ 正确:在脚本里用绝对路径,或者先 cd
0 3 * * * cd /opt/scripts && /usr/bin/python3 backup.py
坑3:输出去哪了
crontab 执行的命令,默认把输出发到本地邮件。你没配邮件的话,输出就丢了,出了错你也看不到。
# 方法1:重定向到日志文件
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
# 方法2:丢弃输出(不推荐,出错看不到)
0 3 * * * /opt/backup.sh > /dev/null 2>&1
# 方法3:只保留错误输出
0 3 * * * /opt/backup.sh >> /var/log/backup-error.log 2>&1
建议每个定时任务都加日志,出了问题能查。
坑4:权限问题
脚本文件没有执行权限,crontab 调不动它:
# 给脚本加执行权限
chmod +x /opt/backup.sh
如果脚本里操作了需要 root 权限的文件(比如写入 /var/log),要么用 root 的 crontab,要么配 sudoers。
坑5:多实例冲突
如果脚本执行时间超过 crontab 的间隔,上一次还没跑完,下一次又启动了,就会同时跑多个实例。数据库备份脚本如果同时跑两个,大概率出问题。
# 用 flock 文件锁防止多实例
0 * * * * flock -n /tmp/backup.lock /opt/backup.sh
-n 参数表示拿不到锁就直接退出,不等待。
实战:备份脚本
一个完整的备份脚本示例:
#!/bin/bash
# /opt/scripts/backup.sh
# 每天凌晨3点备份数据库
# 设置变量
DATE=$(date +%Y%m%d)
BACKUP_DIR="/backup/mysql"
LOG_FILE="/var/log/backup.log"
# 创建目录
mkdir -p $BACKUP_DIR
# 备份数据库
mysqldump -u root -pMyPassword mydb > $BACKUP_DIR/mydb-$DATE.sql 2>&1
# 检查结果
if [ $? -eq 0 ]; then
echo "[$(date)] Backup success: mydb-$DATE.sql" >> $LOG_FILE
# 删除7天前的备份
find $BACKUP_DIR -name "mydb-*.sql" -mtime +7 -delete
else
echo "[$(date)] Backup FAILED!" >> $LOG_FILE
fi对应的 crontab:
# 编辑 crontab
crontab -e
# 添加这一行(注意绝对路径 + 日志重定向)
0 3 * * * /bin/bash /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
系统级定时任务
除了用户级的 crontab -e,还有系统级的定时任务文件:
# 系统级 crontab(需要 root)
/etc/crontab
# 按周期分类的目录
/etc/cron.d/ # 自定义定时任务
/etc/cron.hourly/ # 每小时执行的脚本
/etc/cron.daily/ # 每天执行的脚本
/etc/cron.weekly/ # 每周执行的脚本
/etc/cron.monthly/ # 每月执行的脚本
把脚本扔进对应目录就行,不用写 crontab 语法。比如 logrotate 就是放在 /etc/cron.daily/ 里的。
速查表
编辑: crontab -e
查看: crontab -l
语法: 分 时 日 月 周 命令
每5分: */5 * * * *
每天3点: 0 3 * * *
防冲突: flock -n /tmp/lock script.sh
加日志: script.sh >> /var/log/xx.log 2>&1
加权限: chmod +x script.sh
记住:路径用绝对、输出重定向、脚本加权限。这三条做到了,90% 的 crontab 问题都不会碰上。
— ✦ —
— ✦ —
📢 下期预告:lsof 实战——谁动了我的端口和文件
—— 三页札记
👇 关注我,每周分享运维/Linux/开发实战技巧
