1 搞清概念
1.1 cron與crond
cron 是linux下實現(xiàn)任務(wù)調(diào)度(定時任務(wù))的一種服務(wù),可以在無需人工干預(yù)的情況下運行作業(yè)。
crond 則是 cron 服務(wù)的守護進程,與windows下的計劃任務(wù)類似。
Linux系統(tǒng)會默認安裝cron服務(wù)工具,并自動啟動crond進程。
1.2 crontab
crontab 是Linux系統(tǒng)提供的用于設(shè)置定時任務(wù)的命令行工具。
crontab 也指 cron 服務(wù)的配置文件,是“cron table”的縮寫,語義上即為“任務(wù)調(diào)度列表”。
1.3 系統(tǒng)任務(wù)調(diào)度與用戶任務(wù)調(diào)度
Linux的任務(wù)調(diào)度主要分為兩類:
系統(tǒng)任務(wù)調(diào)度:系統(tǒng)周期性所要執(zhí)行的工作,比如系統(tǒng)數(shù)據(jù)備份、臨時文件清理、緩存清理等。
用戶任務(wù)調(diào)度:某個用戶定期要執(zhí)行的工作,比如用戶數(shù)據(jù)備份、定時郵件提醒等,這些工作可由每個用戶自行設(shè)置。
2 搞清權(quán)限
默認情況下,只有root可以使用crontab,而普通用戶不可以,會出現(xiàn)如下提示信息:

root可以通過/etc/cron.allow與/etc/cron.deny兩個文件來控制哪個用戶有權(quán)使用crontab。這兩個文件的內(nèi)容與格式很簡單,將需要配置的用戶名寫入文件,每個用戶名占一行。
權(quán)限規(guī)則如下:
|
cron.allow |
cron.deny |
有權(quán)使用crontab的用戶范圍 |
1 |
文件不存在 |
文件不存在 |
僅root可使用crontab |
2 |
文件存在 |
文件不存在 |
僅cron.allow中的用戶可使用crontab |
3 |
文件不存在 |
文件存在 |
只要不在cron.deny中的用戶都可以使用crontab |
4 |
文件存在 |
文件存在 |
僅cron.allow中的用戶可使用crontab(cron.allow優(yōu)先級高于cron.deny,此時cron.deny不起作用) |
由于cron.allow與cron.deny同時存在的情況下,cron.deny沒有實際意義。因此從邏輯、語義清晰的角度出發(fā),不建議同時保留兩個文件。根據(jù)實際需求,白名單與黑名單保留一個就好。
3 搞清配置文件
系統(tǒng)任務(wù)調(diào)度對應(yīng)的配置文件有/etc/crontab文件和/etc/cron.d/目錄下的一系列文件。
用戶任務(wù)調(diào)度對應(yīng)的配置文件在/var/spool/cron/目錄下。
cron服務(wù)每隔1分鐘會去讀取這些文件中的調(diào)度任務(wù)。
這一個小節(jié)我們先從宏觀上認識這些配置文件,而這些文件的詳細配置語法我們留到下一個小節(jié)討論。
3.1 /etc/crontab
/etc/crontab是一個文件,root可以通過這個文件來維護系統(tǒng)級的調(diào)度任務(wù)。

