Linux 定时任务
Linux 定时任务
目录
- 软件包安装
- cron:周期性定时任务
- crontab 的格式与语法
- 系统级定时任务:/etc/crontab 与 /etc/cron.d
- cron 服务、日志与排错
- at:一次性定时任务
- sleep + &:简单的一次性延迟
- systemd timer:现代替代方案
- anacron:处理关机导致的漏执行
- crontab 环境变量坑:脚本里 PATH 不完整
- 实战:完整的定时备份示例
- 常见问题排查清单
- 总结与对比
- 参考资料
软件包安装
Rocky/RHEL 的定时任务工具不一定默认安装(尤其 Minimal 安装),使用前先确认并安装。
相关软件包
| 包 | 提供 | 作用 |
|---|---|---|
cronie |
crond、crontab |
主周期性定时任务服务与工具 |
cronie-anacron |
anacron | 支持错过时间的补跑(可选) |
at |
at、atq、atrm |
一次性计划任务 |
检查是否已安装
which crontab at # 看命令是否存在
systemctl status crond # 看 crond 服务是否在运行
rpm -q cronie at # 查看包是否安装
安装命令
# 安装 cronie(含 crontab 和 crond 服务)
sudo dnf install -y cronie
# 安装 at(一次性任务)
sudo dnf install -y at
# 启动并设置开机自启
sudo systemctl enable --now crond
sudo systemctl enable --now atd
其他发行版:Ubuntu/Debian 用
sudo apt install cron和sudo apt install at,服务名是cron。
安装后可用
systemctl status crond确认服务正常。
一、cron:周期性定时任务
1.1 cron 是什么
cron 是一个守护进程(daemon),负责在约定的时间点自动执行用户定义的任务。它对应的服务名在大多数发行版中是 crond。相关的命令与文件包括:
| 命令 / 文件 | 作用 |
|---|---|
crond |
cron 守护进程(服务) |
crontab |
用户管理自己的定时任务 |
/etc/crontab |
系统级定时任务(注意:带用户字段,格式与用户 crontab 不同) |
/etc/cron.d/ |
系统级定时任务的扩展目录(每个文件格式类似 /etc/crontab) |
/etc/cron.hourly/、cron.daily/、cron.weekly/、cron.monthly/ |
按周期执行的脚本目录 |
1.2 crontab 基本管理命令
crontab -l # 查看当前用户的定时任务
crontab -e # 编辑当前用户的定时任务(会用默认编辑器)
crontab -r # 删除当前用户的全部定时任务
crontab -u username # 指定操作某用户的定时任务(需 root)
crontab file # 用文件内容覆盖当前用户的定时任务
- 每个用户有独立的 crontab,文件存在
/var/spool/cron/。 crontab -e保存后立即生效,无需重启服务。- 用
crontab file可以从文件批量导入任务,也常用于脚本化配置(见实战章节)。
参考:
man 1 crontab、菜鸟教程 - Linux crontab
二、crontab 的格式与语法
2.1 5 个时间字段 + 命令
每行一条任务,格式为:
分 时 日 月 周 命令
* * * * * /path/to/command
| 字段 | 含义 | 取值范围 |
|---|---|---|
| 分(minute) | 一小时内的第几分钟 | 0–59 |
| 时(hour) | 一天中的第几小时 | 0–23 |
| 日(day of month) | 一个月中的第几天 | 1–31 |
| 月(month) | 一年中的第几个月 | 1–12 |
| 周(day of week) | 一周中的第几天 | 0–7(0 和 7 都代表星期日) |
2.2 关键语法元素
| 字符 | 含义 | 示例 |
|---|---|---|
* |
任意值(每分钟 / 每小时…) | * * * * * 每分钟 |
, |
列举多个值 | 1,15,30 表示第 1、15、30 分钟 |
- |
区间 | 9-17 表示 9 点到 17 点 |
/ |
步长 | */5 每 5 分钟;0-30/10 在 0–30 分钟内每 10 分钟 |
2.3 日与周是"或"的关系
当"日"和"周"同时指定(都不为 *)时,两个条件满足其一即可执行(OR 关系)。这是 cron 最容易搞错的地方:
0 0 1 * 1 command # 只要"每月 1 日"或"周一"任一满足就执行
也就是说,上例会同时覆盖"每月 1 号"和"每周一"。若你真正想要的是"每月 1 日且正好是周一",用单一时间字段无法精确表达,通常改用脚本内判断(如
date +%d与date +%u同时判断),或拆成更细的逻辑。参考
man 5 crontab原文:"If both fields are restricted (i.e., aren't *), the command will be run when either field matches the current time."
2.4 常见例子
# 每天早上 6 点执行备份脚本
0 6 * * * /usr/local/bin/backup.sh
# 每 5 分钟执行一次
*/5 * * * * /usr/local/bin/healthcheck.sh
# 每周一、周三、周五的 8 点和 20 点执行
0 8,20 * * 1,3,5 /usr/local/bin/report.sh
# 每月 1 号凌晨 1 点执行
0 1 1 * * /usr/local/bin/monthly.sh
# 每个工作日上午 9:30 执行
30 9 * * 1-5 /usr/local/bin/workday.sh
# 每天 3:00 进目录跑清理脚本
0 3 * * * cd /tmp && ./clean.sh
命令最好写绝对路径(如
/usr/bin/find而非find),因为 cron 环境的 PATH 有限(见第九章的坑)。
参考:
man 5 crontab、菜鸟教程 - Linux crontab
三、系统级定时任务:/etc/crontab 与 /etc/cron.d
3.1 /etc/crontab
系统级 crontab 与用户 crontab 的格式不同:它多了一个用户字段(在时间之后、命令之前,指定以哪个用户身份运行):
分 时 日 月 周 用户 命令
0 2 * * * root /usr/local/bin/cleanup.sh
- 只有 root 能编辑
/etc/crontab(sudo vim /etc/crontab)。 - 由于带用户字段,可以用来以任意用户身份调度任务。
3.2 /etc/cron.d/
/etc/cron.d/ 目录里的每个文件格式与 /etc/crontab 相同(也带用户字段),适合按软件/模块拆分系统级任务。例如:
# /etc/cron.d/myapp
30 3 * * * root /usr/local/bin/myapp-maintenance.sh
- 包管理器(rpm/dnf)安装的软件常把自己的定时任务放进
/etc/cron.d/。
3.3 /etc/cron.hourly/、cron.daily/ 等周期目录
系统还提供按周期执行的脚本目录,把脚本(需可执行权限)放进对应目录即可:
| 目录 | 执行周期 |
|---|---|
/etc/cron.hourly/ |
每小时 |
/etc/cron.daily/ |
每天 |
/etc/cron.weekly/ |
每周 |
/etc/cron.monthly/ |
每月 |
这些目录通常由 anacron 驱动,能处理"关机导致的漏执行"(见第八章)。放入脚本时记得
chmod +x并写好 shebang(#!/bin/bash)。
四、cron 服务、日志与排错
4.1 服务管理
systemctl status crond # 查看服务状态(systemd 系统)
systemctl enable --now crond # 开机自启并立即启动
4.2 查看 cron 日志
定时任务执行情况会写入日志,排查"任务没跑 / 报错"主要靠它:
# 传统日志(RHEL/CentOS/Rocky)
sudo tail -f /var/log/cron
# Debian/Ubuntu(旧版 rsyslog)
grep CRON /var/log/syslog
# 用 systemd 看 crond 服务日志
sudo journalctl -u crond --since today
- 日志会记录每次任务的执行时间和命令,以及脚本的输出。
- 若任务没执行,先看这里是否有调度记录。
4.3 常见排错
# 1. 任务是否被调度:看日志
sudo tail -50 /var/log/cron
# 2. 服务是否在运行
systemctl status crond
# 3. crontab 语法是否正确
crontab -l # 检查语法,若语法错会提示
# 4. 手动执行脚本测试
bash /path/to/script.sh # 看是否报错
# 5. 看脚本输出(cron 会把输出发邮件或丢弃)
# 建议在脚本里把输出重定向到文件
* * * * * /path/script.sh >> /var/log/myscript.log 2>&1
排错思路:任务没跑,先看
/var/log/cron有没有调度记录;有记录但脚本没效果,多半是脚本本身或环境变量问题(见第九章)。
五、at:一次性定时任务
at 用于安排只执行一次的未来任务,适合"5 分钟后再做某事""今晚 11 点关服务器"这类场景,与 cron 的周期任务不同。
5.1 基本用法
at 22:00 # 交互式输入命令,Ctrl+D 结束
at now + 5 minutes # 5 分钟后
at 09:00 tomorrow # 明天 9 点
at 12:00 2026-01-01 # 指定具体时间
at midnight / at noon # 午夜 / 正午
通过管道或文件喂给 at 也常用:
echo "systemctl stop myservice" | at now + 10 minutes
at 22:00 < script.sh # 通过文件喂给 at
5.2 管理命令
atq # 查看待执行的一次性任务列表(队列)
atrm <jobid> # 删除指定任务
atq列出待执行任务(含编号),atrm按编号删除。- 输出会通过邮件发给用户(若配置了邮件),或写入日志。
- 服务是
atd,需systemctl start atd(安装章节已enable --now)。
参考:
man 1 at、man 1 atq、man 1 atrm、菜鸟教程 - Linux at 命令
六、sleep + &:简单的一次性延迟
不想装 at、只是"延迟 X 秒后做一次",可以用后台方式:
(sleep 300; /path/to/task.sh) &
- 适合临时、一次性的简单延迟。
- 缺点:依赖当前 shell 存活,进程挂了任务就没了,只适合临时使用。
七、systemd timer:现代替代方案
很多新系统推荐用 systemd timer 取代 cron,因为它有更强的依赖管理、日志、随机延迟、失败重试等能力。
7.1 需要两个文件
任务单元 foo.service(描述要做什么):
[Unit]
Description=My periodic task
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
定时器单元 foo.timer(描述何时触发):
[Unit]
Description=Run foo every hour
[Timer]
OnCalendar=*-*-* 00:01:00 # 每天零点一分
OnBootSec=10min # 开机后 10 分钟(可选)
RandomizedDelaySec=2min # 随机延迟,避免负载尖峰
Persistent=true # 错过后补执行
[Install]
WantedBy=timers.target
7.2 启用与查看
sudo systemctl start foo.timer
sudo systemctl enable foo.timer
systemctl list-timers # 查看所有定时器及下次触发时间
journalctl -u foo.service # 查看任务执行日志
7.3 OnCalendar 语法
OnCalendar 语法与 cron 思路类似但更灵活,常用写法:
Mon..Fri *-*-* 09:00:00:工作日早 9 点*:0/15:每 15 分钟daily、hourly、weekly:关键字
参考:
man 5 systemd.timer、man 7 systemd.time
八、anacron:处理关机导致的漏执行
cron 在关机期间错过的任务不会补跑。anacron 专为解决这一点:开机后检查上次执行时间,如果错过了就补执行。它用于日 / 周 / 月这类低频任务(不处理分钟级)。
- 配置在
/etc/anacrontab。 - 系统里的
/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly等目录,通常就是由 anacron 驱动补跑的(由cronie-anacron包提供,见安装章节)。
九、crontab 环境变量坑:脚本里 PATH 不完整
这是定时任务最常见的坑:脚本在终端里手动运行正常,但 cron 定时跑却报"命令找不到"(command not found)。
9.1 为什么
- 你在终端登录时,shell 会加载
~/.bashrc、/etc/profile等,PATH 很完整(包含 /usr/local/bin、/usr/bin 等)。 - 而 cron 执行脚本时不会加载这些登录环境,PATH 往往只有很有限的值(如
/usr/bin:/bin)。 - 于是脚本里写
mysqldump、docker等非标准路径命令时,cron 里就"找不到"。
9.2 复现与验证
# 在 crontab 里临时加一行,看 cron 环境的 PATH
* * * * * echo "PATH=$PATH" >> /tmp/cron_env.txt
# 等 1 分钟后查看
cat /tmp/cron_env.txt
# 对比终端里的 PATH
echo $PATH
- 你会看到 cron 环境里的 PATH 明显比终端短。
9.3 解决办法
方法一:脚本开头显式设置 PATH(推荐)
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:$PATH
# 脚本其他内容
方法二:脚本里用命令的绝对路径
#!/bin/bash
/usr/bin/mysqldump ...
方法三:在 crontab 里定义 PATH / 其他环境变量(对整个 crontab 生效)
# crontab 顶部(在任务行之前)定义
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
0 2 * * * /usr/local/bin/backup.sh > /tmp/backup.log 2>&1
MAILTO用于设置 cron 输出邮件的收件人;不设置则默认发往本用户邮箱(堆积在/var/spool/mail)。
推荐组合:脚本里用绝对路径或开头
export PATH=...,同时把脚本输出重定向到日志文件,方便排错。
十、实战:完整的定时备份示例
10.1 编写备份脚本
# 编写脚本 /usr/local/bin/daily_backup.sh
sudo cat > /usr/local/bin/daily_backup.sh <<'EOF'
#!/bin/bash
# 每日备份:备份 /var/www,并清理 7 天前的旧备份
export PATH=/usr/local/bin:/usr/bin:/bin
STAMP=$(date +%Y%m%d_%H%M%S)
tar -czf /backup/www_$STAMP.tar.gz -C /var www
find /backup -name 'www_*.tar.gz' -mtime +7 -delete
EOF
# 给脚本可执行权限
sudo chmod +x /usr/local/bin/daily_backup.sh
# 手动执行一次测试(务必先测通再配 crontab)
sudo /usr/local/bin/daily_backup.sh
ls -lh /backup/ # 确认备份文件生成
10.2 追加式写入 crontab
用 crontab file 可以从文件导入。这里用"追加式"写法,把新任务加进现有任务而不覆盖:
# 追加一行,每天凌晨 2:30 执行,输出写日志
(crontab -l 2>/dev/null; echo "30 2 * * * /usr/local/bin/daily_backup.sh >> /var/log/backup.log 2>&1") | crontab -
crontab -l # 验证
crontab -l 2>/dev/null取出当前任务(没有则忽略报错)。echo追加新任务行,两者一起通过管道写入crontab -(从标准输入导入)。
10.3 验证定时任务生效
crontab -l # 确认任务已保存
sudo tail -f /var/log/cron # 看是否按计划执行
ls -lh /backup/ # 确认备份文件生成
脚本要点:开头
export PATH解决环境变量坑;输出重定向到日志便于排查;用-mtime +N清理旧文件防止磁盘占满。
十一、常见问题排查清单
- 任务没执行 → 检查服务运行
systemctl status crond;看日志/var/log/cron。 - 执行了但没效果 → 多半是环境问题(PATH、绝对路径、权限)。
- 脚本没执行权限 →
chmod +x script.sh。 - 时间配错 → 日/周是"或"关系,容易误判。
- 输出卡住 → cron 用邮件发输出,可能堆积在
/var/spool/mail。 - 中文 / 时区 → cron 用的是服务器本地时区,注意
date输出是否与预期一致。
十二、总结与对比
12.1 核心速查
| 需求 | 命令 |
|---|---|
| 安装 | sudo dnf install -y cronie at |
| 编辑当前用户任务 | crontab -e |
| 列出任务 | crontab -l |
| 删除任务 | crontab -r |
| 查看 cron 日志 | sudo tail -f /var/log/cron |
| 一次性任务 | echo 命令 | at 时间 |
| 查看 at 任务 | atq |
| 删除 at 任务 | atrm 编号 |
| 服务状态 | systemctl status crond |
12.2 工具选择对比
| 需求 | 工具 |
|---|---|
| 周期性(分钟 / 小时 / 天 / 周 / 月) | cron / crontab |
| 一次性、指定未来某个时刻 | at |
| 低频且要补跑错过的任务 | anacron |
| 需要依赖控制、日志、随机延迟、现代系统 | systemd timer |
| 简单临时延迟 | sleep + & |
12.3 动手练习建议
# 1. 安装
sudo dnf install -y cronie at
sudo systemctl enable --now crond atd
# 2. 加一个测试任务(每分钟写一行到文件)
crontab -e
# 加:* * * * * echo hello >> /tmp/cron_test.txt
sleep 65; cat /tmp/cron_test.txt # 应能看到内容
# 3. 一次性任务
echo "echo at-test > /tmp/at_test.txt" | at now + 1 minute
atq
# 4. 体验 PATH 坑
# crontab 加:* * * * * echo PATH=$PATH > /tmp/cron_path.txt
# 对比终端 echo $PATH
提示:练习在虚拟机进行。crontab 改错可能导致任务异常,注意先备份(
crontab -l > backup.txt)。
参考资料
- man 手册:
man 5 crontab、man 1 crontab、man 1 at、man 1 atq、man 1 atrm、man 8 crond、man 5 systemd.timer、man 7 systemd.time - Rocky Linux - Automating processes with cron and crontab:https://docs.rockylinux.org/guides/automation/cron_jobs_howto/
- Red Hat - Automating System Tasks(RHEL):https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-Automating_System_Tasks
- 菜鸟教程 - Linux crontab:https://www.runoob.com/linux/linux-comm-crontab.html
- 菜鸟教程 - Linux at 命令:https://www.runoob.com/linux/linux-comm-at.html