cron - Arch Linux 中文维基

跳至內容

# cron

出自 Arch Linux 中文维基

翻譯狀態：

- 本文（或部分內容）譯自 Cron，最近一次同步於 2025-02-23，若英文版本有所更改，則您可以幫助同步與翻譯更改的內容。
- 您可以在 ArchWiki 的對應頁面找到本文翻譯的原始修訂歷史。
- 本文可能與英文原文存在出入。

相關文章

- systemd/Timers

摘自 Wikipedia:

  cron 是一個在 Unix 及類似作業系統上執行計劃任務的程序。cron 使用戶能夠安排工作(命令或shell腳本)在特定時間、日期或間隔定期運行，通常用於系統的自動化維護或者管理。

## 安裝

cron 有多個實現程序，但是基礎系統默認使用 systemd 的 Timers，下面的實現都不被默認安裝。參見 Gentoo wiki 的 cron 指南，該指南提供了這些實現之間的比較。

可用的包:

- cronie包
- fcron包
- dcronAUR
- vixie-cronAUR
- fcron-devAUR
- scron-gitAUR

## 配置

### 激活及開機啟動

安裝後，默認的守護進程不會啟動。安裝的軟體包通常提供了可以用 systemctl 控制的服務文件。例如 cronie 使用 `cronie.service`。

`/etc/cron.daily/` 和類似的目錄包含當前的任務，啟動 cron 服務時會觸發所有這類任務。

注意：cronie 提供了 `0anacron` 任務，每小時執行一次，它允許延遲運行其他某些任務，比如因為未開機而延遲的任務。

### 處理任務中的錯誤

cron 會記錄 stdout 和 stderr 的輸出並嘗試通過 `sendmail` 命令發送郵件給用戶。如果 Cronie 未找到 `/usr/bin/sendmail`，則會禁用郵件通知。要發送郵件到用戶的 spool，需要在系統上運行一個 smtp 守護進程，例如 opensmtpd包。也可以安裝提供 sendmail 命令的軟體包，然後配置成通過外部郵件伺服器發送郵件。或者使用 `-m` 選項將錯誤記錄到日誌並通過定製的腳本進行處理。

提示：通過 Postfix/localmail 可以發送郵件到本地系統。

1. 編輯 `cronie.service` 服務。
2. 安裝 esmtpAUR, mSMTP, opensmtpd包, sSMTP 或編寫自定義腳本。

#### 使用 sSMTP 的例子

sSMTP 是一個僅包含發送功能的 sendmail 模擬器，可以從本地計算機向 smtp 伺服器發送郵件。儘管目前已經沒有活躍維護者，這個程序依然是發送郵件的最簡單方式。不需要運行守護進程，配置可以簡單到只需在一個配置文件中編輯三行即可（如果你的主機是受信任的，可以通過你的郵件服務提供商轉發未經認證的郵件）。sSMTP 無法收取郵件、展開別名或管理隊列。

安裝 ssmtpAUR，安裝時會創建連結 `/usr/bin/sendmail` 指向 `/usr/bin/ssmtp`。安裝後編輯 `/etc/ssmtp/ssmtp.conf` 配置文件。詳情請參考 sSMTP，到 `/usr/bin/sendmail` 的軟連結可以確保 S-nail 等提供 `/usr/bin/mail` 的程序可以無需修改直接使用。

重啟 `cronie.service` 以確保 cronie 能夠檢測到新配置的 `/usr/bin/sendmail` 命令。

#### 使用 mSMTP 的例子

安裝 msmtp-mta包, 安裝時會創建連結 `/usr/bin/sendmail` 指向 `/usr/bin/msmtp`。重啟 `cronie.service` 以確保 cronie 能夠檢測到新配置的 `/usr/bin/sendmail` 命令。你必須提供一種方法讓 `msmtp` 能夠將你的用戶名轉換成電子郵件地址。

然後要麼在你的 crontab 中添加 `MAILTO` 行：

    MAILTO=your@email.com

要麼創建 `/etc/msmtprc` 文件，並添加這一行：

    aliases /etc/aliases

並創建 `/etc/aliases` 文件：

    your_username: your@email.com
    # 可选：
    default: your@email.com

然後修改 cronie 的配置，將cronie 守護進程的 `ExecStart` 命令替換為

    ExecStart=/usr/bin/crond -n -m '/usr/bin/msmtp -t'

