Linux定时任务全攻略:at一次性任务与crontab周期性任务详解
2026/9/18 12:08:47 网站建设 项目流程

在服务器上跑过运维、写过脚本的人,几乎都绕不开两个命令:at和crontab。很多新手一开始也分不清这两个东西,甚至会把crond误写成crontd、把crontab当成服务名。其实它们俩就是Linux系统里最常用的两类任务计划工具:at管一次性任务,cron管周期性任务。这篇博文就围绕这两个命令展开,把概念、用法、时间语法、调试排错一次性讲清楚,不管是刚接触Linux的学生、刚转行的运维新人,还是写脚本经常要挂定时任务的开发,都能从中找到可以直接抄作业的内容。

先说个有意思的点:标题里的“crontd”严格来说不是一个标准命令名,常见的是crond(cron守护进程)和crontab(编辑任务表命令)。很多人打字快了就打成crontd,意思大家都懂,但为了后续查资料方便,我会在正文里把crond、crontab、cron服务这三个概念掰开揉碎讲明白。

1. at与cron:两类任务计划的定位差异

1.1 at:一次性任务的首选

at命令从名字上就能猜到含义,它解决的是“某件事我只想让它做一次,做完就完事”的需求。比如下午三点要给某个客户发一封提醒邮件、明天凌晨两点要临时重启一次应用服务、十分钟后要执行一个清理临时文件的脚本,这类场景用at再合适不过。

at的工作机制是:你把任务提交给at守护进程(atd),atd会把这个任务存起来,等到指定时间到了就调用shell去执行。任务执行完之后,atd就把这个任务记录清除掉,不会留下任何周期性的残留。这个特性跟cron有本质区别,cron是“到点就执行,然后等待下一个周期”,at是“执行完就结束”。

1.2 cron:周期性任务的常驻管家

cron这套体系就有点像一个常驻的管家,核心守护进程是crond,它会每分钟检查一次系统里配置的所有任务表,看哪些任务到了执行时间,到了就拉起执行。任务表的总入口分成两个层级:一个是系统级的/etc/crontab,另一个是用户级的crontab -e管理的用户任务表。

用户级任务表通常存在/var/spool/cron/目录下,文件名就是用户名,例如/var/spool/cron/root。这里要特别提醒:这些用户任务表不应该被直接用vim暴力修改,标准做法是使用crontab -e命令,因为它会帮你做语法检查、权限校验,还能防止并发写入导致任务丢失。很多老手都踩过“直接改文件结果任务不生效”的坑,原因多半就是权限或者文件格式不对。

1.3 选型判断:什么时候用at,什么时候用cron

我的经验是:判断标准就一句话,看这个任务是“一次性”还是“周期性”。如果这个任务需要定期重复执行,比如每天凌晨备份数据库、每周一拉取一次数据报表、每月一号清理一次日志,那就用cron。如果只是临时的一次性动作,比如系统维护窗口结束后重启某个服务、两小时后删除一个临时目录,那就交给at。

这里有个容易忽略的点:at和cron不是互斥的关系,它们经常配合使用。比如你可以在cron里设置一条“每周五晚上检查磁盘空间”的任务,如果发现磁盘空间超过阈值,就通过at安排一个“半小时后执行一次完整的大文件清理扫描”。这样既保证了周期性检查,又避免了清理动作影响正常的业务高峰。

另外,如果你看到的是“crontd”这个拼写,大概率是指crond,偶尔也有人把它当成crontab的简称。理解的时候不要纠结命令名,先分清三层意思:crond是守护进程,crontab是管理命令,cron是一整套定时任务机制。

2. at命令的完整实操指南

2.1 at命令的安装与守护进程

at要能工作,首先得有atd守护进程。不同的发行版安装方式稍有不同,Debian/Ubuntu系可以直接用apt install at,RedHat/CentOS系则是yum install at或dnf install at。装完之后,注意启动atd并把它设为开机自启,否则你at提交的任务到时间了根本不会执行。

