47·Shell 脚本进阶入门

at 与 batch 一次性任务

atscheduling

第47课:at 与 batch - 一次性任务调度

学习目标

  1. 掌握使用 at 命令安排一次性定时任务的基本方法。
  2. 理解 atbatch 命令的核心区别与应用场景。
  3. 学会查看、管理和删除 at 队列中的待执行任务。
  4. 能够在实际运维场景中合理选择 atbatchcron

核心概念

在上一课中,我们学习了 cron,它非常适合安排那些需要周期性重复执行的任务,比如每天备份、每周生成报告。然而,在运维工作中,我们常常还需要处理另一类任务:只执行一次,且就在某个特定的未来时间点执行

想象一下:

  • “请在今晚11点重启数据库服务。”
  • “请在系统负载降低后,运行一次数据清理脚本。”
  • “服务器将在10分钟后维护,请在那时发送通知邮件。”

对于这类“一次性”的调度需求,cron 就显得有些“大材小用”和不够灵活了。为此,Linux 提供了 atbatch 两个命令。

1. at - 定时执行一次性任务

at 命令就像一个“预约单”。你告诉它一个未来的时间点(如 now + 5 minutes, teatime tomorrow, 10:00 PM),并输入你要执行的命令或脚本。at 会将你的任务和指令保存到 /var/spool/at/ 目录下的一个文件中,并由 atd 守护进程在指定的时间将其执行。

基本语法

at [选项] [时间]
# 输入命令,按 Ctrl+D 结束输入

2. batch - 空闲时执行一次性任务

batch 命令是 at 的一个特殊变体。它不需要你指定一个具体的时间点。当你使用 batch 安排任务时,任务会被放入队列,但系统会等到负载较低(通常平均负载低于0.8)时才自动执行它。这对于那些不急于立刻执行,但希望在系统不忙的时候运行的资源密集型任务非常有用。

基本语法

batch
# 输入命令,按 Ctrl+D 结束输入

3. at vs batch vs cron 对比

特性at / batchcron
执行频率一次性周期性
触发条件at: 精确时间点<br>batch: 系统空闲时基于时间规则(分、时、日、月、周)
典型场景单次维护、延迟重启、负载敏感任务日常备份、定期巡检、日志轮转
任务队列有独立的 at 队列,用 atq 查看通过 crontab -l 查看个人任务
任务文件/var/spool/at//var/spool/cron/

代码示例

示例1:使用 at 安排一次性任务

# 安排一个在当前时间1分钟后执行的任务,创建一个标记文件
at now + 1 minutes <<EOF
touch /tmp/at_test_$(date +%s).txt
echo "Task executed by at at $(date)" >> /tmp/at_log.txt
EOF

# 查看当前用户的 at 任务队列
atq

# 查看某个具体任务(假设 job 编号是 3)的内容
at -c 3

# 删除一个待执行的任务(假设 job 编号是 3)
atrm 3

解释

  1. at now + 1 minutes:指定执行时间为“从现在开始的1分钟后”。
  2. <<EOF ... EOF:这是一种“Here Document”用法,用于将多行命令传递给 at
  3. atq:列出所有待执行的 at 任务。输出格式为:[任务ID] [执行时间] [队列] [用户]
  4. at -c [任务ID]:查看该任务的详细环境变量和执行命令。
  5. atrm [任务ID]:从队列中删除指定任务。

示例2:使用 batch 安排任务

# 安排一个任务,当系统空闲时执行,记录开始执行的时间
batch <<EOF
echo "Batch job started at $(date)" >> /tmp/batch_log.txt
# 模拟一个耗时的查找任务
find / -name "*.log" -mtime +30 2>/dev/null > /tmp/old_logs_list.txt
EOF

# 使用 atq 同样可以查看 batch 任务(它们也存放在 at 队列中,但标记为 `=`)
atq
# 输出可能类似于:1  Thu Oct 26 15:30:00 2023 = user
# 等号 `=` 表示这是一个 batch 任务,时间字段显示的是提交时间。

解释

  1. batch 命令直接使用,无需指定时间。
  2. 提交后,任务进入 at 队列,但状态为 =
  3. atd 守护进程会持续监控系统负载,一旦满足条件(负载低于阈值),就会执行该任务。

