当前位置:首页>Linux>每天学一个Linux命令系列(27):crontab - 定时任务不执行?排查思路在这

每天学一个Linux命令系列(27):crontab - 定时任务不执行?排查思路在这

  • 2026-09-11 11:03:13
每天学一个Linux命令系列(27):crontab - 定时任务不执行?排查思路在这
运
运维少年 · 科技观察

这个命令是干啥的

crontab 是Linux上用来搞定时任务的。我最早接触它是在帮朋友维护一个老项目,每天凌晨需要跑一个数据同步脚本。那时候不太熟,写完任务发现第二天数据没过来,排查了一上午才发现是环境变量的问题。

crontab的用处其实就一个:让系统在指定时间自动执行某个命令或脚本。日志清理、数据备份、定时巡检、报表生成,这些重复性的工作全都可以交给它。

每个用户都有自己的crontab配置文件,互相不干扰。root用户的crontab可以做系统级任务。

基本用法(3分钟上手)

编辑定时任务

# 编辑当前用户的定时任务列表
crontab -e

第一次执行会让你选编辑器,我一般选vim。进去后格式就是一行一个任务:

# 分 时 日 月 周  要执行的命令
  *  *  *  *  *   command

五个时间字段依次是:分钟(0-59)、小时(0-23)、日(1-31)、月(1-12)、周(0-7,0和7都表示周日)。

几个例子:

# 每天早上8点执行备份脚本
0 8 * * * /home/ubuntu/backup.sh

# 每5分钟检查一次服务是否存活
*/5 * * * * /home/ubuntu/check_service.sh

# 每周一凌晨3点清理日志
0 3 * * 1 /home/ubuntu/clean_logs.sh

# 每天中午12点和下午6点各执行一次
0 12,18 * * * /home/ubuntu/sync.sh

# 每月的1号和15号凌晨2点执行
0 2 1,15 * * /home/ubuntu/monthly_report.sh

查看现有任务

# 列出当前用户的所有定时任务
crontab -l

清空所有任务

# 删除当前用户的所有crontab任务
crontab -r

这个命令用完就没法恢复了,我一般只敢在确定没有任务的时候用。

其他用户(需要root)

# 以root身份查看www用户的定时任务
crontab -u www -l

# 编辑www用户的定时任务
crontab -u www -e

进阶骚操作

秒级任务怎么办?

crontab最小粒度是分钟,做不了秒级。但有时候我真的需要每10秒检查一次队列状态。怎么搞?

方法一:shell里写循环

# crontab里每1分钟执行,脚本内部sleep循环(不推荐,容易堆积进程)
* * * * * /home/ubuntu/every_10s.sh

脚本内容:

#!/bin/bash
# 每隔10秒执行一次,总共执行6次(1分钟)
for i in $(seq 1 6); do
    # 你的实际任务放这
    /usr/bin/curl -s http://localhost/health > /dev/null
    sleep 10
done

方法二:用systemd timer(后面会讲)

环境变量问题(重点坑)

crontab执行时,PATH环境变量只有 /usr/bin:/bin 这些基础路径。你自己装到 /usr/local/bin 或者自定义目录下的命令,直接写命令名是找不到的。

我的解决方案有三种:

方案一:脚本里写全路径

# 所有命令都写绝对路径,最稳妥
0 3 * * * /usr/local/bin/python3 /home/ubuntu/scripts/backup.py

方案二:在crontab开头设环境变量

# 在crontab -e编辑器最前面加上
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
# 这样后面所有任务都能用自定义路径的命令
0 3 * * * python3 /home/ubuntu/scripts/backup.py

方案三:脚本开头source用户环境变量

#!/bin/bash
# 加载用户的环境变量文件
source ~/.bashrc
cd /home/ubuntu/app
python3 backup.py

输出重定向

crontab任务的输出默认会通过邮件发给当前用户(如果系统配了mail)。大部分服务器上根本没配mail服务,输出就丢了,出问题都不知道。

# 标准做法:把输出追加到日志文件
0 3 * * * /home/ubuntu/backup.sh >> /var/log/backup.log 2>&1

# 如果不想保留日志,直接扔黑洞
0 3 * * * /home/ubuntu/backup.sh > /dev/null 2>&1

跟systemd timer对比

crontab方便但不够灵活,systemd timer可以替代它:

特性crontabsystemd timer
配置复杂度一行搞定要写两个文件(service + timer)
秒级任务不支持支持精度到纳秒
依赖控制无可以等网络/磁盘就绪再跑
失败重试要自己写系统自带Restar=
日志记录要自己重定向自动journalctl

有次我需要"等docker启动之后再跑的定时任务",crontab搞不定依赖,换成systemd timer + After=docker.service就好了。

禁用邮件通知

# 在命令末尾加上MAILTO=""或>/dev/null
MAILTO=""
0 3 * * * /home/ubuntu/backup.sh

避坑指南

坑1:crontab执行了但脚本没生效