# Debian/Ubuntu apt install -y at systemctl enable --now atd # RHEL/CentOS系的写法 yum install -y at systemctl enable --now atd

检查atd是否运行,可以用systemctl status atd,能看到active (running)就说明正常。这里有个小细节:atd默认是每分钟唤醒一次检查任务列表,所以你设置的任务如果是“下一分钟”执行,实际误差在几十秒内,这是正常的。

2.2 at交互模式与命令行模式

at命令有两种写法:一种是直接at时间回车,进入交互模式,输入要执行的命令,然后按Ctrl+D结束;另一种是用管道或者echo把命令喂进去,脚本化程度更好。

# 交互式用法 at 20:00 warning: commands will be executed using /bin/sh at> echo "hello" > /tmp/hello.txt at> <EOT> job 1 at Fri Jun 20 20:00:00 2025 # 管道非交互用法 echo "systemctl restart nginx" | at 23:30

用管道方式写的输出会比较干净,适合在脚本里使用。不过要注意:at默认的shell是/bin/sh,不是你的bash,如果某些命令是bash内置特性,可能就没法直接用,建议把复杂逻辑写成一个脚本,再用at执行这个脚本。

2.3 at时间语法详解

at的时间语法非常灵活,很多人用不习惯是因为不知道它支持“模糊时间表达”。除了标准的HH:MM之外,它还能识别noon、midnight、teatime(下午四点,冷知识)、now + 数字 + 时间单位等写法。

# 常见时间格式示例 at 14:30 # 今天下午2:30,如果已经过了,就是明天 at 14:30 2025-07-01 # 指定日期 at noon # 中午12点 at midnight # 午夜0点 at now + 5 minutes # 5分钟之后 at now + 2 hours # 2小时之后 at 9:00 AM tomorrow # 明天早上9点 at 15:00 next month # 下个月15号

这些模糊表达在实际运维中非常高效,尤其是now + N minutes这种格式,写自动化脚本的时候特别好用。再补充一个坑:如果你的系统语言不是英文,at的模糊时间词可能识别不了,稳妥的办法还是使用标准HH:MM或now + N格式。

2.4 管理任务:atq与atrm

任务提交之后,想查看还没执行的任务列表,用atq(有的系统也叫at -l),输出会显示任务的编号、执行时间、提交用户等信息。如果想取消任务,用atrm加任务编号。

atq 2 Fri Jun 20 09:30:00 2025 a root 3 Fri Jun 20 23:00:00 2025 a root # 删除编号为2的任务 atrm 2

如果任务已经排了一堆,想全部清空,可以用atrm $(atq | awk '{print $1}'),不过操作前务必确认这些任务确实都没用了。

2.5 任务执行时的环境与输出处理

at执行任务的时候,环境变量使用的是提交时的用户环境,但有几个细节需要注意。第一,at会把标准输出和标准错误以邮件形式发给当前用户,如果你的系统没配邮件服务,输出就会在/var/spool/mail/用户名文件里越堆越多,长年累月会占不少磁盘空间。建议在任务命令末尾加上重定向,把输出丢到日志文件。

echo "/usr/local/bin/clean_tmp.sh >> /var/log/at_task.log 2>&1" | at now + 10 minutes

第二,如果要执行的是自定义脚本,脚本里的路径建议全部写绝对路径,因为at执行的PATH可能跟你手动登录时不一样,很多自定义安装的程序(比如/usr/local/bin下的工具)默认情况下根本找不到。

3. cron全家桶:crontab与crond实战

3.1 crontab命令:用户的定时任务入口

crond这个服务是整套cron机制的核心,它一秒钟都不休息,系统启动后就会按每分钟一次的节奏去扫任务表。用户要定义自己的周期性任务,标准入口是crontab命令。

crontab -e # 编辑当前用户的任务表 crontab -l # 查看当前用户的任务表 crontab -r # 删除当前用户的所有任务,慎用 crontab -u username -e # 编辑指定用户的任务表,需要root权限

