13 10 月 2026

服务器上有大量"定期做一次"的事情:每天备份数据库、每周清理日志、每小时检查服务是否还活着。手动做会忘,写个脚本定时跑才是正解。

这篇讲 crontab 的语法、常见写法、排错方法,以及和 systemd timer 的对比。

常驻任务需要一台稳定在线的服务器。财富云计算 的云服务器 7×24 运行;内网服务、游戏服务端可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上)。

一、crontab 是什么

cron 是 Linux 自带的定时任务守护进程,crontab 是它的配置文件。

systemctl status cron          # Debian/Ubuntu 服务名是 cron
systemctl status crond         # CentOS 是 crond

crontab -l                     # 查看当前用户的任务
crontab -e                     # 编辑(第一次会让你选编辑器)
crontab -r                     # 删除所有任务(危险)
crontab -l -u root             # 查看指定用户的(需 root)
crontab -e -u www-data         # 以指定用户身份编辑

用户级 vs 系统级:

位置说明
crontab -e用户级,存在 /var/spool/cron/crontabs/用户名
/etc/crontab系统级,多一个"用户"字段
/etc/cron.d/*系统级,按文件拆分,软件包常用
/etc/cron.hourly/ daily/ weekly/ monthly/放脚本,按周期自动执行

二、语法:五个星号

*  *  *  *  *  命令
│  │  │  │  │
│  │  │  │  └── 星期 (0-7, 0和7都是周日)
│  │  │  └───── 月 (1-12)
│  │  └──────── 日 (1-31)
│  └─────────── 时 (0-23)
└────────────── 分 (0-59)

特殊符号

符号含义例子
*任意值* * * * * 每分钟
,多个值0,30 * * * * 每小时的 0 分和 30 分
-范围0 9-18 * * * 9点到18点每小时
/步长*/10 * * * * 每 10 分钟
@reboot开机时@reboot /opt/start.sh
@daily每天 0 点等同 0 0 * * *
@hourly每小时等同 0 * * * *
@weekly每周等同 0 0 * * 0
@monthly每月等同 0 0 1 * *
@yearly每年等同 0 0 1 1 *

常用写法速查

*/5 * * * *          每 5 分钟
0 * * * *            每小时整点
30 3 * * *           每天 3:30
0 3 * * *            每天 3:00
0 0 * * 0            每周日 0:00
0 2 1 * *            每月 1 号 2:00
0 9 * * 1-5          工作日(周一到周五)9:00
0 9,18 * * *         每天 9:00 和 18:00
*/10 9-18 * * 1-5    工作日 9-18 点每 10 分钟
0 0 1,15 * *         每月 1 号和 15 号
在线校验写法:https://crontab.guru

三、注意事项(踩坑合集)

1)命令要写绝对路径

cron 的 PATH 环境和你的交互式 shell 不一样,很多命令找不到。

# 错误
0 3 * * * mysqldump -uroot db > /backup/db.sql

# 正确
0 3 * * * /usr/bin/mysqldump -uroot db > /backup/db.sql

查绝对路径:

which mysqldump tar rsync

或者在 crontab 顶部声明 PATH:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * mysqldump -uroot db > /backup/db.sql

2)% 需要转义

在 crontab 里 % 有特殊含义(表示换行),必须写成 \%:

# 错误
0 3 * * * tar -czf /backup/$(date +%F).tar.gz /data

# 正确
0 3 * * * tar -czf /backup/$(date +\%F).tar.gz /data

或者把命令写进脚本文件,crontab 只调用脚本(推荐,不容易出错)。

3)环境变量不继承

cron 不会加载 ~/.bashrc、/etc/profile。需要环境变量就在脚本里显式设置:

#!/bin/bash
export LANG=en_US.UTF-8
export PATH=/usr/local/bin:$PATH
source /opt/app/env.sh
...

或者在 crontab 里写:

SHELL=/bin/bash
LANG=en_US.UTF-8

4)输出要去向明确