#### 使用 esmtp 的例子

安裝 esmtpAUR 和 procmailAUR。

安裝完成後，進行如下配置:

    /etc/esmtprc

    identity myself@myisp.com
           hostname mail.myisp.com:25
           username "myself"
           password "secret"
           starttls enabled
           default
    mda "/usr/bin/procmail -d %T"

Procmail 需要 root 權限才能在投遞模式下工作，但如果你是以 root 身份運行 cronjobs，就不會有這個問題。

要測試一切是否正常工作，請創建一個內容為 `"test message"` 的 `message.txt` 文件。

在同一目錄下運行：

    $ sendmail user_name < message.txt

緊接著：

    $ cat /var/spool/mail/user_name

現在您應該看到測試信息以及發送的時間和日期。

所有作業的錯誤輸出現在會被重定向到 `/var/spool/mail/user_name`。

由於權限問題，很難創建和發送郵件給root用戶（比如 `su -c ""`）。你可以要求 `esmtp` 將 root 的所有郵件轉發給普通用戶，方法是：

    /etc/esmtprc

    force_mda="user-name"

注意：如果上述測試沒有成功，您可以嘗試在 `~/.esmtprc` 中創建一個具有相同內容的本地配置。

運行下面的命令以確保它有正確的權限：

    $ chmod 710 ~/.esmtprc

然後用 `message.txt` 文件重新運行一遍上面的測試。

#### 使用 opensmtpd 的例子

安裝 opensmtpd包。

編輯 `/etc/smtpd/smtpd.conf`。下面的配置允許本地投遞：

    listen on localhost
    action "local" mbox alias <aliases>
    match for local action "local"

您現在可以進行測試。運行 `smtpd.service`，然後執行：

    $ echo test | sendmail user

user 可以用任何 能夠處理 mbox 格式的郵件閱讀器來檢查郵件，或者直接查看 `/var/spool/mail/user` 文件。如果一切都符合預期，您可以啟用 openmtpd 使其開機運行。

這種方法的好處是不需要向遠程伺服器發送本地 cron 通知。但缺點是需要運行一個新的守護進程。

注意：

- 在寫這篇文章的時候，Arch 的 opensmtpd 包還沒有在 `/var/spool/smtpd` 下創建好所有需要的目錄，但是守護進程會在需要指定所有者和權限時發出警告。只需按照警告創建即可。
- 儘管上文的配置中程序並不會接受遠程連接，但使用iptables或類似的方法來阻止埠 25 也是預防措施。

#### 運行時間很長的 cron 任務

假設 cron 調用了這個腳本：

    #!/bin/sh
    echo "我有一个可恢复错误！"
    sleep 1h

這時會發生以下事件：

1. cron 運行這個腳本
2. 一旦 cron 檢查到腳本有輸出，就會啟動你的郵件傳輸程序，並向通過管道其傳遞一些必要的頭部內容。因為腳本還沒有結束，還會有更多的輸出，管道就沒有關閉。
3. 郵件傳輸程序打開到郵件伺服器的連結，並等待後續的輸出。
4. 郵件伺服器會在特定時間後關掉這個空閒的連結，你會受到類似這樣的錯誤 :

       smtpmsg='421 … Error: timeout exceeded' errormsg='the server did not accept the mail'（伺服器沒有接受郵件

要解決這個問題，你可以使用 moreutils包 中的 chronic 或 sponge 命令。 它們各自的手冊頁面中寫道：

chronic(1)（時序）
  chronic 運行一個命令時，會緩存它的標準輸出和標準錯誤輸出，並只在命令失敗（返回值非零或崩潰）的情況下才會一併輸出。如果命令成功執行，任何無關的輸出將被隱藏。
sponge(1)（海綿）
  sponge 讀取標準輸入並將其寫入指定的文件。不同於 shell 重定向，sponge 在打開輸出文件之前會吸收所有的輸入……如果沒有指定輸出文件，sponge 會輸出到標準輸出。

Chronic也會在打開標準輸出之前緩衝命令輸出。

## Crontab 格式

crontab 的基本格式是：

分 時 日 月 星期 命令

- 分值從 0 到 59。
- 時值從 0 到 23。
- 日值從 1 到 31。
- 月值從 1 到 12。
- 星期值從 0 到 6, 0 代表星期日。

空格用來分開欄位，要微調你的時間表，也可以用下面特殊字符來設定範圍:

符號 | 描述
--- | ---
* | 通配符，表示所有支持的時間值
, | 用逗號分隔多個時間
- | 連接兩個數值，給出一個範圍
/ | 指定一個周期或頻率

例如，下面一行：

    */5 9-16 * 1-5,9-12 1-5 ~/bin/i_love_cron.sh

將會在6月、7月和8月外的周一至周五，從早上 9 點到下午 4 點 55 分，每隔 5 分鐘執行一次腳本 `i_love_cron.sh`。

此外，crontab 還有一些特殊的關鍵字。

Keyword | Description
--- | ---
@reboot | 啟動時
@yearly | 每年一次
@annually | 同 @yearly
@monthly | 每月一次
@weekly | 每周一次
@daily | 每天一次
@midnight | 同 @daily
@hourly | 每小時一次

例如：

    @reboot ~/bin/i_love_cron.sh

將在啟動時執行腳本 `i_love_cron.sh`。

更多信息參見: https://www.adminschoice.com/crontab-quick-reference

更多的例子和高級配置技巧可以在下面找到。

## 基本命令

Crontabs 絕不應該被直接編輯；你應該使用 `crontab` 程序來處理你的 crontabs。

要查看你的 crontabs：

    $ crontab -l

要編輯你的 crontabs：

    $ crontab -e

要移除你的所有 crontabs：

    $ crontab -r

如果你有一個保存好的 crontab ，而且你想要用它完全覆蓋舊的 crontab：

    $ crontab saved_crontab_filename

要從命令行(Wikipedia:stdin)覆蓋一個 crontab：

    $ crontab -

要編輯其他用戶的 crontab：

    # crontab -u username -e

同一個格式（在命令後追加 `-u username`）也可以用來列出或刪除 crontabs。

## 範例

下面的條目:

    01 * * * * /bin/echo Hello, world!

將會在每個月的每一天的每一個小時的第一分鐘（例如，在12:01，1:01，2:01等）執行命令 `/bin/echo Hello, world!`

類似地,

    */5 * * jan mon-fri /bin/echo Hello, world!

將會在一月的每個工作日每五分鐘（例如，在12：00，12：05，12：10等）執行一次相同的命令。

和前文 Crontab 格式一章相同，這一行（如 crontab(5) 所述）：

    *0,*5 9-16 * 1-5,9-12 1-5 /home/user/bin/i_love_cron.sh

將會在周內從早上 9 點到下午 4 點 55 分，每隔 5 分鐘執行一次腳本 `i_love_cron.sh`，夏季除外（6月、7月和8月）。

也可以像這樣輸入周期性設置：

    # Chronological table of program loadings
    # Edit with "crontab" for proper functionality, "man 5 crontab" for formatting
    # User: johndoe

    # mm  hh  DD  MM  W /path/progam [--option]...  ( W = weekday: 0-6 [Sun=0] )
      21  01  *   *   * /usr/bin/systemctl hibernate
      @weekly           $HOME/.local/bin/trash-empty

下面是一些不言自明的crontab語法例子：

    30 4 echo "四點半了。"
    0 22 echo "晚上十點了。"
    30 15 25 12 echo "現在是聖誕節下午三點半。"
    30 3 * * * echo "每天早上三點半提醒我。"
    0 * * * * echo "新的一個小時到來了。"
    0 6 1,15 * * echo "每月1號和15號的早上六點。"
    0 6 * * 2,3,5 echo "周二三四的早上六點。"
    59 23 * * 1-5 echo "周內每天的最後一分鐘。"
    0 */2 * * * echo "每兩個小時。"
    0 20 * * 4 echo "周四的晚上八點。"
    0 20 * * Thu echo "周四的晚上八點。"
    */15 9-17 * * 2-5 echo "周內朝九晚五的每一刻鐘。"
    @yearly echo "新年好！"

## 默認編輯器

要修改默認編輯器，請在 shell 初始化腳本中定義 `EDITOR` 環境變量，如環境變量所述。

作為普通用戶，需要使用 `su` 代替 `sudo` 來正確拉取環境變量：

    $ su -c "crontab -e"

如果希望給這個命令取別名，因為 su 會在一個新啟動的子 shell 中啟動，為了防止一些以外的發生，需要用 `printf` 加一個任意字符串，來提醒你仍然在 root 下運行：

    alias scron="su -c $(printf "%q " "crontab -e")"