首次运行crontab -e会让你选一个编辑器,建议选vim或nano,看个人习惯。如果不想每次被提示,可以在shell配置里设置EDITOR环境变量。

3.2 五个时间字段的完整解读

crontab每行任务的基本结构是五个时间字段加一个命令字段,顺序依次是分、时、日、月、星期。这五个字段也是新手最容易翻车的地方,我见过太多人把顺序记反,把分和时搞混。

分(0-59) 时(0-23) 日(1-31) 月(1-12) 星期(0-7,0和7都代表周日) 命令

常用的特殊语法有星号、逗号、减号、斜杠,组合起来能实现非常丰富的周期。下面给几个典型例子:

# 每天凌晨2点30分执行备份 30 2 * * * /opt/scripts/backup.sh # 每小时的第10分钟执行一次,注意是每小时一次不是每分钟 10 * * * * /opt/scripts/hourly_check.sh # 每五分钟执行一次 */5 * * * * /opt/scripts/check.sh # 周一至周五每天早上9点和下午6点各执行一次 0 9,18 * * 1-5 /opt/scripts/workday.sh # 每个月1号和15号的凌晨3点15分执行 15 3 1,15 * * /opt/scripts/bill.sh # 每月的第一个星期一,如果有特别的需求,需要配合脚本判断日期 0 4 * * 1 [ $(date +\%d) -le 7 ] && /opt/scripts/first_monday.sh

最后那条需要特别注意:crontab的命令行中的百分号%是有特殊含义的,它会代表换行,如果想在命令行里直接用date命令输出日期,等于号后面要用%转义。这一点是很多人反复踩坑的地方。

3.3 使用脚本替代命令行

虽然crontab可以直接写命令,但我强烈建议:凡是业务逻辑超过一条命令的,一律把命令写进一个独立脚本,然后在crontab里只保留一句脚本调用。这样好处非常明显,一是调试方便,可以直接手动执行脚本看输出;二是crontab里没有复杂的转义问题;三是权限和日志管理更清晰。

# 脚本 /opt/scripts/backup.sh 一定要加执行权限 chmod +x /opt/scripts/backup.sh # crontab -e 里写 20 1 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

这里有个很要紧的习惯:脚本里的所有关键路径都要写绝对路径,脚本开头可以用#!/bin/bash强制指定解释器。为什么强调这个?因为cron执行脚本时的PATH环境变量通常极简,只有/usr/bin和/bin,你自己安装的MySQL、Python、Node都可能不在PATH里,如果不写绝对路径,脚本在终端手敲能跑,一进cron就报command not found。

3.4 系统级cron与crond服务管理

除了用户级任务表,cron体系还提供系统级任务表/etc/crontab和一些/etc/cron.d/、/etc/cron.hourly等目录。两者的核心区别是:用户级crontab没有用户名字段,系统级crontab在时间字段后面要指定执行用户。

# /etc/crontab 示例,注意比用户级多了一个用户字段 30 2 * * * root /opt/scripts/backup.sh

Debian系系统还提供了cron.d目录和cron.hourly/cron.daily/cron.weekly/cron.monthly目录,这些目录下放脚本,系统会按照预设周期自动执行。如果是日常的个人服务器或开发机,一般用户级crontab就够了,系统级适合做全局任务或者要指定多个不同用户执行任务的情况。

服务管理方面,关注这几个命令就够用了:

systemctl status crond systemctl restart crond systemctl enable crond

注意CentOS 7之后的系统服务名叫crond,而Debian/Ubuntu系统服务名是cron,两者都是同一个东西,只是命名习惯不同。

3.5 日志与调试技巧

排查cron问题最常用的就是看日志。RHEL/CentOS系日志通常在/var/log/cron,Debian/Ubuntu系需要确认rsyslog是否记录了cron相关条目,一般也在/var/log/syslog里搜cron关键字。

# CentOS/RHEL tail -f /var/log/cron # Debian/Ubuntu,可先过滤,再辅助看 grep CRON /var/log/syslog | tail -50