示例3:管理 at 服务和查看执行结果

# 确保 atd 服务正在运行(CentOS/RHEL)
sudo systemctl start atd
sudo systemctl enable atd

# 等待 at 任务执行完成后,检查结果
cat /tmp/at_log.txt
cat /tmp/batch_log.txt

实践练习

练习1:基础定时任务

  1. 使用 at 命令安排一个任务,在 2分钟后 创建一个名为 /tmp/exercise1.txt 的文件,并将当前时间写入文件。
  2. 使用 atq 确认任务已加入队列。
  3. 等待任务执行,然后检查 /tmp/exercise1.txt 文件是否存在且内容正确。

预期命令与输出

# 安排任务
echo "date > /tmp/exercise1.txt" | at now + 2 minutes
# 查看队列
atq
# 等待后检查
cat /tmp/exercise1.txt
# 输出应为类似:Wed Oct 25 14:30:00 CST 2023

练习2:复杂脚本一次性执行

  1. 编写一个简短的Shell脚本 backup_temp.sh,内容为:tar -czf /tmp/temp_backup_$(date +%Y%m%d%H%M).tar.gz /tmp/*.txt
  2. 使用 at 安排此脚本在 今天晚上11:00 (23:00 today11 pm) 执行。
  3. 写出完整的 at 命令(提示:可以使用输入重定向 < 从文件读取命令)。

预期命令

# 创建脚本
echo 'tar -czf /tmp/temp_backup_$(date +%Y%m%d%H%M).tar.gz /tmp/*.txt' > backup_temp.sh
chmod +x backup_temp.sh
# 安排执行
at 23:00 today < backup_temp.sh

练习3:使用 batch 优化资源

  1. 系统当前负载可能较高。编写一个命令,使用 batch 安排一个查找整个 /etc 目录下所有 .conf 文件的任务,并将结果列表保存到 /tmp/conf_files_list.txt
  2. 思考:为什么这个任务适合使用 batch 而不是 at

预期命令

batch <<EOF
find /etc -type f -name "*.conf" > /tmp/conf_files_list.txt
EOF

思考答案:因为 find /etc 是一个磁盘I/O密集型操作,如果在系统繁忙时执行,可能会加剧系统负担,影响关键服务。使用 batch 让它在系统空闲时自动执行,可以最小化对正常业务的影响。

常见错误

  1. 忘记指定时间

    • 错误:直接输入 at 后回车,导致命令无法执行。
    • 修正at 必须接一个时间参数,如 at nowat 15:00at 17:00 tomorrow
  2. 权限不足

    • 错误:普通用户试图删除其他用户的 at 任务,或 atd 服务未运行。
    • 修正:使用 sudo 提权管理他人的任务,或使用 sudo systemctl status atd 检查服务状态。
  3. 输出未重定向

    • 错误atbatch 任务执行时,其标准输出和错误输出默认会通过邮件发送给用户,但如果未配置邮件系统,输出可能“丢失”,导致无法确认任务是否成功。
    • 修正:始终在任务命令中加入重定向,如 command > /path/to/logfile 2>&1,以便记录输出。
  4. 误解 batch 的执行条件

    • 错误:认为 batch 安排的任务会“立即”执行,或者指定一个具体时间。
    • 修正batch 的执行时机由系统负载决定,是不确定的。它只保证在负载低时执行,但不保证具体时间。如果需要精确时间,请使用 at

小结

  • at 是安排在特定未来时间点执行一次性任务的命令。
  • batch 是安排在系统空闲时执行一次性任务的命令,适合非紧急的资源密集型任务。
  • 两者都使用 atq 查看队列,atrm 删除任务,at -c 查看任务详情。
  • atd 守护进程是执行这些任务的核心服务,必须确保其运行。
  • 选择策略
    • 精确时间点 -> at
    • 空闲时执行 -> batch
    • 周期重复 -> cron
  • 良好实践:为 at/batch 任务安排日志输出重定向,以便事后审计和调试。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「trap 信号处理」 以巩固所学知识。