Bash 零基础教程

Linux 定时任务:cron 与 crontab 自动化调度

cron 是类 Unix 操作系统中一个基于时间的任务调度器。它允许你通过设置特定的时间、日期或时间间隔来自动执行任务。这对于系统管理、数据备份、报告生成以及许多其他自动化流程来说极其有用。有了 cron,你无需再手动重复运行脚本或命令,它会替你接管这些任务,确保它们在没有人工干预的情况下,始终如一且可靠地执行。

1. 认识 cron 与 crontab

cron 本身是一个在后台运行的守护进程(daemon),它会定期(通常是每分钟)醒来检查是否有任何计划中的任务需要执行。

这些任务的执行计划被存储在一个叫做 crontab(cron table,即 cron 计划表)的文件中。系统上的每个用户都可以拥有属于自己的 crontab 文件,用来调度仅针对其自身用户帐户的任务。而系统级别的任务通常配置在系统全局的 crontab 中,修改这类文件通常需要 root(管理员)权限。

2. crontab 语法规则

crontab 文件中的每一行代表一个单独的 cron 任务,并且遵循一套特定的语法。这套语法由六个字段组成:

分钟 小时 日 月 星期 命令

让我们逐一拆解这些字段:

  • 分钟 (minute): 任务在哪一分钟执行,范围是 0-59。
  • 小时 (hour): 任务在哪一小时执行,范围是 0-23。
  • 日 (day_of_month): 任务在一个月中的哪一天执行,范围是 1-31。
  • 月 (month): 任务在一年中的哪个月执行,范围是 1-12(或用 Jan-Dec 表示)。
  • 星期 (day_of_week): 任务在一周中的哪一天执行,范围是 0-6(或用 Sun-Sat 表示,其中 0 代表星期日)。
  • 命令 (command): 需要执行的命令。它可以是一个简单的系统命令,也可以是某个脚本的绝对路径。

你可以使用各种特殊符号来指定不同的时间调度模式:

  • * (星号): 代表该字段所有可能的值。例如,分钟字段如果是 *,则表示“每分钟”。
  • , (逗号): 用于指定一个值列表。例如,分钟字段写 1,15,30 表示在第 1、15 和 30 分钟时运行。
  • - (连字符): 用于指定一个值范围。例如,小时字段写 8-17 表示从早上 8 点到下午 5 点,每小时运行一次。
  • / (斜杠): 用于指定一个步长值。例如,分钟字段写 */15 表示每 15 分钟运行一次。

2.1 常用 crontab 配置示例

以下是一些常见的 crontab 条目及其含义:

  • 0 0 * * * /path/to/backup_script.sh:每天午夜(00:00)运行 backup_script.sh 脚本。
  • */5 * * * * /path/to/check_disk_space.sh:每 5 分钟运行一次 check_disk_space.sh 脚本。
  • 0 8 * * 1-5 /path/to/generate_report.sh:每个工作日(星期一到星期五)的早上 8 点运行 generate_report.sh 脚本。
  • 0 12 1 * * /path/to/monthly_cleanup.sh:每个月的第一天中午 12 点运行 monthly_cleanup.sh 脚本。
  • 30 6 15 4 * /path/to/send_reminder.sh:在 4 月 15 日早上 6:30 运行 send_reminder.sh 脚本。

3. crontab 常用操作命令

你可以使用 crontab 命令来管理你的定时任务表。

3.1 查看已调度的任务

要查看当前的计划表,使用以下命令:

crontab -l

这将显示你的 crontab 文件的内容,列出所有已调度的任务。如果你目前还没有安排任何任务,它通常会输出“no crontab for [你的用户名]”。

3.2 编辑任务计划表

要编辑你的计划表,使用以下命令:

crontab -e

这将会在文本编辑器(通常是 vinano,取决于你的系统配置)中打开你的 crontab 文件。你可以添加、修改或删除其中的条目。一旦你保存并关闭文件,cron 会自动应用新的调度计划。

示例: 假设我们想添加一个任务,让存放在 /home/user/scripts/ 目录下的 monitor_system.sh 脚本每小时的半点(30分)运行一次。我们会在计划表文件中添加以下这一行:

30 * * * * /home/user/scripts/monitor_system.sh

3.3 删除所有任务

要清空所有 cron 任务,使用以下命令:

crontab -r

这将会彻底删除你的 crontab 文件。使用此命令时请务必小心,因为它会清除你所有的计划任务。根据系统的不同,运行此命令时可能会弹出确认提示。

4. 用户级别与系统级别的定时任务

正如前面所提到的,每个用户都有自己的 crontab 文件,并通过上面介绍的 crontab 命令进行管理。

另一方面,系统级别的 cron 任务通常配置在特定的目录中,比如 /etc/cron.d//etc/cron.hourly/(每小时)、/etc/cron.daily/(每天)、/etc/cron.weekly/(每周)和 /etc/cron.monthly/(每月)。

修改系统级任务通常需要 root 权限(例如使用 sudo)。例如,你可以在 /etc/cron.daily/ 目录下创建一个文件,用来运行日常维护脚本。存放在这些目录中的文件会被 cron 按照指定的频率自动执行。

示例: 要创建一个系统级的每日定时任务,你可以创建一个名为 /etc/cron.daily/my_daily_backup 的脚本,内容如下:

#!/bin/bash
# 这个脚本用于创建重要数据的每日备份
tar -czvf /var/backups/my_data_$(date +%Y-%m-%d).tar.gz /path/to/my/data

切记要赋予该脚本可执行权限:

sudo chmod +x /etc/cron.daily/my_daily_backup

现在,这个脚本就会每天自动运行了。

