在Ubuntu系统运维中,如果你需要在某个特定时间点执行一次命令或脚本,比如凌晨三点重启某个服务、两小时后执行一次数据库备份,最简单直接的方式就是用at命令。at是Linux系统自带的一次性延迟任务调度工具,不需要安装额外软件,不像crontab那样是周期性任务,at专门解决"就执行一次、延后再跑"的需求。在Ubuntu 18.04、20.04、22.04、24.04等主流版本中,at命令都默认可用,只需确保atd守护进程在运行即可。
一、at命令的基本原理和适用场景
at命令的工作原理非常简单:你输入一条命令并指定执行时间,at守护进程(atd)会在后台等待,到了指定时间就以当前用户的身份执行该命令。它和crontab的核心区别在于——at是一次性的,crontab是循环的。典型使用场景包括:临时安排一次系统维护、延迟执行某个耗时操作、在非工作时间自动触发某项任务、测试某个定时脚本是否正常工作等。运维人员在排查问题时经常需要"等一会儿再看日志",这时候at就比手动盯着终端高效得多。
二、Ubuntu上安装和启动at服务
大多数Ubuntu桌面版和服务器版已经预装了at,但最小安装的系统可能没有。先检查是否已安装:
which at
如果没有输出,执行安装:
sudo apt update sudo apt install at -y
安装完成后,必须启动atd守护进程,否则at命令会报错"Cannot open job queue":
sudo systemctl enable atd sudo systemctl start atd sudo systemctl status atd
看到"active (running)"就说明服务正常了。这一步很多新手会忽略,导致后面所有操作都失败。另外要注意,atd默认是以root身份运行的,普通用户要使用at需要在/etc/at.allow或/etc/at.deny中有权限配置。如果/etc/at.allow存在,只有里面列出的用户才能用at;如果不存在但/etc/at.deny存在,则deny里的用户被禁止使用。最简单的做法是确保当前用户在at.allow里:
sudo usermod -aG at $(whoami) # 或者直接编辑 echo $(whoami) | sudo tee -a /etc/at.allow
三、at命令的时间指定方式
at命令支持多种时间格式,这是它灵活的核心。以下是常用写法:
1. 相对时间(最常用):
at now + 10 minutes # 10分钟后执行 at now + 2 hours # 2小时后执行 at now + 1 day # 明天同一时间执行 at now + 30 seconds # 30秒后执行
2. 绝对时间:
at 14:30 # 今天下午2点30分 at 03:00 2025-01-15 # 2025年1月15日凌晨3点 at 10:00 AM tomorrow # 明天上午10点
3. 关键词时间:
at midnight # 今晚午夜 at noon # 明天中午 at teatime # 下午4点(英式说法)
实际运维中,"now + N minutes/hours"是最实用的,因为你往往是在操作过程中临时决定"过一会儿再执行某个命令"。
四、at命令的三种调用方式
方式一:交互式输入。直接输入at加上时间,回车后进入at提示符,逐行输入命令,最后按Ctrl+D结束:
at now + 5 minutes warning: commands will be executed using /bin/sh at> /usr/local/bin/backup.sh at> echo "Backup completed" >> /var/log/backup.log at> <EOT> job 5 at Thu Jan 9 15:30:00 2025
方式二:管道输入(适合脚本中使用):
echo "/usr/local/bin/backup.sh" | at now + 1 hour
方式三:从文件读取命令:
at now + 30 minutes -f /tmp/tasklist.sh
其中tasklist.sh是一个包含多条命令的脚本文件。运维中推荐使用方式二和方式三,因为可以写进自动化脚本里,不需要人工交互。
五、at任务的管理和查看
提交任务后,你需要知道任务的状态。常用管理命令如下:
atq # 查看当前用户所有待执行任务 atrm 5 # 删除编号为5的任务 at -c 5 # 查看编号5任务的具体内容 at -V # 查看at版本信息
atq输出格式类似这样:
5 Thu Jan 9 15:30:00 2025 a username 6 Thu Jan 9 16:00:00 2025 a username
第一列是任务编号,后面是执行时间和用户名。如果你想取消所有待执行任务,可以用:
atrm $(atq | awk '{print $1}')
但要谨慎使用,避免误删重要任务。
六、at执行任务的环境和注意事项
这是很多运维人员踩坑的地方。at执行任务时的环境变量和你当前终端的环境不完全一样。它默认使用/bin/sh作为shell,PATH环境变量比较精简。所以如果你的脚本依赖特定的环境变量或者用了bash特有语法,必须在脚本开头显式指定:
#!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin # 你的命令写在下面
或者在at命令里直接指定shell:
at now + 10 minutes at> /bin/bash -c '/usr/local/bin/my_script.sh' at> <EOT>
另外,at任务执行时不会关联任何终端(TTY),所以如果你的脚本里有需要交互输入的命令(比如ssh登录、sudo提权等),会直接失败。解决办法是提前配置好免密登录或在脚本里用expect处理交互,或者把需要交互的部分提前做好。
七、at在实际运维中的典型用法
场景1:延迟重启服务。某个服务刚修改了配置,需要等当前连接处理完再重启:
echo "sudo systemctl restart nginx" | at now + 5 minutes
场景2:定时备份。每天凌晨2点备份数据库,但只想临时跑一次测试:
at 02:00 tomorrow at> mysqldump -u root -p'password' mydb > /backup/mydb_$(date +%Y%m%d).sql at> <EOT>
场景3:批量执行维护命令。把多条命令写进文件一次性提交:
cat > /tmp/maint.txt << 'EOF' apt update && apt upgrade -y systemctl restart docker docker system prune -f echo "Maintenance done at $(date)" >> /var/log/maint.log EOF at now + 15 minutes -f /tmp/maint.txt
场景4:监控脚本延迟执行。先检查系统负载,过一会儿再跑:
echo "uptime >> /tmp/load_check.txt; /usr/local/bin/health_check.sh" | at now + 3 minutes
八、at与其他定时工具的对比
在Ubuntu运维中,定时任务工具有好几个,选对工具很重要。at适合一次性延迟任务;crontab适合周期性重复任务;systemd timer适合需要精确控制依赖关系和日志记录的服务级定时任务。如果你的需求是"每5分钟检查一次",用crontab或systemd timer;如果是"半小时后执行一次",用at最合适。不要把at当成crontab的替代品,它们定位完全不同。对于需要精确到秒级的任务,at的最小粒度是分钟级(虽然支持now + 30 seconds这种写法,但实际调度精度取决于系统),如果需要更高精度,考虑用systemd timer配合OnCalendar或OnBootSec。
九、常见问题排查
问题1:报错"Cannot open job queue"。原因是atd服务没启动,执行sudo systemctl start atd即可。
问题2:任务提交了但没执行。检查atd是否在运行,检查/var/log/syslog里有没有相关报错,检查任务是否被at.deny拦截了。
问题3:任务执行了但命令没生效。大概率是环境变量问题,用完整路径写命令,或者在脚本开头source /etc/profile。
问题4:atq显示任务但执行时间过了还没跑。可能是系统时间被修改过,或者atd进程异常重启了,重启atd服务试试。
十、安全建议
at命令以提交者的用户身份执行,如果被恶意用户利用,可以执行任意命令。生产环境中务必严格控制at.allow和at.deny文件的权限,只允许必要的运维账号使用。同时建议开启审计,记录谁在什么时候提交了什么任务,方便事后追溯。可以通过配置auditd来监控/var/spool/at/目录下的文件变化。
总结来说,at是Ubuntu运维中一个轻量但实用的工具,特别适合"临时安排一次延迟执行"的场景。掌握它的安装、启动、时间指定、任务管理和环境处理这几个关键点,就能在日常运维中灵活应对各种一次性定时需求。不要小看这个老工具,在很多紧急运维场景下,它比写一个完整的systemd service要快得多、简单得多。
