cron
摘自 Wikipedia:
- cron 是一個在 Unix 及類似作業系統上執行計劃任務的程序。cron 使用户能夠安排工作(命令或shell腳本)在特定時間、日期或間隔定期運行,通常用於系統的自動化維護或者管理。
安裝[編輯 | 編輯原始碼]
cron 有多個實現程序,但是基礎系統默認使用 systemd 的 Timers,下面的實現都不被默認安裝。Gentoo Linux Cron 指南提供了一個這些實現之間的比較。
可用的包:
配置[編輯 | 編輯原始碼]
激活及開機啟動[編輯 | 編輯原始碼]
安裝後,默認的守護進程不會啟動。安裝的軟件包通常提供了可以用 systemctl 控制的服務文件。例如 cronie 使用 cronie.service
。
/etc/cron.daily/
和類似的目錄包含當前的任務,啟動 cron 服務時會觸發所有這類任務。
0anacron
任務,每小時執行一次,它允許延遲運行其他某些任務,比如因為未開機而延遲的任務。處理任務中的錯誤[編輯 | 編輯原始碼]
cron 會記錄 stdout 和 stderr 的輸出並嘗試通過 sendmail
命令發送郵件給用户。如果 Cronie 未找到 /usr/bin/sendmail
,則會禁用郵件通知。要發送郵件到用户的 spool,需要在系統上運行一個 smtp 守護進程,例如 opensmtpd包。也可以安裝提供 sendmail 命令的軟件包,然後配置成通過外部郵件伺服器發送郵件。或者使用 -m
選項將錯誤記錄到日誌並通過定製的腳本進行處理。
使用 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 的例子[編輯 | 編輯原始碼]
安裝完成後,進行如下配置:
/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
這時會發生以下事件:
- cron 運行這個腳本
- 一旦 cron 檢查到腳本有輸出,就會啟動你的郵件傳輸程序,並向通過管道其傳遞一些必要的頭部內容。因為腳本還沒有結束,還會有更多的輸出,管道就沒有關閉。
- 郵件傳輸程序打開到郵件伺服器的連結,並等待後續的輸出。
- 郵件伺服器會在特定時間後關掉這個空閒的連結,你會受到類似這樣的錯誤 :
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 )
另見這個論壇主題。
確保排他性[編輯 | 編輯原始碼]
如果您有可能運行很久的任務(比如説變化很多或者網速突然變慢,備份可能會偶爾運行很長時間),那麼 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
中的可執行文件將每分鐘執行一次。這些可執行文件通常是 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
可以顯示是否有這樣的問題。
>/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 條目的一種可接受格式,即以空格分隔的字段:
- @period
- ID=jobname (這個是 dcron 獨有的)
- command
crontab 條目的另一種標準格式是:
- minute
- hour
- day
- month
- day of week
- command
crontab文件本身通常存儲為 /var/spool/cron/username
。例如,root 的 crontab 文件位於 /var/spool/cron/root
。
參見 crontab man page 獲取更多信息和配置示例。
另請參見[編輯 | 編輯原始碼]
- Gentoo Linux Cron 指南
- crontab.guru - cronjob 表達式在線編輯器。