5. 编写 cron 任务的最佳实践

在使用 cron 时,遵循一些最佳实践非常重要,这能确保你的任务可靠运行并避免常见陷阱。

5.1 使用绝对路径

crontab 条目中,始终使用命令和脚本的绝对路径。这确保了无论当前工作目录是什么,cron 都能准确找到可执行文件。cron 运行时的环境变量非常精简,过度依赖 $PATH 很容易导致“找不到命令”的错误。

示例: 不要只写 backup_script.sh,而应该写完整的绝对路径 /home/user/scripts/backup_script.sh

5.2 环境变量配置

由于 cron 任务在一个极简的环境中运行,你在交互式终端中能使用的环境变量,并不一定对 cron 任务可用。如果你的脚本依赖特定的环境变量,你需要在 crontab 文件顶部或脚本内部明确定义它们。

示例:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 0 * * * /path/to/script.sh

在这里,我们显式定义了 SHELLPATH 变量,以确保脚本能够正确执行。

5.3 输出重定向

默认情况下,cron 会将任务产生的任何输出(包括标准输出和标准错误)发送到用户的本地电子邮件帐户。如果你的任务产生大量日志,这会迅速塞满你的收件箱。为了避免这种情况,建议将输出重定向到一个日志文件,如果你根本不需要这些输出,可以将其丢弃到 /dev/null

示例:

  • 仅将标准输出重定向到文件0 0 * * * /path/to/script.sh > /path/to/output.log
  • 仅将标准错误重定向到文件0 0 * * * /path/to/script.sh 2> /path/to/error.log
  • 将标准输出和标准错误合并重定向到文件0 0 * * * /path/to/script.sh > /path/to/output.log 2>&1
  • 丢弃所有输出0 0 * * * /path/to/script.sh > /dev/null 2>&1

5.4 错误处理、日志与安全

  • 错误处理: 在脚本中实现妥善的错误处理逻辑。这包括检查报错、记录错误日志以及采取适当的应对措施(如发送报警邮件)。
  • 日志记录: 在脚本中记录重要事件和错误。这将大大有助于排查问题并监控自动化任务的健康状态。
  • 安全防范: 注意以 cron 任务身份运行脚本带来的安全影响。避免在 crontab 文件或脚本中明文存储密码等敏感信息。设置合理的文件权限,防止他人未经授权修改你的脚本。

5.5 测试

在将 cron 任务部署到生产环境之前,务必进行彻底的测试。你可以先手动执行脚本进行测试,或者在 cron 中设置一个非常短的时间间隔(例如每分钟)来观察它的自动运行情况。

6. cron 进阶技巧

除了基础的时间调度,cron 还提供了一些能在复杂场景下派上用场的高级特性。

6.1 使用 @reboot

@reboot 指令允许你在每次系统启动时运行一个任务。这对于重启后必须执行的操作非常有用,比如启动特定的自定义服务或初始化某些配置。

示例:

@reboot /path/to/startup_script.sh

6.2 结合命名管道 (FIFOs)

对于 cron 任务更复杂的交互需求,你可以使用命名管道(FIFOs)。FIFO 允许你在正在运行的 cron 任务和其他进程之间传递数据。

示例:

  1. 创建一个 FIFO:mkfifo /tmp/my_fifo
  2. 在你的 cron 任务中,将数据写入 FIFO:/path/to/script.sh > /tmp/my_fifo
  3. 在另一个进程中,从 FIFO 读取数据:cat /tmp/my_fifo

这允许你实时接收来自后台 cron 任务的输出。

6.3 认识 Anacron

cron 非常适合一直保持开机状态的服务器。而 anacron 则是为那些不常开机的系统(比如笔记本电脑或个人台式机)设计的。anacron 可以确保哪怕系统在预定的任务执行时间处于关机状态,任务也能在开机后的特定周期内(如每天、每周、每月)被“补救”执行。它不是 cron 的直接替代品,而是一个互补工具。虽然深入讲解 anacron 超出了本章的范围,但了解它的存在是很有必要的。

7. 实战案例:自动化系统巡检与报警

让我们回顾一下我们自动化系统管理的实战案例。假设我们想要自动化检查磁盘空间使用情况,并在使用率超过特定阈值时发送报警邮件。

首先,创建一个检查脚本 (check_disk_space.sh):

#!/bin/bash
# 设置磁盘空间使用率的报警阈值(百分比)
THRESHOLD=80

# 获取当前根目录的磁盘空间使用率(去除百分号)
USAGE=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')

# 检查使用率是否超过了阈值
if [ "$USAGE" -gt "$THRESHOLD" ]; then
  # 获取当前主机名
  HOSTNAME=$(hostname)
  
  # 设置邮件主题
  SUBJECT="磁盘空间警报:$HOSTNAME"
  
  # 设置邮件正文
  BODY="$HOSTNAME 上的磁盘空间使用率目前已达到 $USAGE%。请立即排查。"
  
  # 发送邮件
  echo "$BODY" | mail -s "$SUBJECT" admin@example.com
fi

为脚本添加可执行权限:

chmod +x check_disk_space.sh

使用 cron 调度该脚本每小时运行一次:

crontab -e

在打开的 crontab 文件中添加以下行:

0 * * * * /path/to/check_disk_space.sh

现在,这个脚本将会每小时自动运行一次,检查磁盘剩余空间。如果使用率超过 80%,它会自动给 admin@example.com 发送一封警告邮件。请记得将示例中的 /path/to/check_disk_space.sh 替换为你脚本的实际绝对路径。

这个实战案例结合了我们之前学过的知识点:在被 cron 调度的自动化脚本中,综合运用了变量、条件判断和输出提取。