什么是crontab?为什么每个运维都要掌握它?
在Linux服务器运维中,定时任务是最基础也最频繁的需求之一。备份数据库、清理日志、发送监控告警、更新SSL证书——这些重复性的工作如果全靠人工手动执行,不仅效率低下,还容易遗漏。
crontab(cron table)是Linux系统中最经典的定时任务管理工具。它能让系统在指定的时间自动执行预设的命令或脚本,是运维工程师必备的核心技能。
本教程将从crontab的基本语法开始,逐步深入到生产环境中的实战场景和最佳实践,无论你是刚接触Linux的新手,还是有几年经验的运维老手,都能从中获得实实在在的帮助。
一、crontab基础:安装与常用命令
大多数Linux发行版默认已安装cron。先确认一下你的系统状态:
$ systemctl status cron
● cron.service - Regular background program processing daemon
Loaded: loaded (/lib/systemd/system/cron.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-05-25 08:00:01 CST
Main PID: 1234 (cron)
Tasks: 1 (limit: 2327)
Memory: 2.1M
CPU: 85ms
CGroup: /system.slice/cron.service
└── 1234 /usr/sbin/cron -f
如果没安装,一条命令搞定:
$ sudo apt update && sudo apt install cron -y # Debian/Ubuntu
$ sudo yum install cronie -y # CentOS/RHEL
crontab的常用命令只有5个,背下来就行:
$ crontab -e # 编辑当前用户的定时任务(最常用)
$ crontab -l # 列出当前用户的定时任务
$ crontab -r # 删除当前用户的所有定时任务(慎用!)
$ crontab -u username # 管理指定用户的定时任务(需要root)
$ crontab -i # 删除前先确认(安全开关)
二、cron表达式:5个星号搞定一切
crontab的语法看起来简单,但写错一个星号就可能让任务不按预期执行。来,记住这个口诀:
┌───────── 分钟 (0 - 59)
│ ┌───────── 小时 (0 - 23)
│ │ ┌───────── 日期 (1 - 31)
│ │ │ ┌───────── 月份 (1 - 12)
│ │ │ │ ┌───────── 星期 (0 - 7, 0和7都表示周日)
│ │ │ │ │
* * * * * <要执行的命令>
几个特殊字符帮你写出更灵活的表达式:
*— 每(每个分钟/小时/天…),— 多个值(如1,15,30表示第1、15、30分钟)-— 范围(如9-17表示9点到17点)/— 步长(如*/5表示每5分钟)
三、10个最常用的cron表达式示例
直接上实战,以下场景你大概率都会遇到:
# 每分钟执行一次
* * * * * /usr/bin/php /var/www/cron/every_minute.php
# 每5分钟执行一次
*/5 * * * * /usr/bin/python3 /opt/scripts/check_health.py
# 每天凌晨2:30执行(日志清理的经典时间)
30 2 * * * /usr/local/bin/clean_logs.sh
# 每天上午8点和下午6点各执行一次
0 8,18 * * * /opt/scripts/send_report.sh
# 每周一凌晨3:00执行
0 3 * * 1 /usr/local/bin/weekly_backup.sh
# 每月1号凌晨4:00执行
0 4 1 * * /usr/local/bin/monthly_report.sh
# 每季度执行(1、4、7、10月的凌晨5点)
0 5 1 1,4,7,10 * /usr/local/bin/quarterly_cleanup.sh
# 工作日(周一到周五)每2小时执行一次
0 */2 * * 1-5 /opt/scripts/workday_task.sh
# 每半小时执行一次(适合频繁监控脚本)
*/30 * * * * /opt/scripts/monitor_memory.sh
# 每小时的第15分钟执行
15 * * * * /usr/local/bin/hourly_task.sh
四、实战场景1:Nginx日志每日切割与清理
这是运维中最常见的需求之一。Nginx访问日志如果不做切割,单个文件很快就能撑满磁盘。
先写一个日志切割脚本 /usr/local/bin/rotate_nginx_logs.sh:
#!/bin/bash
# Nginx日志每日切割脚本
LOG_DIR="/var/log/nginx"
YESTERDAY=$(date -d "yesterday" +"%Y%m%d")
# 切割访问日志
mv $LOG_DIR/access.log $LOG_DIR/access_$YESTERDAY.log
mv $LOG_DIR/error.log $LOG_DIR/error_$YESTERDAY.log
# 向Nginx发送USR1信号,让它重新打开日志文件
kill -USR1 $(cat /var/run/nginx.pid)
# 保留最近30天的日志,删除更早的
find $LOG_DIR -name "*.log" -mtime +30 -delete
echo "[$(date)] Nginx logs rotated, kept last 30 days"
$ chmod +x /usr/local/bin/rotate_nginx_logs.sh
然后添加到crontab:
$ crontab -e
# 加入这一行,每天凌晨0点执行日志切割
0 0 * * * /usr/local/bin/rotate_nginx_logs.sh >> /var/log/rotate_cron.log 2>&1
五、实战场景2:MySQL数据库自动备份
数据是命根子,自动备份必须安排上:
#!/bin/bash
# MySQL每日备份脚本
BACKUP_DIR="/data/backups/mysql"
DB_USER="backup_user"
DB_PASS="your_secure_password"
DB_NAME="wordpress"
DATE=$(date +"%Y%m%d_%H%M")
RETENTION_DAYS=7
# 创建备份目录
mkdir -p $BACKUP_DIR
# 执行备份
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_$DATE.sql.gz
# 保留最近7天,删除旧备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "[$(date)] Backup completed: ${DB_NAME}_$DATE.sql.gz ($(du -sh $BACKUP_DIR/${DB_NAME}_$DATE.sql.gz | cut -f1))"
然后添加定时任务(每天凌晨3点备份,避开业务高峰期):
0 3 * * * /usr/local/bin/backup_mysql.sh >> /var/log/backup.log 2>&1
六、实战场景3:系统资源监控与告警
磁盘快满了还等用户来报?自动监控才是正解:
#!/bin/bash
# 磁盘使用率监控告警脚本
THRESHOLD=85
EMAIL="admin@example.com"
df -H | grep -vE '^Filesystem|tmpfs|overlay' | awk '{print $5 " " $6}' | while read output;
do
usage=$(echo $output | awk '{print $1}' | cut -d'%' -f1)
partition=$(echo $output | awk '{print $2}')
if [ $usage -ge $THRESHOLD ]; then
echo "⚠️ 磁盘告警: $partition 使用率已达 $usage%" | \
mail -s "[CRITICAL] 磁盘空间告警 - $(hostname)" $EMAIL
fi
done
# 每10分钟检查一次
*/10 * * * * /usr/local/bin/disk_monitor.sh
七、生产环境最佳实践
1. 始终使用绝对路径
cron的环境变量和终端不同,PATH非常有限。命令和脚本路径一律写绝对路径:
# ❌ 错误方式——cron可能找不到python3
*/5 * * * * python3 /opt/scripts/check.py
# ✅ 正确方式——用绝对路径
*/5 * * * * /usr/bin/python3 /opt/scripts/check.py
2. 输出重定向与日志
cron的默认输出会发邮件给用户,大部分服务器没配邮件,你的日志就丢了。一定要重定向:
# 标准输出和错误都记录到日志文件
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# 或丢弃所有输出(确定脚本稳定后)
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
3. 加锁防止重复执行
对于执行时间可能超过间隔的任务(比如大数据量的数据库备份),需要加锁:
#!/bin/bash
# 使用flock确保同一时间只有一个实例在运行
LOCKFILE="/tmp/backup_mysql.lock"
exec 200>$LOCKFILE
flock -n 200 || exit 1
# 这里是实际的任务内容
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME | gzip > /data/backup/db_$(date +%Y%m%d).sql.gz
flock -u 200
4. 设置环境变量
在crontab文件顶部设置环境变量:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com
0 2 * * * /usr/local/bin/backup.sh
5. 使用crontab.guru验证表达式
写没把握的表达式时,去 crontab.guru 验证一下,把5个星号填进去,它告诉你实际什么时间执行。这个习惯能救你很多次。本神亲测有效。
八、故障排查:cron任务没执行怎么办?
crontab没按预期执行,90%是以下原因:
- 路径问题 — cron的PATH远比你终端的短。始终用绝对路径。
- 权限问题 — 脚本没加执行权限(
chmod +x) - 语法错误 — 用
crontab -l检查实际写入的内容 - cron服务没运行 — 检查
systemctl status cron - 系统时间不对 — 用
date确认时区,timedatectl set-timezone Asia/Shanghai修正
排查命令一条龙:
$ grep CRON /var/log/syslog | tail -20 # Ubuntu/Debian 查看cron日志
$ journalctl -u cron --since "1 hour ago" # systemd 查看cron日志
$ crontab -l # 确认任务已写入
$ ls -la /var/spool/cron/crontabs/ # crontab文件实际位置
九、进阶:anacron——解决服务器关机错过任务的问题
如果你的服务器不是7×24小时运行(比如笔记本或定时开关机的开发机),cron在关机期间的任务会直接跳过。这时候需要 anacron——它会在系统启动后补执行错过的任务。
$ sudo apt install anacron -y
anacron的任务配置在 /etc/anacrontab:
# 格式:周期(天) 延迟(分钟) 任务名 命令
1 5 daily_backup /usr/local/bin/backup.sh
7 10 weekly_clean /usr/local/bin/weekly_cleanup.sh
30 15 monthly_report /usr/local/bin/monthly_report.sh
第一列的天数是什么意思?就是”任务至少每N天执行一次”。如果系统关机了3天,开机后5分钟(第二列设置的延迟)就会执行daily_backup。
常见问题(FAQ)
Q: crontab -e 用什么编辑器?怎么改成vim?
select-editor (Ubuntu)或 export EDITOR=vim (加到 ~/.bashrc 永久生效)。
Q: crontab 支持秒级定时任务吗?
while sleep 10; do ... done 包装成后台服务,或用 systemd timer 实现。
Q: 修改了crontab需要重启服务吗?
crontab -e 保存后立即生效。但如果修改了系统的 /etc/crontab,则需要 systemctl restart cron。
— 本文发布于2026年5月27日,命令均在Ubuntu 24.04 LTS / Debian 12上验证通过。如有更新,欢迎收藏订阅。