# 14.4.Cron 和 Periodic

## 14.4.1. Cron

[cron(8)](https://man.freebsd.org/cgi/man.cgi?query=cron\&sektion=8\&format=html) 工具在后台运行，并定期检查 **/etc/crontab** 中的任务，并在 **/var/cron/tabs** 中查找自定义的 crontab 文件。

这些文件用于调度任务，cron 将在指定的时间执行这些任务。

每个 crontab 条目定义一个要执行的任务，称为 *cron 作业*。

使用两种不同类型的配置文件：系统 crontab，不应修改，以及用户 crontab，可以根据需要创建和编辑。这些文件的格式在 [crontab(5)](https://man.freebsd.org/cgi/man.cgi?query=crontab\&sektion=5\&format=html) 中有详细记录。系统 crontab 的格式 **/etc/crontab** 包含一个 `who` 列，这在用户 crontab 中不存在。在系统 crontab 中，cron 以该列指定的用户身份运行命令。而在用户 crontab 中，所有命令都以创建该 crontab 的用户身份运行。

用户 crontab 允许每个用户调度自己的任务。`root` 用户也可以有一个用户 **crontab**，用于调度系统 **crontab** 中不存在的任务。

这是来自系统 crontab **/etc/crontab** 的一个示例条目：

```sh
# /etc/crontab - root 的 crontab 配置文件
#
①
#
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin ②
#
#分钟 小时    日期    月份   星期    用户    命令 ③
#
# 每 11 分钟保存一些熵，以便 /dev/random 在启动时重新播种。
*/11    *       *       *       *       operator /usr/libexec/save-entropy ④
#
# 每小时轮换日志文件（如果需要）。
0       *       *       *       *       root    newsyslog
#
# 执行日常/每周/每月维护。
1       3       *       *       *       root    periodic daily
15      4       *       *       6       root    periodic weekly
30      5       1       *       *       root    periodic monthly
#
# 如果 CMOS 时钟保持本地时间，而不是 UTC 时间，则调整时区。
# 详情请参见 adjkerntz(8)。
1,31    0-5     *       *       *       root    adjkerntz -a
```

* ① 以 `#` 字符开头的行是注释。注释可以放在文件中，作为执行所需操作的提醒。注释不能与命令位于同一行，否则它们将被解释为命令的一部分；它们必须单独放在新的一行。空行会被忽略。
* ② 等号（`=`）字符用于定义环境设置。在此示例中，它用于定义 `SHELL` 和 `PATH`。如果省略 `SHELL`，cron 将使用默认的 Bourne shell。如果省略 `PATH`，则必须提供命令或脚本的完整路径。
* ③这一行定义了系统 crontab 中使用的七个字段：`minute`、`hour`、`mday`、`month`、`wday`、`who` 和 `command`。`minute` 字段是指定命令运行的分钟数，`hour` 是命令运行的小时数，`mday` 是日期，`month` 是月份，`wday` 是星期几。这些字段必须是数字值，表示24小时制，或者是 `*`，表示该字段的所有值。`who` 字段仅存在于系统 crontab 中，指定命令应该以哪个用户身份运行。最后一个字段是要执行的命令。 |
* ④这个条目定义了这个 cron 作业的值。`*/11` 后面跟着多个 `*` 字符，表示 `/usr/libexec/save-entropy` 将由 `operator` 每小时的每11分钟执行一次，每天、每周、每月都如此。命令可以包含多个选项。如果命令跨越多行，必须使用反斜杠 `\\` 继续字符。

## 14.4.2. 创建用户 Crontab

要创建用户 crontab，可以在编辑模式下调用 `crontab`：

```sh
% crontab -e
```

这将使用默认文本编辑器打开用户的 crontab。用户第一次运行此命令时，会打开一个空文件。待用户创建了 crontab，此命令将打开该文件进行编辑。

将以下行添加到 crontab 文件的顶部以设置环境变量，并记住 crontab 字段的含义是非常有用的：

```ini
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
# crontab 字段顺序
# 分钟 小时 日期 月份 星期 命令
```

然后为每个要运行的命令或脚本添加一行，指定运行命令的时间。此示例每天在下午两点运行指定的自定义 Bourne shell 脚本。由于 `PATH` 中未指定脚本的路径，因此给出脚本的完整路径：

```ini
0 14 * * * /home/user/bin/mycustomscript.sh
```

> **技巧**
>
> 在使用自定义脚本之前，请确保它是可执行的，并在 cron 设置的有限环境变量下进行测试。为了复制将在上述 cron 条目中使用的环境，可以使用：
>
> ```ini
> env -i SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin HOME=/home/user LOGNAME=user /home/user/bin/mycustomscript.sh
> ```
>
> cron 设置的环境可以在 [crontab(5)](https://man.freebsd.org/cgi/man.cgi?query=crontab\&sektion=5\&format=html) 中找到。检查脚本在 cron 环境下是否正常运行尤其重要，特别是当脚本包含任何使用通配符删除文件的命令时。

编辑完 crontab 后，保存文件。它将自动安装，cron 会读取 crontab 并在指定时间运行 cron 作业。要列出 crontab 中的 cron 作业，可以使用以下命令：

```sh
% crontab -l
```

输出应类似于以下内容：

```sh
0 14 * * * /home/user/bin/mycustomscript.sh
```

要删除用户 crontab 中的所有 cron 作业：

```sh
% crontab -r
```

输出应类似于以下内容：

```sh
remove crontab for user? y
```

## 14.4.3. Periodic

FreeBSD 提供了一组系统管理脚本，用于检查各种子系统的状态，执行与安全相关的检查，轮换日志文件等。这些脚本按周期执行：每日、每周或每月。这些任务的管理由 [periodic(8)](https://man.freebsd.org/cgi/man.cgi?query=periodic\&sektion=8\&format=html) 执行，其配置位于 [periodic.conf(5)](https://man.freebsd.org/cgi/man.cgi?query=periodic.conf\&sektion=5\&format=html) 中。周期性任务由系统 crontab 中的条目启动，如上所示。

[periodic(8)](https://man.freebsd.org/cgi/man.cgi?query=periodic\&sektion=8\&format=html) 执行的脚本位于 **/etc/periodic/**（基础工具）和 **/usr/local/etc/periodic/**（第三方软件）中。

它们被组织在 4 个子目录中：daily、weekly、monthly 和 security。

## 14.4.4. 启用或禁用周期性任务

FreeBSD 默认启用了某些脚本以定期运行。

要启用或禁用任务，第一步是编辑 **/etc/periodic.conf** 文件，执行以下命令：

```sh
# ee /etc/periodic.conf
```

然后，启用例如 `daily_status_zfs_enable`，将以下内容添加到文件中：

```sh
daily_status_zfs_enable="YES"
```

要禁用默认启用的任务，只需将 `YES` 更改为 `NO`。

## 14.4.5. 配置周期性任务的输出

在 **/etc/periodic.conf** 中，变量 `daily_output`、`weekly_output` 和 `monthly_output` 指定了脚本执行结果的发送位置。

默认情况下，周期性脚本的输出会发送到 root 用户的邮件，因此最好阅读 root 的邮件，或者将 root 的邮件别名为一个被监控的邮箱。

要将结果发送到另一个邮件地址或其他邮件地址，可以在 **/etc/periodic.conf** 中添加以空格分隔的邮件地址：

```ini
daily_output="email1@example.com email2@example.com"
weekly_output="email1@example.com email2@example.com"
monthly_output="email1@example.com email2@example.com"
```

如果希望将周期性输出记录到日志文件，而不是通过邮件接收，可以将以下行添加到 **/etc/periodic.conf** 中。 [newsyslog(8)](https://man.freebsd.org/cgi/man.cgi?query=newsyslog\&sektion=8\&format=html) 会在适当的时间轮换这些文件：

```ini
daily_output=/var/log/daily.log
weekly_output=/var/log/weekly.log
monthly_output=/var/log/monthly.log
```