## 運行基於 X.org 的應用程式

Cron 不在 X.org 下運行，因此它無法知道啟動 X.org 應用程式所需的環境變量，因此必須顯式定義。我們可以使用類似 xuserrun-gitAUR 這樣的程序來完成：

    17 02 * ... /usr/bin/xuserrun /usr/bin/xclock

本文或本節內容已經過時。

原因： 是否仍然有效？ (在Talk:Cron#cron x.org aplications討論)

或者可以手動定義它們（`echo $DISPLAY` 將給出當前的 DISPLAY 環境變量值）：

    17 02 * ... env DISPLAY=:0 /usr/bin/xclock

如果需要在 cron 中運行 notify-send 進行桌面通知，因為 notify-send 通過 dbus 發送值。需要告訴 dbus 連接到正確的總線。 通過檢查 DBUS_SESSION_BUS_ADDRESS 環境變量，並設置為相同的值，就可以找到地址。因此：

    17 02 * ... env DBUS_SESSION_BUS_ADDRESS=your-address notify-send 'Foo bar'

如果是通過SSH完成的，需要給與權限：

    # xhost +si:localuser:$(whoami)

## 異步任務處理

如果你經常關機，但又不想錯過任務的執行，這裡有一些解決方案（從最簡單到最難）：

### Cronie

cronie包 內含 anacron。其項目主頁介紹道：

Cronie 包含了標準的 UNIX 守護進程 crond，它可以在預定的時間運行指定的程序和相關工具。 它基於最初的 cron，並增強了安全性和配置功能，比如可以使用 pam 和 SELinux。

### Dcron

Vanilla dcronAUR 支持異步任務處理。只要用@hourly、@daily、@weekly或者@monthly加上任務名就可以了：

    @hourly         ID=greatest_ever_job      echo This job is very useful.

### Cronwhip

cronwhipAUR 是一個自動運行遺漏的 cron 任務的腳本；它與以前的默認 cron 實現 dcron 一起工作。 另見這個論壇帖子。

### Anacron

Anacron 是 dcron 的完全替代者，它可以異步處理任務。

它由 cronie包 提供，配置文件為 `/etc/anacrontab`。關于格式的信息可以在 anacrontab(5) 中找到。運行 `anacron -T` 可以測試 `/etc/anacrontab` 的有效性。

### Fcron

和anacron一樣，fcron包 假設計算機並不總是在運行。但與anacron不同的是，它可以在比一天更短的時間內安排這些事件，這對於經常暫停/休眠的系統（例如筆記本電腦）可能很有用。和 cronwhip 一樣， fcron 也可以運行那些本應在計算機停機期間運行的作業。

用 fcron 取代 cronie包 時，spool 目錄會變為 `/var/spool/fcron`，並使用 `fcrontab` 命令代替 crontab 來編輯用戶的 crontabs。這些 crontab 以二進制格式存儲，其旁邊的文本版本在 spool 目錄中為 foo.orig。由於這種行為上的差異，任何手動編輯的用戶 crontabs 可能需要進行調整。

一個快速的腳本小程序，可以幫助您將傳統的用戶 crontabs 轉換為 fcron 格式：

    cd /var/spool/cron && (
     for ctab in *; do
      fcrontab ${ctab} -u ${ctab}
     done
    )

另見這個論壇主題。

## 確保排他性

如果您有可能運行很久的任務（比如說變化很多或者網速突然變慢，備份可能會偶爾運行很長時間），那麼 `flock`（util-linux包）可以確保 cron 任務在同一時間點只有一個運行。

      5,35 * * * * /usr/bin/flock -n /tmp/lock.backup /root/make-backup.sh

## Cronie

cronie 的相關文件層次結構如下：

       /etc/
         |----- cron.d/
                  | ----- 0hourly
         |----- cron.minutely/
         |----- cron.hourly/
                  | ----- 0anacron
         |----- anacrontab
         |----- cron.daily/
         |----- cron.monthly/
         |----- cron.weekly/
         |----- crontab
         |----- cron.deny

Cronie 提供了 cron 和 anacron 兩種功能：只要系統在指定的時間可用，cron 以固定的時間間隔（粒度為一分鐘）運行工作，而anacron則在以天為單位指定的時間間隔執行命令。與 cron 不同的是，它並不假設系統連續運行。當系統啟動時，anacron 就會檢查是否有任何漏掉的任務，並進行相應的處理。

cron任務可以在 `/etc/cron.d` 目錄中類似 crontab 的文件中定義，或者在 `/etc/crontab` 文件中添加。注意後者在默認情況下並不存在，但如果存在就會被使用。按照 `/etc/cron.d/0hourly` 的內容，`/etc/cron.hourly` 中的任何可執行文件將每小時運行一次（默認為每小時的第1分鐘），而在 `/etc/cron.minutely` 中的可執行文件將每分鐘執行一次（如果 `/etc/cron.d/0minutely` 中有相應的指令）。這些可執行文件通常是 shell 腳本，也可以使用可執行文件的符號連結。 `/etc/cron.deny` 文件包括不允許使用 crontab 的用戶列表，如果沒有這個文件，只有 `/etc/cron.allow` 中列出的用戶才能使用它。

Anacron的工作原理類似，通過執行根據所需的作業頻率放置在 `/etc/cron.daily`、`/etc/cron.weekly` 和 `/etc/cron.monthly`目錄下的文件，cron 作業 `/etc/cron.hourly/0anacron` 確保 anacron 每天運行一次，以執行其待辦任務。

注意：

- Cronie 使用 `run-parts` 來執行不同目錄下的腳本。文件名中不應該包含任何點號（.），因為 `run-parts` 在默認模式下會忽略它們（參見run-parts(8)）。名字必須只由大小寫字母、數字、下劃線和減號組成。
- `systemctl status cronie` 的輸出可能會顯示諸如 `CAN'T OPEN (/etc/crontab): No such file or directory` 的內容。您可以忽略這些內容，因為這個文件 cronie 不是必須的。
- Cronie 對 `/etc/cron.d/0hourly` 的權限要求很嚴格。如果 `/etc/cron.d/{hourly,weekly,daily}...` 中的任務損壞或權限不正確，`/etc/cron.d/0hourly` 中的所有任務都不會被運行（包括 anacron 啟動器）。`pacman -Qkk cronie` 可以顯示是否有這樣的問題。

提示：如果不需要某些命令的輸出和電子郵件提醒，請在該 cron 任務的行末添加 `>/dev/null 2>&1`，將輸出重定向到/dev/null：

    0 1 5 10 * /path/to/script.sh >/dev/null 2>&1

您也可以在您的 crontab 文件中設置 `MAILTO=""` 變量來禁用全部電子郵件提醒。

## Dcron

cron守護進程會解析一個名為`crontab`的配置文件。系統中的每個用戶都可以維護一個單獨的crontab文件來單獨調度命令。root 用戶的 crontab 用於調度全系統的任務（用戶可以選擇使用 `/etc/crontab` 或 `/etc/cron.d` 目錄，這取決於他們選擇的 cron 實現）。

    /var/spool/cron/root

    # Run command at a scheduled time
    # Edit this 'crontab -e' for error checking, man 1 crontab for acceptable format

    # <@freq>                       <tags and command>
    @hourly         ID=sys-hourly   /usr/sbin/run-cron /etc/cron.hourly
    @daily          ID=sys-daily    /usr/sbin/run-cron /etc/cron.daily
    @weekly         ID=sys-weekly   /usr/sbin/run-cron /etc/cron.weekly
    @monthly        ID=sys-monthly  /usr/sbin/run-cron /etc/cron.monthly

    # mm  hh  DD  MM  W /path/command (or tags) # W = week: 0-6, Sun=0
      21  01  *   *   * /usr/bin/systemctl suspend

下面幾行是 crontab 條目的一種可接受格式，即以空格分隔的欄位:

1. @period
2. ID=jobname （這個是 dcron 獨有的）
3. command

crontab 條目的另一種標準格式是：

1. minute
2. hour
3. day
4. month
5. day of week
6. command

crontab文件本身通常存儲為 `/var/spool/cron/username`。例如，root 的 crontab 文件位於 `/var/spool/cron/root`。

參見 crontab man page 獲取更多信息和配置示例。

## 另請參閱

- Gentoo Linux Cron 指南
- crontab.guru - cronjob 表達式在線編輯器
- cron-notifyAUR 是一個兼容 FreeDesktop.org 的通知服務，用於在執行命令前定期請求確認。命令在自定義配置文件中進行配置。

分類：​

- 系統管理
- 使用「模板:過時」標記的頁面或章節

cron
新增話題