一般情況下,不建議將所有的全局性的調(diào)度任務(wù)都維護在/etc/crontab這一個文件中,那樣會很混亂,因此有了/etc/cron.d/。
3.2 /etc/cron.d/
/etc/cron.d/是一個目錄,也由root維護。這個目錄下可以存放多個crontab任務(wù)文件,這樣我們就可以以文件的粒度對不同類別的任務(wù)調(diào)度進行管理了。
/etc/cron.d/目錄下的任務(wù)調(diào)度文件需要遵循以下命名規(guī)范才能被cron服務(wù)正確掃描:
- 只能包含字母、數(shù)字、下劃線和中劃線(尤其注意不能包含 . )
/etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly 這些是什么?
系統(tǒng)預(yù)設(shè)了這四個目錄,用來放置每小時、每天、每周、每月要執(zhí)行的可執(zhí)行腳本文件。
注意:這四個目錄只是預(yù)設(shè),并沒有完全啟用,并不是直接將可執(zhí)行腳本文件放入其中就可以,還需要run-parts命令的配合。
筆者所使用的centos 7.2中,就只有 /etc/cron.hourly 這一個目錄作為系統(tǒng)樣例被啟用了(配置在了/etc/cron.d/目錄下的 0hourly 文件中)。
3.3 /var/spool/cron/
/var/spool/cron/是一個目錄,用來存放包括root在內(nèi)的每個用戶獨有的crontab任務(wù)文件,文件名同用戶名,如用戶“pcserver10”的任務(wù)文件為“
/var/spool/cron/pcserver10”。
/var/spool/cron/目錄下的crontab任務(wù)文件不可以直接創(chuàng)建或直接修改,而是通過crontab命令來維護。
4 搞清語法
4.1 cron服務(wù)啟停
功能 |
命令 |
查看cron服務(wù)狀態(tài) |
service crond status |
啟動cron服務(wù) |
service crond start |
停止cron服務(wù) |
service crond stop |
重啟cron服務(wù) |
service crond restart |
調(diào)度任務(wù)沒有執(zhí)行,我們首先要通過service crond status來確認cron服務(wù)是否已啟動。
在調(diào)度任務(wù)突然失效的情況下,我們可以嘗試service crond restart重啟cron服務(wù)來解決問題。
4.2 crontab命令
crontab命令用來維護用戶級的調(diào)度任務(wù),其實就是維護/var/spool/cron/目錄下對應(yīng)用戶的crontab任務(wù)文件,語法格式如下:
crontab [-u user] file
crontab [-u user] { -e | -l | -r }
復(fù)制代碼
參數(shù)說明:
- -u user:用來指定要維護哪一個用戶的crontab任務(wù),此選項一般由root用戶來使用,普通用戶維護自己的crontab任務(wù)表時不需要此選項。
- file:file是文件名,我們可以提前維護好一個任務(wù)文件,然后通過crontab file命令將所指定的文件內(nèi)容載入到crontab中。
- -e:進入文本編輯器,編輯當(dāng)前或指定用戶的任務(wù)信息(本質(zhì)上是在編輯一個臨時文件,這個臨時文件先讀取了/var/spool/cron/目錄下對應(yīng)用戶的crontab任務(wù)文件的內(nèi)容,完成編輯后,保存并退出文本編輯器的時候,會用臨時文件的內(nèi)容覆蓋對應(yīng)的crontab任務(wù)文件)。
- -l:顯示當(dāng)前或指定用戶的任務(wù)信息。
- -r:刪除當(dāng)前或指定用戶的全部任務(wù)信息(即刪除了/var/spool/cron/目錄下對應(yīng)用戶的crontab任務(wù)文件)。
特別注意:
由于crontab -r刪除任務(wù)表后不可恢復(fù),因此強烈建議為任務(wù)表做好備份。
也就我們在維護任務(wù)表時,不要因為方便而使用crontab -e直接編輯任務(wù)表,而是應(yīng)該將任務(wù)表的內(nèi)容維護至一個備份文件中,再通過crontab file命令更新任務(wù)表,這樣我們就能留有一份備份文件,以防誤操作使用了crontab -r而無法恢復(fù)。
4.3 crontab文件配置 - 調(diào)度任務(wù)部分
不管是/etc/crontab文件本身、/etc/cron.d/目錄下的任務(wù)文件,還是通過crontab命令維護的/var/spool/cron/目錄下的任務(wù)文件,這些文件的配置語法基本一致(僅/var/spool/cron/下的文件稍微有不同)。
以/etc/crontab為例,我們來看一下crontab任務(wù)文件的內(nèi)容,可以將其分為環(huán)境變量和調(diào)度任務(wù)兩個部分:

4.3.1 cron任務(wù)的基本格式
每一個cron任務(wù)的格式如下:
<分鐘> <小時> <日期> <月份> <星期> [<用戶>] <指令>
復(fù)制代碼
- 前五個配置項用來表示任務(wù)執(zhí)行的時間。
- 【用戶】用來指定執(zhí)行任務(wù)的用戶,僅適用于系統(tǒng)級的任務(wù)調(diào)度文件。(通過crontab命令來維護的用戶級任務(wù)調(diào)度(/var/spool/cron/目錄下的任務(wù)文件)不存在此配置項 )
- 【指令】就是要執(zhí)行的具體任務(wù)。
4.3.2 時間的配置
首先來看各個時間配置項的取值范圍:
配置項 |
取值范圍 |
分鐘 |
取值范圍:0-59 |
小時 |
取值范圍:0-23 |
日期 |
取值范圍:1-31 |
月份 |
取值范圍:1-12,也可以使用jan、feb、mar、apr、may、jun、jul、aug、sep、oct、nov、dec(不區(qū)分大小寫) |
星期 |
取值范圍:0-7(0與7均代表星期日),也可以使用mon、tue、wed、thu、fri、sat、sun(不區(qū)分大小寫) |
除了上述的取值范圍外,還支持以下特殊符號:
特殊符號 |
詳細說明 |
*(星號) |
“每一個”的意思,代表著取值范圍內(nèi)的任何一個有效值都需要執(zhí)行指令。 |
-(減號) |
用來表示時間范圍(只支持數(shù)字,不支持用名稱來表示的月份與星期),如:“3-6” |
,(逗號) |
可以用來分隔開一系列的值,來形成一個時間列表(只支持數(shù)字,不支持用名稱來表示的月份與星期),如:“1,3,5,7”、“0-4,8-12” |
/n(斜線 + 數(shù)字) |
用來指定時間間隔與頻率,也就是“每隔 n 個時間單位”的意思,如:“*/2”用在小時上代表著每2個小時、“0-29/2”用在分鐘上代表著前半個小時中每2分鐘 |
下面是一些例子:
表達式 |
說明 |
0 6 * * * |
每天早6點 |
0 */2 * * * |
每兩個小時 |
0 23-7/2,8 * * * |
每晚11點到早8點間的每兩個小時以及早8點 |
0 11 * * 1-3 |
每周一到周三的早11點 |
0 4 1 1 * |
1月1日的早4點 |
5,15,25,35,45,55 16,17,18 * * * |
每天下午4點、5點、6點的5分、15分、25分、35分、45分、55分 |
特別注意:
可以分別以周或以日月為單位,但不要使用 “幾月幾號且為星期幾” 的模式,如:“0 6 1 1 3”。從語法上這個時間表達式并沒有錯誤,可以運行。但是從語義上卻存在歧義,我們可能希望1月1號且為周三時執(zhí)行,但系統(tǒng)有可能會處理為每年1月1號與每個周三分別執(zhí)行。
我們還可以用以下“昵稱”來替代五個時間配置項:
昵稱 |
詳細說明 |
@reboot |
系統(tǒng)重啟時執(zhí)行一次指令 |
@yearly |
每年1月1號0點0分執(zhí)行一次指令,即“0 0 1 1 *” |
@annually |
同@yearly |
@monthly |
每月1號0點0分執(zhí)行一次指令,即“0 0 1 * *” |
@weekly |
每周日0點0分執(zhí)行一次指令,即“0 0 * * 0” |
@daily |
每天0點0分執(zhí)行一次指令,即“0 0 * * *” |
@hourly |
每小時0分執(zhí)行一次指令,即“0 * * * *” |
4.3.3 指令的形式
指令可以有以下幾種形式:
- 單條命令,如:* * * * * echo -e $(date '+%Y%m%d') >> /root/tmp.log
- 以;分隔的多條命令,如:* * * * * . /etc/profile;/bin/sh /root/scripts/test.sh
- 完整的可執(zhí)行腳本路徑,如:* * * * * /root/scripts/test.sh
- 通過run-parts命令來執(zhí)行某個目錄下的所有可執(zhí)行腳本文件,如:* * * * * run-parts /root/scripts/cron.minitely
注意:%是crontab的特殊字符,第一個%后的所有數(shù)據(jù)都會被作為標(biāo)準輸入。在指令中如需使用%,則需要進行轉(zhuǎn)義%,如:date '+%Y%m%d'。
4.3.4 如何實現(xiàn)秒級的任務(wù)?
crontab可配置的最小的時間粒度是分鐘,那么我們?nèi)绾螌崿F(xiàn)秒級的任務(wù)呢?有如下兩種方式:
- 相同的任務(wù)配置多條,通過sleep來制造執(zhí)行的時間差,如每10秒執(zhí)行一次:
* * * * * command
* * * * * sleep 10; command
* * * * * sleep 20; command
* * * * * sleep 30; command
* * * * * sleep 40; command
* * * * * sleep 50; command
復(fù)制代碼
- 僅配置一條任務(wù),在任務(wù)所執(zhí)行的腳本內(nèi),通過for循環(huán)實現(xiàn)每隔n秒執(zhí)行一次的邏輯,如:
* * * * * script
復(fù)制代碼
#!/bin/sh
STEP=5
for ((i=0; i < 60; i+=STEP))
do
TIME_NOW=$(date "+%Y-%m-%d %H:%M:%S")
echo -e $TIME_NOW >> /root/scripts/logs/test.log
sleep $STEP
done
復(fù)制代碼
4.4 crontab文件配置 - 環(huán)境變量部分