cron 默认会把输出通过邮件发给用户(通常没配邮件,就丢掉了)。建议重定向:

# 丢弃所有输出
0 3 * * * /opt/backup.sh > /dev/null 2>&1

# 记日志
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1

# 只记错误
0 3 * * * /opt/backup.sh > /dev/null 2>>/var/log/backup.err

2>&1 表示把标准错误合并到标准输出。

5)脚本要有执行权限和 shebang

chmod +x /opt/backup.sh
head -1 /opt/backup.sh    # 必须是 #!/bin/bash

6)避免并发执行

任务执行时间超过间隔时会重叠。用 flock 加锁:

*/5 * * * * flock -n /tmp/backup.lock -c '/opt/backup.sh'

-n 表示拿不到锁就立即退出,不等待。

7)随机延迟,避免"惊群"

多台服务器同时执行会同时打数据库/存储:

# 在脚本开头加随机延迟
sleep $((RANDOM % 300))

四、实战任务示例

1)每天备份数据库

脚本 /usr/local/bin/backup_mysql.sh:

#!/bin/bash
DATE=$(date +%F)
DIR=/backup/mysql
mkdir -p $DIR
/usr/bin/mysqldump -uroot --single-transaction --all-databases \
  | gzip > $DIR/all-$DATE.sql.gz
find $DIR -name "all-*.sql.gz" -mtime +30 -delete
echo "$(date) 备份完成" >> /var/log/backup.log
0 3 * * * /usr/local/bin/backup_mysql.sh >> /var/log/backup.log 2>&1

2)每天备份网站文件

10 3 * * * /usr/bin/tar -czf /backup/www-$(date +\%F).tar.gz /var/www/html 2>>/var/log/backup.log

3)异地同步

30 3 * * * /usr/bin/rsync -azP --delete -e "ssh -p 2222" /backup/ root@备份机:/backup/ >> /var/log/rsync.log 2>&1

4)每周清理日志

0 4 * * 0 /usr/bin/journalctl --vacuum-time=7d >> /var/log/clean.log 2>&1
0 4 * * 0 /usr/bin/find /var/log -name "*.gz" -delete

5)证书自动续期

0 3 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"

6)定时重启服务(游戏服常用)

0 5 * * * /usr/bin/systemctl restart gameserver

配合提前广播通知(游戏内):

#!/bin/bash
/usr/bin/screen -S cs2 -X stuff "say 服务器将在 1 分钟后重启\n"
sleep 55
/usr/bin/screen -S cs2 -X stuff "say 服务器重启中\n"
sleep 5
/usr/bin/systemctl restart cs2

7)服务存活检查(简易看门狗)

#!/bin/bash
if ! systemctl is-active --quiet nginx; then
    systemctl start nginx
    echo "$(date) nginx 已重启" >> /var/log/watchdog.log
fi
*/5 * * * * /usr/local/bin/watchdog.sh
更优雅的做法是用 systemd 的 Restart=on-failure,见《systemd 服务编写与开机自启实战》。

8)磁盘空间告警

#!/bin/bash
THRESHOLD=85
df -h | awk -v t=$THRESHOLD '$5+0 > t {print $6": "$5}' | while read line; do
  echo "$(date) 磁盘告警: $line" >> /var/log/disk-alert.log
done
*/30 * * * * /usr/local/bin/disk-check.sh

9)定时同步时间

0 */6 * * * /usr/sbin/ntpdate -u ntp.aliyun.com > /dev/null 2>&1
# 或用 systemd-timesyncd / chrony

10)网站健康检查

#!/bin/bash
CODE=$(curl -s -o /dev/null -w "%{http_code}" https://example.com)
if [ "$CODE" != "200" ]; then
  echo "$(date) 网站异常: $CODE" >> /var/log/site-check.log
  # 可以加钉钉/企业微信告警
fi
*/5 * * * * /usr/local/bin/site-check.sh

五、怎么确认任务真的跑了

这是最容易被忽略的:写了任务不代表它在正常执行。

1)看 cron 日志

