
在服务器上跑过运维、写过脚本的人几乎都绕不开两个命令at和crontab。很多新手一开始也分不清这两个东西甚至会把crond误写成crontd、把crontab当成服务名。其实它们俩就是Linux系统里最常用的两类任务计划工具at管一次性任务cron管周期性任务。这篇博文就围绕这两个命令展开把概念、用法、时间语法、调试排错一次性讲清楚不管是刚接触Linux的学生、刚转行的运维新人还是写脚本经常要挂定时任务的开发都能从中找到可以直接抄作业的内容。先说个有意思的点标题里的“crontd”严格来说不是一个标准命令名常见的是crondcron守护进程和crontab编辑任务表命令。很多人打字快了就打成crontd意思大家都懂但为了后续查资料方便我会在正文里把crond、crontab、cron服务这三个概念掰开揉碎讲明白。1. at与cron两类任务计划的定位差异1.1 at一次性任务的首选at命令从名字上就能猜到含义它解决的是“某件事我只想让它做一次做完就完事”的需求。比如下午三点要给某个客户发一封提醒邮件、明天凌晨两点要临时重启一次应用服务、十分钟后要执行一个清理临时文件的脚本这类场景用at再合适不过。at的工作机制是你把任务提交给at守护进程atdatd会把这个任务存起来等到指定时间到了就调用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 atRedHat/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时间回车进入交互模式输入要执行的命令然后按CtrlD结束另一种是用管道或者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 21 | 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-70和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 21这里有个很要紧的习惯脚本里的所有关键路径都要写绝对路径脚本开头可以用#!/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.shDebian系系统还提供了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/cronDebian/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.allowat和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_HOST127.0.0.1 DB_USERbackup_user DB_PASSYourPassword KEEP_DAYS7 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 21这里有三个细节值得学习一是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 21这样做的好处是临时任务用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).bakat任务可以用atq导出列表。这样一旦服务器要迁移或重新初始化几秒钟就能把所有定时任务恢复回来。最后再提醒一点写crontab时一定要留意时区问题多读几遍man手册多在小范围环境里测试不要直接在百台服务器上批量推送一个格式错误的crontab。学这两个命令不难难的是把周期性任务的设计思想融入到日常运维流程里。希望这篇文章能让你下次遇到任务调度问题时心里先有张完整的地图。