4.4.1 基本說明
在crontab任務(wù)文件的頭部,可以設(shè)置以下環(huán)境變量:
環(huán)境變量 |
說明 |
由crond設(shè)置的默認值 |
SHELL |
指定系統(tǒng)要使用哪種shell解釋器 |
/bin/sh |
PATH |
指定系統(tǒng)執(zhí)行命令的路徑 |
/usr/bin:/bin |
MAILTO |
指定cron將任務(wù)執(zhí)行的相關(guān)信息通過電子郵件發(fā)送給哪個用戶 |
無 |
HOME |
指定執(zhí)行命令或者腳本時使用的主目錄 |
根據(jù)crontab的擁有者從/etc/passwd文件中獲取 |
4.4.2 PATH 環(huán)境變量不起作用的巨坑
當(dāng)我們的指令或腳本依賴于環(huán)境變量的時候,問題就出現(xiàn)了。在命令行中可以順利運行的指令或腳本,在crontab中卻無法正確執(zhí)行。這是因為crontab不會從用戶的profile文件中讀取環(huán)境變量參數(shù)。
這時我們就會把目光轉(zhuǎn)向PATH,直接設(shè)置這個變量不就可以了嘛。然而,這個變量似乎就是個擺設(shè),并不會按照我們想象的那樣去運行。
這里記錄一下PATH的實驗過程:
筆者的CentOS 7.2,系統(tǒng)默認安裝了OpenJDK,版本為號為1.8.0.181。
筆者又重新配置了1.8.0_144版本的JDK,寫入了/etc/profile系統(tǒng)環(huán)境變量中:
JAVA版本驗證如下:
現(xiàn)在我們寫一個定時任務(wù),將java -version的輸出結(jié)果寫入日志中:* * * * * java -version 2>>
/root/scripts/logs/test_java.log 復(fù)制代碼 看一下結(jié)果:
這不是我們想要的結(jié)果,很明顯環(huán)境變量出了問題,我們把環(huán)境變量打印出來:
* * * * * echo -e "PATH="$PATH >>
/root/scripts/logs/test_java.log;java -version 2>> /root/scripts/logs/test_java.log 復(fù)制代碼 再看結(jié)果:
環(huán)境變量果然不對,當(dāng)前是crontab文檔中說的由crond所設(shè)置的默認值。我們通過PATH手動修改一下吧:
PATH=
/sbin:/bin:/usr/sbin:/usr/bin:/usr/java/jdk1.8.0_144/bin:/usr/java/jdk1.8.0_144/jre/bin: * * * * * echo -e "PATH="$PATH >> /root/scripts/logs/test_java.log;java -version 2>> /root/scripts/logs/test_java.log 復(fù)制代碼 再看結(jié)果:
環(huán)境變量倒是設(shè)置成功了,但是實際上并沒有生效,所以,設(shè)置PATH感覺就是設(shè)置了個寂寞。
那么環(huán)境變量的問題該如何解決呢?有以下兩種方式:
- 修改cron任務(wù),在原有的指令前增加讀取環(huán)境變量文件的命令:
* * * * * . /etc/profile; your command
復(fù)制代碼
- 在所要執(zhí)行的腳本文件的開頭增加讀取所需的環(huán)境變量文件的命令:
#!/bin/sh
. /etc/profile
. ~/.bash_profile
復(fù)制代碼
4.4.3 crontab執(zhí)行后發(fā)送的郵件
每一個調(diào)度任務(wù)執(zhí)行完畢時,都會將標(biāo)準輸出與錯誤輸出信息以電子郵件的形式發(fā)送給對應(yīng)的用戶,/var/spool/mail/目錄下的與用戶同名的文件用來存放對應(yīng)用戶所接收到的郵件信息,郵件內(nèi)容如:
我們要討論的第一個問題就是MAILTO所能起到的作用。
(1)在沒有配置MAILTO的情況下:
- /etc/crontab文件和/etc/cron.d/目錄下的任務(wù)文件,配置調(diào)度任務(wù)時會指定執(zhí)行用戶,電子郵件相應(yīng)的也就會發(fā)送給這個用戶。
- 通過crontab命令維護的任務(wù)文件自身屬于哪個用戶,電子郵件就發(fā)送給哪個用戶。
(2)配置了MAILTO的情況下:
- MAILTO可以改變上述的默認行為,將電子郵件發(fā)送給指定的用戶。
- MAILTO作用于文件中的所有調(diào)度任務(wù),且僅在當(dāng)前文件中生效,不會跨文件生效。
- 僅定義MAILTO但不賦值(即MAILTO=),此時不會發(fā)送任何郵件。
調(diào)度任務(wù)執(zhí)行后發(fā)送郵件,這樣日積月累,郵件文件會越來越大,嚴重的情況下可能會影響系統(tǒng)的運行。我們要討論的下一個問題就是妥善的處理郵件信息。
上面已經(jīng)提到了,我們可以通過定義一個空的MAILTO,來禁止發(fā)送郵件的行為。但MAILTO會作用于文件中所有的任務(wù),是否有辦法從單個任務(wù)的角度出發(fā)來解決這個問題呢?我們可以使用輸出重定向,在每條指定后增加: >/dev/null 2>&1。(當(dāng)前,這么做的前提是,對所需要的正常的輸出已經(jīng)做了處理,比如追加到某個日志文件中。)
4.4.4 體驗 HOME 的作用
雖然實際情況下,基本用不到也不建議使用HOME(建議所有的地方都使用絕對路徑,以屏蔽因路徑而引發(fā)的各類問題),但并不妨礙我們實踐并體驗一下HOME的作用。
我們在/root/scripts目錄下做4個測試腳本,并將HOME的值設(shè)置為此目錄,然后以4種不同的寫法做測試:
測試結(jié)果如下:
- sh home_test_01,因為已指定了HOME=/root/scripts,執(zhí)行腳本時會到此路徑下去尋找腳本文件,因此能夠正確執(zhí)行。
- sh $HOME/home_test_02和$HOME/home_test_04,在指令中引用了所設(shè)置的環(huán)境變量HOME,實際上相當(dāng)于指明了絕對路徑,可以正確執(zhí)行。
- home_test_03不能執(zhí)行,不通過命令(sh)執(zhí)行的腳本文件,只有寫完整的路徑才能執(zhí)行。
上述測試過程僅為體驗,依然建議:所有的地方都使用絕對路徑!!!
4.5 其他注意事項
- 應(yīng)該為調(diào)度任務(wù)編寫注釋,以#開頭的行即為注釋行。
- root用戶可以查看cron服務(wù)的運行日志,/var/log/cron。
作者:熙寧
鏈接:
https://juejin.cn/post/7065972818965430286