Linux定时任务实战:at与cron配置、原理与避坑指南

发布时间:2026/10/1 22:33:43
Linux定时任务实战:at与cron配置、原理与避坑指南 Linux 上安排定时任务绕不开at和cron这对老搭档。at管一次性任务cron管周期性任务配合使用能把运维里那些“凌晨执行”“每小时检查”之类的活全自动掉。很多新手一上来就冲去装第三方调度工具其实系统自带的这两个命令配合得当就足够覆盖绝大多数日常场景。这篇文章我就结合自己在服务器上跑过的真实任务把at、cron的配置方法、时间规则、日志排查和常见坑点一次讲清楚。如果你正在准备 Linux 运维相关的内容这一块也几乎是必问的基础题值得系统过一遍。1. 一次性和周期性任务at 与 cron 的分工1.1 为什么需要两个工具先看两个实际需求。第一件事今晚 11 点半要把数据库备份完并且把备份文件同步到另一台机器这个操作只执行一次明天不会再有。第二件事每天早上 9 点检查磁盘空间超过阈值就发告警这个操作每天都要重复。前者用at后者用cron。两者一起类比就是“一次性闹钟”和“循环闹钟”的区别。at由atd守护进程负责管理它维护一个待执行任务队列到点后执行并丢弃该任务。cron则由crond守护进程负责它每分钟扫描一次系统里所有用户的 crontab 文件匹配当前时间与时间字段命中就执行任务然后继续等下一分钟。从这个机制能看出一个关键差异at更适合“我明确知道某个未来时刻要做一次什么”的场景cron则适合“频率固定、长期存在”的场景。很多初学 Linux 的人习惯把所有定时任务都塞进 crontab结果临时任务也要为它保留一条规则既不直观也容易让 crontab 越来越乱。我自己的习惯是超过一次、需要重复调度的才进 cron只跑一次的临时维护一律at。1.2 场景怎么选最省心选型没有绝对标准但有个简单的判断逻辑问自己“这个任务下周、下个月还会存在吗”如果答案是“不会”就走at。例如计划内维护窗口重启服务、临时跑一次数据修复脚本、明天凌晨单独做一次增量备份都是典型的一次性场景。如果答案是“会”再用 cron。比如日志清理、监控指标采集、定时同步、每日报表生成这些频率相对固定写成 cron 规则后基本不用再改。还有一类任务要特别注意如果服务器在计划时间点正好关机错过之后能不能补跑。cron 配合 anacron 可以在开机后把错过的周期任务补跑但at任务错过时间之后通常不会自动补执行。这个差异在选型时就要想清楚不要等到真的关机错过了才后悔。2. 核心概念和配置底座2.1 at 命令的基本形态与 atd 服务大多数发行版默认装了at但atd服务不一定开机自启。先用systemctl status atd看一眼如果没启动就执行systemctl enable --now atd很多“at 不生效”的问题根源就是服务没起来任务虽然排进队列了但没有任何进程去消费。at的交互式用法很简单[roothost ~]# at 23:30 warning: commands will be executed using /bin/sh at /usr/local/sbin/mysql_backup.sh at EOT job 5 at Thu Jun 12 23:30:00 2025输入命令后按CtrlD提交。注意不要按CtrlC那是中断当前输入任务不会被提交。at支持的时间格式很灵活除了标准的HH:MM还支持now 5 minutes、09:00 tomorrow、3pm 2 days以及midnight、noon、teatime这类语义化时间。我在服务器上最常用的还是now 5 minutes这种相对时间方便给命令留一点准备空间。2.2 crontab 的编辑方式与系统级 crontabcron 的用户级配置文件用crontab -e编辑。第一次执行时会让你选择编辑器我建议直接设置EDITORvim省得每次都被问。常用操作有四个crontab -e # 编辑当前用户的定时任务 crontab -l # 查看当前用户的定时任务 crontab -r # 删除当前用户所有定时任务 crontab -u other # 管理员编辑其他用户的定时任务用户级 crontab 每行格式是五个时间字段加一条命令分 时 日 月 周 命令五个时间字段分别对应分钟、小时、日期、月份、星期取值范围是分钟 0-59小时 0-23日期 1-31月份 1-12星期 0-7其中 0 和 7 都表示周日。除了用户级 crontab系统级还有/etc/crontab和/etc/cron.d/目录下的文件它们的格式会比用户级多一个用户字段分 时 日 月 周 用户 命令系统最后还会自带/etc/cron.hourly/、/etc/cron.daily/、/etc/cron.weekly/、/etc/cron.monthly/四个目录把脚本丢进去就能按对应周期执行适合存放不需要精确到具体几分几秒的日常清理脚本。2.3 五分钟搞懂 cron 表达式很多人提到“cron 表达式”时会想到开发框架里的0 30 2 * * ?这种写法这其实是两套体系。Linux 系统自带的 cron 用的是五段格式而 Java Quartz、Python APScheduler 等工具通常用六段或七段格式多了一个秒字段类型格式示例系统 crontab分 时 日 月 周30 2 * * *表示每天 2:30Quartz 等秒 分 时 日 月 周 年0 30 2 * * *表示每天 2:30:00在系统 crontab 里*表示任意值1,15表示列举1-5表示区间*/10表示每隔 10 个时间单位。比如*/5 * * * *是每 5 分钟执行一次0 9-18 * * 1-5是工作日每天 9 点到 18 点整点执行。如果写自定义程序时用到带秒的 cron 表达式记得两者别混否则容易把“每天 2 点 30 分”错写成“每天 30 分 2 秒”。3. 从零实操常用调度任务的完整步骤3.1 用 at 安排一次性任务先演示非交互式提交这是我最常用的一种方式echo /usr/local/sbin/mysql_backup.sh | at 23:30有多个命令要执行时可以用at -f直接把脚本文件丢给atat -f /usr/local/sbin/backup_and_sync.sh 23:30at默认使用/bin/sh来执行命令如果你的脚本里有数组、[[ ]]等 bash 特性脚本的首行必须写#!/bin/bash并赋予可执行权限。另一个容易踩的坑是at执行任务时的工作目录是用户的家目录而不是你提交任务时所在的目录。所以脚本里涉及相对路径时一定要小心最稳妥的做法是脚本内部先cd到固定目录再用绝对路径操作文件。3.2 使用 atq、atrm 管理待执行任务任务提交后怎么确认排上了、怎么删掉atq负责列出当前用户的待执行任务5 Thu Jun 12 23:30:00 2025 a root第一列是任务编号第二列是执行时间最后一列是用户名。删除时用atrm 5即可。如果要查看所有用户的 at 任务root 直接执行atq也能看到全部。这里有个经验删除前最好先看时间列确认一下编号尤其是同时排了好几个任务时。误删之后不能恢复只能重新提交。另外at任务存放在/var/spool/at/或/var/spool/atd/目录下每个任务对应一个文件如果你知道任务编号也可以直接去目录里确认文件创建时间但这属于兜底手段日常还是用atq更靠谱。3.3 用 crontab 创建周期任务创建一个 cron 任务建议先写脚本再在 crontab 里引用脚本而不是把一长串命令直接写在 crontab 行里。这样便于调试和复用。举个例子我的一台数据库服务器上有个备份脚本/usr/local/sbin/mysql_backup.sh#!/bin/bash BACKUP_DIR/data/backup/mysql DATE$(date %F) mysqldump -uroot -p123456 mydb $BACKUP_DIR/mydb_$DATE.sql find $BACKUP_DIR -name mydb_*.sql -mtime 7 -delete然后编辑 crontabMAILTO PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 30 2 * * * /usr/local/sbin/mysql_backup.sh /var/log/mysql_backup.log 21MAILTO表示不把任务输出发到邮箱PATH...指定任务执行时的常用路径这两个放在 crontab 文件头部对后面的所有任务生效。末尾的 /var/log/mysql_backup.log 21是把标准输出和错误都追加到日志文件避免输出无处可去。保存后用crontab -l查看确认任务就会按规则执行了。3.4 设置任务执行权限系统默认可能允许所有普通用户使用 crontab生产环境我一般建议收敛权限。相关控制文件是/etc/cron.allow和/etc/cron.denyat对应的是/etc/at.allow和/etc/at.deny。规则是这样的如果allow文件存在只有文件中列出的用户可以使用如果allow不存在但deny存在除了 deny 中列出的用户之外都可以使用如果两个文件都不存在不同发行版的默认策略会有差别有的只允许 root有的允许所有用户。为了避免默认策略不明确我会在初始化的服务器上显式创建cron.allow里面只写需要的账号再创建cron.deny写入all这种注释性的拒绝兜底。注意/etc/cron.d/和系统级 crontab 只有 root 能编辑普通用户的权限只限于自己的crontab -e。4. 运行原理与关键参数背后的为什么4.1 cron 的时间匹配逻辑日期和星期是“或”不是“且”这是很多老手都会踩的坑。看下面这条规则30 2 1 * 1直觉上这是“每月 1 号的 2:30 执行并且要求当天是周一”但 cron 的实际逻辑不是这样。当日期字段和星期字段同时被限制不是*时只要其中一个匹配就会执行。也就是说这条规则的实际效果是“每月 1 号的 2:30 执行一次并且每个周一的 2:30 也要执行一次”。如果你真的需要“每月 1 号且正好是周一”才执行cron 本身表达不出来要在脚本里加判断。常见写法是这样30 2 1 * * [ $(date \%d) 01 ] [ $(date \%u) 1 ] /usr/local/sbin/weekly_report.sh这里date \%d在 crontab 里必须把%转义成\%否则会被 cron 当成换行符这一点后面还会专门讲。我最初遇到这问题时以为系统计算逻辑出了 bug后来查了 man 手册才发现是“或”匹配。记住这个特性很多诡异的重复执行现象都能解释清楚。4.2 环境变量和 PATH 对任务的影响手动在终端执行脚本没问题放 cron 里就报“command not found”这是出现频率最高的 cron 问题。原因在于 cron 执行任务时是一个极简的 shell 环境默认 PATH 通常只有/usr/bin:/bin。你手动登进去时/etc/profile、~/.bash_profile等加载出来的路径cron 一概不认。比如装在/usr/local/mysql/bin下的mysqldumpcron 任务里直接写mysqldump就会找不到。解决思路有两种。第一种是在 crontab 头部显式设置 PATH我前面已经给出示例第二种是在脚本开头 source 系统环境#!/bin/bash source /etc/profile需要提醒的是~/.bashrc在非交互执行时并不一定会被读取所以不要依赖它。at的环境也有类似问题虽然 at 能保留一部分提交时的环境变量但脚本的独立性同样重要。我的原则是凡是写进调度系统的脚本都当它在一个空白环境里执行所有命令尽量用绝对路径所有环境变量要么在脚本内部设置要么显式 source。4.3 输出去向与日志体系cron 默认会把任务运行时的标准输出和错误以邮件形式发送给用户。如果服务器没有配置邮件服务这些邮件会在本地/var/mail/目录下堆积既占磁盘又起不到通知作用。所以在 crontab 里我基本都会设置MAILTO并且把输出重定向到专门的日志文件。日志文件要定期轮转。最简单的方式是让 logrotate 接管系统日志目录但很多自定义日志不在默认列表里。更实用的做法是脚本自己控制日志大小比如备份脚本每天覆盖前一天的文件或者定期清理超过 N 天的日志。对于任务本身有没有跑cron 的运行记录在/var/log/cronRHEL/CentOS 系或/var/log/syslogDebian 系里。前者可以直接查看后者要经过grep CRON过滤。at 的执行记录通常在系统日志中也能看到但很多发行版日志默认不打印 atd 的细节所以 at 任务我一般会在脚本开头主动输出一行“开始执行”的信息。5. 常见问题与排查技巧实录5.1 任务没执行先查这五件事遇到定时任务没有按预期执行我建议按顺序查第一确认 crontab 规则真的保存进去了。用crontab -l看输出检查时间字段顺序是不是“分 时 日 月 周”。常见错误是写成“时 分”结果任务在错误的整点或半点触发。第二确认 cron 服务在运行。执行systemctl status crond或systemctl status cron两个服务名在不同发行版有差异。服务没启动规则写得再对也不会跑。第三确认脚本有可执行权限、语法没问题。ls -l /usr/local/sbin/mysql_backup.sh看权限位手动执行一次脚本确认没有语法错误和路径问题。第四看日志。RHEL 系直接tail -n 50 /var/log/cronDebian 系用grep CRON /var/log/syslog。日志里能看到 cron 触发了哪条任务、用户是谁、执行结果有没有报错。第五如果脚本内部有输出但重定向的日志文件是空的说明可能根本没有走到脚本内部或者脚本在早期就 exit 了。我通常会在脚本开头加一行echo $(date) start /var/log/custom.log一旦任务真的执行这个文件里立刻能看到时间戳排查效率高很多。5.2 经典坑位百分号、换行符、重复执行百分号是 crontab 里最隐蔽的坑。在 crontab 文件中未转义的%会被 cron 替换成换行符。比如你想记录当前日期* * * * * /usr/bin/date %F /tmp/date.log实际执行时会被解析成两行命令语法直接出错。正确写法是* * * * * /usr/bin/date \%F /tmp/date.log所以只要命令中出现date %F、date %T这类格式符一定要记得转义。我后来写 crontab 的习惯是凡是涉及%的命令一律包成脚本在脚本里就不用管转义还能顺便解决路径和环境变量问题。另一个经典问题是 Windows 换行符。如果你在 Windows 下编辑过脚本再传到 Linux文件里会带着\r执行时经常报bad interpreter或$\r: command not found。处理办法是执行sed -i s/\r$// script.sh或者安装dos2unix转一下。关于重复执行如果你的备份任务执行耗时超过调度周期cron 会按时再触发一次造成多个实例同时跑。轻则浪费资源重则两个备份互相覆盖、数据库锁冲突。所以耗时不定的任务一定要加锁机制这属于生产环境的基本素养。5.3 排查工具与日志速查表整理一张速查表便于对照排查内容查看位置常用命令crond/atd 服务状态systemd 状态systemctl status crond atdcrontab 内容用户级配置crontab -lat 待执行队列at 队列atqcron 执行记录RHEL 系日志tail -n 100 /var/log/croncron 执行记录Debian 系日志grep CRON /var/log/syslogat 执行记录系统 journaljournalctl -u atd脚本自定义日志自定义路径tail -f /var/log/xxx.log这套组合基本能覆盖 90% 的定时任务排查场景。过程中如果怀疑是环境变量问题还可以临时在 crontab 那条命令前面加上bash -x让脚本输出调试信息到日志定位速度会明显加快。6. 进阶玩法与个人实践经验6.1 用锁和幂等保护敏感任务生产环境的备份、发布、数据同步类任务需要保证同一时间只有一个实例在跑。Linux 自带的flock是做一个轻量锁的好选择。给前面的备份脚本加上锁的开头#!/bin/bash exec 9/var/lock/mysql_backup.lock flock -n 9 || exit 0 BACKUP_DIR/data/backup/mysql DATE$(date %F) mysqldump -uroot -p123456 mydb $BACKUP_DIR/mydb_$DATE.sql find $BACKUP_DIR -name mydb_*.sql -mtime 7 -deleteexec 9...是打开一个文件描述符flock -n 9尝试给这个描述符加排他锁加不上就说明已经有另一个实例在跑直接退出。这样任务即使被 cron 重复触发也不会产生并发实例。幂等性同样重要。任务尽量设计成“重复执行结果一致、不产生破坏性副作用”比如备份脚本覆盖同名文件而不是无限堆叠同步脚本先校验再写入。做不到完全幂等时至少加上时间戳和去重逻辑降低重复执行带来的风险。6.2 调度方案对比at/cron 之外还有什么Linux 自带方案之外还有几个常见替代品方案特点适用场景systemd timer支持精确触发、随机延时、依赖服务、日志集中系统服务型任务需要跟踪状态Python APScheduler程序内调度支持 session 式持久化业务代码内部任务Java Quartz成熟cron 表达式带秒字段Java 应用内定时任务Airflow 等调度平台重依赖、可视化、重试、DAG大数据工作流、复杂任务编排我并不是说 cron 一定比它们好而是要看场景。如果只是让服务器“每天几点跑个脚本”引入一套调度平台反而增加维护成本。反过来如果任务之间有关联、需要失败重试和任务流编排硬写 cron 也会非常痛苦。先用最简单的方案跑起来复杂需求出现时再平滑迁移这才是务实的做法。6.3 我踩过坑后养成的固定习惯最后分享几个我吃了不少亏之后养成的习惯。第一新建任何一个定时任务第一周都强制观察。任务执行后去看一眼日志和结果文件确认它真的按预期跑了。定时任务最大的危险是“没人知道它某天悄悄失败了”前期多看一眼能避免后期积累成大事故。第二每次修改 crontab 前先备份。执行crontab -l crontab.bak.$(date %Y%m%d)改坏了还能快速还原。这是最小成本的保险。第三删除 at 任务前先看atq确认编号。我有一次手快敲错编号把一个还没到期的迁移任务删了最后只能重新排。好在 at 任务内容简单如果是大数据量的操作重新排期的成本会很高。我现在维护的服务器上真正长期跑的自动任务绝大多数还是 crontab 里的几十行at 只在临时窗口期使用。工具不在新关键是把每一步都想清楚、每一条输出都留下证据。希望这篇文章能帮你把 Linux 定时任务这关顺利过掉。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询