# Debian/Ubuntu
grep CRON /var/log/syslog
tail -f /var/log/syslog | grep CRON

# CentOS
tail -f /var/log/cron

看到类似:

Oct 13 03:00:01 host CRON[12345]: (root) CMD (/usr/local/bin/backup.sh)

说明任务被触发了。

2)让任务自己写日志

最可靠的方式:

0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1

然后检查日志有没有新内容、有没有报错。

3)主动验证

# 手动跑一遍脚本
bash /opt/backup.sh
echo $?        # 返回 0 才是成功

# 用同样的环境模拟 cron
env -i /bin/bash -c '/opt/backup.sh'

4)加成功/失败告警

#!/bin/bash
if /usr/local/bin/backup.sh; then
  echo "$(date) OK" >> /var/log/cron-status.log
else
  echo "$(date) FAILED" >> /var/log/cron-status.log
  curl -s -X POST 'https://oapi.dingtalk.com/robot/send?access_token=xxx' \
    -H 'Content-Type: application/json' \
    -d '{"msgtype":"text","text":{"content":"备份任务失败!"}}'
fi

六、常见故障排查

现象原因解决
任务没执行cron 服务没启动systemctl start cron
命令找不到没用绝对路径which 查路径,或声明 PATH
date +%F 不生效% 未转义改成 \%F
脚本手动能跑,cron 不行环境变量差异脚本里显式设环境变量
输出丢失没重定向加 >> log 2>&1
任务重复执行执行时间超过间隔用 flock 加锁
时区不对系统时区设置timedatectl set-timezone Asia/Shanghai,改完重启 cron
权限问题以错误用户执行用 crontab -u 用户 -e
/etc/crontab 里的任务不跑缺用户字段系统级格式是 分 时 日 月 周 用户 命令

改时区后必须重启 cron:

timedatectl set-timezone Asia/Shanghai
systemctl restart cron

七、cron vs systemd timer

对比cronsystemd timer
语法五个星号,需要记配置文件,语义清晰
日志要自己重定向自动进 journalctl
错过执行关机期间的任务直接丢失Persistent=true 可开机补跑
依赖管理无可声明依赖(如网络就绪)
随机延迟自己写 sleepRandomizedDelaySec
资源限制无可设 MemoryMax、CPUQuota
调试麻烦systemctl list-timers 看下次执行时间

建议:

  • 简单任务(备份、清理)→ crontab,够用
  • 重要任务、需要日志和可靠性 → systemd timer

systemd timer 写法详见《systemd 服务编写与开机自启实战》。


八、安全注意

# 限制谁能用 cron
cat /etc/cron.allow    # 白名单(存在时只有名单内用户可用)
cat /etc/cron.deny     # 黑名单

# 检查有没有可疑任务(入侵排查必做)
crontab -l
crontab -l -u root
ls -la /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/
cat /etc/crontab
⚠️ 木马最常用的持久化手段就是写一条 cron 任务。排查入侵时务必检查,详见《服务器被入侵应急排查与处置》。

九、速查

crontab -l                 查看
crontab -e                 编辑
crontab -r                 删除全部
crontab -l -u root         查看 root 的
grep CRON /var/log/syslog  看执行记录

systemctl status cron      服务状态
systemctl restart cron     改时区后要重启
# 模板
分 时 日 月 周 /绝对路径/命令 >> /var/log/xxx.log 2>&1

0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

定时任务需要服务器长期在线。财富云计算 的云服务器 7×24 稳定运行,配合 systemd 可实现开机自启与崩溃自动恢复;内网服务、游戏服务端可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上)。

相关阅读:

  • 《systemd 服务编写与开机自启实战》
  • 《服务器备份与快照策略》
  • 《Linux 日志管理与 logrotate 配置》
延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:Linux 网络排查工具大全:ping、mtr、tcpdump、ss 怎么用
  2. 下一篇:Linux 日志管理与 logrotate 配置:日志撑爆磁盘怎么办
发表评论 8

取消回复