我有段时间写了个备份脚本,确认crontab日志显示执行了,但备份文件没生成。最后发现脚本里用了相对路径 ./backup/,而crontab的工作目录是用户的home目录,不是脚本所在目录。

解决方法:脚本里所有路径都写绝对路径,或者在脚本开头加 cd $(dirname $0)。

坑2:环境变量不是你以为的那个

切到Python虚拟环境时特别容易翻车。你在终端里激活了虚拟环境能跑,但crontab不会加载虚拟环境。

# 错误:直接写python,crontab找不到虚拟环境的python
0 3 * * * python /home/ubuntu/myapp/run.py

# 正确:用虚拟环境的python绝对路径
0 3 * * * /home/ubuntu/myapp/venv/bin/python /home/ubuntu/myapp/run.py

坑3:%号需要转义

crontab里 % 有特殊含义,表示换行。如果你的命令里有日期格式 $(date +%Y%m%d),%号要转义:

# 错误的写法:%会被crontab吃掉
0 3 * * * /home/ubuntu/backup.sh --date=$(date +%Y%m%d)

# 正确写法:百分号前面加反斜杠
0 3 * * * /home/ubuntu/backup.sh --date=$(date +\%Y\%m\%d)

更稳妥的做法是把这类代码放到脚本里,不要在crontab行里写复杂表达式。

坑4:脚本执行权限

crontab执行脚本需要脚本本身有执行权限。我见过有人写好了脚本但没chmod +x,crontab跑的时候报Permission denied。

# 别忘了给执行权限
chmod +x /home/ubuntu/backup.sh

实战场景(重点!结合真实运维场景)

场景1:日志清理(真实案例)

我管理的一台应用服务器,日志增长很快,一个服务每天产生500MB日志。如果不清理,10天就把磁盘撑爆了。

# 每天凌晨3点执行日志清理脚本
0 3 * * * /opt/scripts/clean_logs.sh >> /var/log/clean_cron.log 2>&1

脚本内容:

#!/bin/bash
# 日志保留天数
DAYS=7
# 日志目录
LOG_DIR="/var/log/myapp"

# 删除7天前的日志文件
find $LOG_DIR -name "*.log.*" -mtime +$DAYS -exec rm -f {} \;

# 压缩3天前的日志(节省空间)
find $LOG_DIR -name "*.log" -mtime +3 -exec gzip {} \;

# 记录清理结果
echo "$(date): 清理完成,剩余空间: $(df -h / | tail -1 | awk '{print $4}')"

场景2:服务健康检查并自动恢复

有次线上服务老挂,但又不频繁,人盯不过来。我写了这么个定时任务:

# 每5分钟检查一次服务健康
*/5 * * * * /opt/scripts/health_check.sh >> /var/log/health.log 2>&1

脚本:

#!/bin/bash
# 检查应用端口是否存活
SERVICE="myapp"
PORT=8080

# 用curl测试端口是否正常响应
curl -f -s http://localhost:$PORT/health > /dev/null 2>&1

# 根据返回值判断服务是否正常
if [ $? -ne 0 ]; then
    echo "$(date): $SERVICE 挂了,尝试重启"
    # 用systemctl尝试重启服务
    systemctl restart $SERVICE
    # 记录重启结果
    if [ $? -eq 0 ]; then
        echo "$(date): $SERVICE 重启成功"
    else
        echo "$(date): $SERVICE 重启失败,请人工介入"
    fi
fi

场景3:crontab不执行的排查流程

这是我最常用的排查思路:

1. 确认crontab任务存在:crontab -l
2. 看cron服务是否在跑:systemctl status crond(CentOS)或 systemctl status cron(Ubuntu)
3. 看系统日志找线索:grep CRON /var/log/syslog 或 journalctl -u cron
4. 手动运行脚本看是否正常:bash /path/to/script.sh
5. 检查脚本是否有执行权限:ls -l /path/to/script.sh
6. 检查脚本里是不是用了相对路径
7. 检查环境变量(PATH、虚拟环境等)
8. 检查输出重定向有没有写对

这个流程我背得滚瓜烂熟,因为踩坑太多次了。

场景4:一次性延迟任务

crontab不支持"10分钟后执行一次"这种场景。这时候我一般用 at 命令:

# 10分钟后执行
echo "/opt/scripts/backup.sh" | at now + 10 minutes

或者用 sleep:

# 当前时间 + 延迟
sleep 600 && /opt/scripts/backup.sh

今日作业

写一个crontab定时任务,实现以下需求:

1. 每天凌晨2:30清理 /var/log/myapp/ 目录下超过3天的 .log 文件
2. 输出结果重定向到 /var/log/myapp/clean.log,错误输出也要记录进去
3. 每周一早上9:00生成一份周报(假设周报脚本在 /opt/report/weekly.sh)
4. 每10分钟用 curl 检查本机 http://localhost:3000/health 是否正常,如果返回非200状态码,记录到 /var/log/myapp/health.log

请写出完整的crontab配置内容。

E N D

运维少年 · 科技观察

最新文章

随机文章