cron

出自 Arch Linux 中文维基

摘自 Wikipedia:

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

安裝[編輯 | 編輯原始碼]

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

可用的包:

配置[編輯 | 編輯原始碼]

激活及開機啟動[編輯 | 編輯原始碼]

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

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

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

處理任務中的錯誤[編輯 | 編輯原始碼]

cron 會記錄 stdoutstderr 的輸出並嘗試通過 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 以確保 cronie 能夠檢測到新配置的 /usr/bin/sendmail 命令。

使用 mSMTP 的例子[編輯 | 編輯原始碼]

安裝 msmtp-mta, 安裝時會創建連結 /usr/bin/sendmail 指向 /usr/bin/msmtp。重啟 cronie 以確保 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 的例子[編輯 | 編輯原始碼]

安裝 esmtpAURprocmailAUR

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

/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(時序)
chronic 運行一個命令時,會緩存它的標準輸出和標準錯誤輸出,並只在命令失敗(返回值非零或崩潰)的情況下才會一併輸出。如果命令成功執行,任何無關的輸出將被隱藏。
sponge(海綿)
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, 使用root運行下面的命令:

# 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 格式一章相同,這一行

*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

或者可以手動定義它們(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
)

另見這個論壇主題

確保排他性[編輯 | 編輯原始碼]

如果您有可能運行很久的任務(比如說變化很多或者網速突然變慢,備份可能會偶爾運行很長時間),那麼 flockutil-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 提供了 cronanacron 兩種功能:只要系統在指定的時間可用,cron 以固定的時間間隔(粒度為一分鐘)運行工作,,而anacron則在以天為單位指定的時間間隔執行命令。與 cron 不同的是,它並不假設系統連續運行。當系統啟動時,anacron 就會檢查是否有任何漏掉的任務,並進行相應的處理。

cron任務可以在 /etc/cron.d 目錄中類似 crontab 的文件中定義,或者在 /etc/crontab 文件中添加。注意後者在默認情況下並不存在,但如果存在就會被使用。按照 /etc/cron.d/0hourly 的內容,/etc/cron.hourly 中的任何可執行文件將每小時運行一次(默認為每小時的第1分鐘),而在 /etc/cron.minutely 中的可執行文件將每分鐘執行一次。這些可執行文件通常是 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 實現)。

The cron daemon parses a configuration file known as crontab. Each user on the system can maintain a separate crontab file to schedule commands individually. The root user's crontab is used to schedule system-wide tasks (though users may opt to use /etc/crontab or the /etc/cron.d directory, depending on which cron implementation they choose).

/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 獲取更多信息和配置示例。

另請參見[編輯 | 編輯原始碼]