服务器定时任务实战:crontab 语法与不生效排查

    发布时间:2026-09-15 12:32 更新时间:2026-09-15 12:32 阅读量:0

    很多站长第一次写定时任务,都会遇到一个尴尬场面:脚本在终端里手敲能跑,写进 crontab 之后就像没存在过一样,日志没有、报错没有、备份也没生成。这不是 crontab 坏了,而是它的运行环境和你在 SSH 里登录的交互式 shell 差别不小。这篇就把 crontab 的语法、环境变量缺失的成因,以及任务不生效时的排查顺序讲清楚,让定时备份、日志清理这类活儿真正跑起来。

    一、crontab 语法:五个时间字段加一条命令

    crontab 的时间表达式由五个字段组成,从左到右分别是分、时、日、月、周,字段之间用空格分隔,后面接要执行的命令。取值范围:分钟 0-59,小时 0-23,日期 1-31,月份 1-12,星期 0-7(0 和 7 都表示周日)。星号表示“每一个”,逗号列举多个值,短横线表示区间,斜杠表示步长。

    几个常见写法可以记一下:0 3 * * * 表示每天凌晨 3 点整执行一次;*/10 * * * * 表示每 10 分钟一次;0 2 * * 0 表示每周日 2 点。注意“日”和“周”字段如果都不是星号,部分实现里是“或”的关系,容易踩坑,稳妥做法是只保留其中一个条件,另一个写星号。

    # 查看当前用户的定时任务列表
    crontab -l
    
    

    编辑当前用户的定时任务(保存后自动生效)

    crontab -e

    每 10 分钟跑一次脚本,并把输出追加到日志

    */10 * * * * /usr/bin/bash /opt/scripts/check.sh >> /var/log/check.log 2>&1

    系统级任务还可以放在 /etc/crontab 和 /etc/cron.d/ 目录下,这类文件的格式比用户 crontab 多一个字段——执行用户,写错这一列任务同样不会执行。具体格式以发行版官方文档为准。

    二、为什么任务不生效:环境变量是头号嫌疑

    crontab 执行命令时用的不是你的登录 shell,而是一个极简环境。最典型的问题是 PATH 很短,通常只有 /usr/bin:/bin 这类基础路径。于是你写 python3 xxx.py 或 docker ps 时,系统找不到命令,任务静默失败。解决办法很简单:命令一律写绝对路径,或者先在脚本里把 PATH 补全。

    除了 PATH,还有几个高频坑。一是工作目录,crontab 默认在家目录而不是脚本所在目录,脚本里如果用相对路径读配置,必然找不到文件。二是 shell 类型,crontab 默认可能用 /bin/sh,而你脚本里用了 bash 独有语法,需要用 /usr/bin/bash /path/to/script.sh 显式指定解释器。三是脚本没有可执行权限,chmod +x 之后才谈得上执行。

    推荐的做法是把环境变量在脚本开头统一声明,让脚本无论被谁调用都自洽:

    #!/usr/bin/bash
    

    脚本内自补环境,避免 crontab 环境过简

    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export PATH

    明确进入工作目录,防止相对路径读文件失败

    cd /opt/apps/backup || exit 1

    关键命令写绝对路径

    /usr/bin/mysqldump -u backup_user -p"$DB_PASS" app_db > app_db_$(date +%F).sql

    密码这类敏感信息不要直接写在 crontab 命令行里,因为 crontab -l 会明文显示。可以放进权限为 600 的独立配置文件,由脚本 source 读取,这也是很多备份脚本的常规做法。

    三、排查顺序:先看日志,再验环境

    任务不生效时,与其反复改时间表达式,不如按顺序验证。第一步确认 cron 服务在运行,第二步看系统日志里有没有执行记录,第三步手动模拟 cron 的极简环境跑一遍脚本。系统日志位置因发行版而异,Debian/Ubuntu 一般在 /var/log/syslog,CentOS/RHEL 系在 /var/log/cron,journald 环境下可以用 journalctl -u cron 查看,具体以实际环境为准。

    # 确认 cron 服务状态
    systemctl status cron    # Debian/Ubuntu
    systemctl status crond   # CentOS/RHEL
    
    

    查看最近的定时任务执行记录

    grep CRON /var/log/syslog | tail -n 20 journalctl -u cron --since "1 hour ago"

    用极简环境手动跑脚本,复现 crontab 现场

    env -i /usr/bin/bash /opt/scripts/check.sh

    还有一个常被忽略的点:任务其实执行了,只是没有输出也没有日志,看起来像没跑。cron 默认会把命令的标准输出和错误通过邮件发给本机用户,如果服务器没装邮件服务,这些信息就丢了。所以命令末尾统一加上 >> /var/log/xxx.log 2>&1 是很有必要的,出问题时能第一时间看到报错。如果确实想收邮件,可以在 crontab 顶部设置 MAILTO 指向一个真实可收信的地址。

    另外,短周期任务要防止上一次没跑完就叠加下一次,可以用 flock 加锁,避免备份脚本互相覆盖:

    # 用文件锁保证同一时刻只有一个实例在跑
    */30 * * * * /usr/bin/flock -n /tmp/backup.lock /usr/bin/bash /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

    小结一下:写 crontab 时记住三件事——时间字段别写错、命令和文件路径尽量用绝对路径、输出一定重定向到日志。排查时按“服务是否在跑、日志有无记录、极简环境能否复现”三步走,绝大多数“定时任务不生效”都能定位到具体原因。下一步建议把常用脚本统一放到 /opt/scripts 并加上注释头,再配合日志轮转控制文件大小,运维会省心不少。

    继续阅读

    📑 📅
    MySQL慢查询日志开启与优化入门 2026-09-15
    Docker Compose 部署多容器应用实战:环境变量、数据卷与重启策略配置要点 2026-09-15
    Nginx 日志按天切割与过期清理:logrotate 实战 2026-09-15
    Let's Encrypt 证书自动续期失败排查:日志、80端口校验与 reload 钩子 2026-09-13
    Docker 磁盘占用过高清理实战:overlay2、容器日志与悬空镜像 2026-09-12
    Nginx 499 状态码排查实战:客户端断连与 upstream 超时的区别 2026-09-16
    容器内存超限被 OOM Kill 排查:指标、cgroup 与堆参数对应 2026-09-16
    MySQL 主从延迟排查:Seconds_Behind_Master 忽高忽低怎么定位 2026-09-16
    rsync 增量同步与断点续传实战:exclude、--delete 与限速 2026-09-16
    Nginx 后端真实 IP 获取:X-Forwarded-For 与 real_ip 配置 2026-09-16