查看日志时,重点看有没有“(root) CMD (...)”这样的条目,出现说明任务已经提交给shell执行了。如果日志里连CMD条目都没有,说明crontab里的任务根本没被加载,优先检查任务表格式;如果CMD出现了但脚本实际没生效,就要去查脚本日志和脚本退出码。

4. 常见问题与排查技巧实录

4.1 at任务不执行怎么办

at任务到了时间没执行,我遇到过的原因大致有这么几种。第一,atd服务没有启动,这最容易忽略,只要systemctl start atd并设为开机自启就能解决。第二,时间格式写错导致at把任务排到了很久以后,用atq就能看出来实际执行时间和你预期是否一致。第三,命令里的程序路径不对,也就是前面反复强调的PATH问题,脚本里最好把所有外部程序都换成绝对路径。

还有一个偏门的情况:在某些系统上,如果内存不足或者磁盘inode耗尽,atd也可能无法正常生成任务记录。查看/var/log/syslog或对应的journal日志能发现端倪。

4.2 crontab任务不生效的十大原因汇总

crontab任务不生效是运维日常最高频的问题,我把常见原因整理成一张速查表,方便照着排查:

原因分类具体表现解决方案
时间格式错误六段式写成五段式用户级crontab必须五段式,别加用户字段
环境变量缺失手动能跑,定时跑不起来脚本内写绝对路径,必要时在脚本开头source环境变量
脚本权限不足脚本没有执行权限chmod +x 脚本
解释器错误脚本第一行没有#!/bin/bash显式指定解释器
输出重定向问题日志文件没写权限重定向路径改成/root或/tmp等有权限的路径
任务被注释#号开头注释掉编辑crontab -e取消注释
语法转义错误%号被解释为换行命令行中的%要写成%
系统时间错误机器时间和预期不符date -s或配置ntp/chrony同步时间
服务未运行crond没启动systemctl status crond确认
特殊字符缺失用了通配符被shell解析命令统一放进脚本,避免crontab里做变量和通配

4.3 时间漂移与服务状态检查

有个容易被忽视的问题,就是服务器时区和时间漂移。如果你的cron任务定义的是“每天早上8点执行”,但服务器的时区是UTC,那实际执行时间会和你所在的北京时间相差8个小时。很多新手在国内买海外服务器时最容易踩这个坑,建议安装完系统后第一件事就设置时区。

# 查看当前时区 timedatectl # 设置时区为上海 timedatectl set-timezone Asia/Shanghai # 手动同步时间 chronyc makestep

日常巡检时建议把systemctl status crond和timedatectl两个命令加进去,一分钟就能排除两类常见故障。

4.4 安全:at.allow与cron.allow

at和crontab默认允许所有本机用户使用,这在多用户服务器上可能会有安全问题。Linux提供了两个控制文件来限制用户的权限:/etc/at.allow和/etc/at.deny、/etc/cron.allow和/etc/cron.deny。如果allow文件存在,那么只有文件里列出的用户能够使用相应命令;如果allow文件不存在,deny文件里的用户则被禁止使用。

# 只允许root和backup使用cron echo "root" > /etc/cron.allow echo "backup" >> /etc/cron.allow

这是运维里一个非常容易被忽略的加固项。四五台的服务器还好,几十台上百台的服务器,如果谁能随便提交定时任务,风险就非常大了。建议统一收口,普通业务用户只开放执行权,不要开放提交权。

5. 真实场景案例与经验补充

5.1 实操案例一:数据库每日自动备份

这个例子是完全可以用在真实服务器上的。假设要每天凌晨2点20分备份一个MySQL数据库,并且保留最近7天的备份文件。

先写备份脚本/opt/scripts/db_backup.sh,内容如下:

#!/bin/bash BACKUP_DIR=/data/backup/mysql DB_HOST=127.0.0.1 DB_USER=backup_user DB_PASS='YourPassword' KEEP_DAYS=7 DATE_STR=$(date +\%Y\%m\%d_\%H\%M\%S) mkdir -p "${BACKUP_DIR}" # 如果库特别大,建议用xtrabackup方案,这里用mysqldump是简单可靠的做法 mysqldump -h"${DB_HOST}" -u"${DB_USER}" -p"${DB_PASS}" --single-transaction --routines --triggers dbname | gzip > "${BACKUP_DIR}/dbname_${DATE_STR}.sql.gz" # 只保留最近7天的备份 find "${BACKUP_DIR}" -name "dbname_*.sql.gz" -mtime +"${KEEP_DAYS}" -delete

然后crontab -e里加一行:

20 2 * * * /opt/scripts/db_backup.sh >> /var/log/db_backup.log 2>&1

这里有三个细节值得学习:一是mysqldump加--single-transaction可以避免锁表影响线上业务;二是脚本里date格式的%写法是给crontab转义预留的;三是日志单独落盘,出问题好排查。如果发现mysqldump命令路径不对,可以先which mysqldump,然后把脚本里换成绝对路径。

5.2 实操案例二:临时任务与周期性任务的组合玩法

有次生产环境磁盘告警,I/O等待特别高,我判断是大文件扫描会影响业务,于是没有立刻全盘清理,而是用at把扫描任务推迟到凌晨业务低谷执行,同时用cron先挂了一个磁盘使用率检查任务,超过85%就告警。

# 凌晨2点执行一次深度扫描并记录大文件 echo "du -ah /data | sort -rh | head -30 > /tmp/disk_report_$(date +\%Y\%m\%d).txt" | at 02:00 # crontab里挂一个每10分钟的磁盘告警检查 */10 * * * * /opt/scripts/disk_alert.sh >> /var/log/disk_alert.log 2>&1

这样做的好处是:临时任务用at精确排期,不影响当前白天的业务;周期告警用cron长期生效,避免问题复发。两者配合,既能快速止血,又能长效监控。如果你在线上环境遇到“临时性能问题但不想手动守夜”的场景,这个思路可以直接套用。

5.3 关于“crontd”这个写法的一些补充

既然标题里出现了crontd这个词,我想提一句:在真正的Linux命令体系里,没有crontd,只有crond(守护进程)和crontab(管理命令的入口)。如果你在阅读一些BBS帖子或视频脚本时看到crontd,基本就是作者打字时手滑了。搞懂这个之后,再去搜索资料、查man手册,心里更有底。man crontab、man 5 crontab可以分别查看命令用法和任务表格式的完整说明,这是最权威的参考资料。

5.4 我的几点独家心得

写到这里,我再分享几个我自己踩坑后总结的经验。第一次调试cron任务,不要盯着日志瞎猜,最好的办法是把crontab里的命令改成“记录时间戳到文件”的先验证一下,确认任务真的在跑,再去排查脚本逻辑。比如先写一行* * * * * echo $(date) >> /tmp/cron_test.log,等一分钟看文件有没有变化。

还有一个习惯非常值得养成:每台服务器上都建一个/var/log/task_logs目录,把不同任务脚本的日志按名字分开写,比如clean.log、backup.log、sync.log,出问题找日志非常高效。不要全部堆到/var/log/messages里,因为你不可能为了找一个任务的输出去翻几百MB的系统日志。

另外,at和cron的任务文件都要养成备份习惯。crontab -l可以导出任务:crontab -l > /backup/crontab_$(date +%F).bak,at任务可以用atq导出列表。这样一旦服务器要迁移或重新初始化,几秒钟就能把所有定时任务恢复回来。

最后再提醒一点:写crontab时一定要留意时区问题,多读几遍man手册,多在小范围环境里测试,不要直接在百台服务器上批量推送一个格式错误的crontab。学这两个命令不难,难的是把周期性任务的设计思想融入到日常运维流程里。希望这篇文章能让你下次遇到任务调度问题时,心里先有张完整的地图。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询