一文精通 crontab 從入門到出坑

此篇技術博文主要介紹的是crontab,Linux下的計劃任務管理工具。涉及內容包括crontab使用配置、常見坑的分析和編者總結的錯誤調試方法。php

個人理解,後臺任務一般分爲兩種:常駐和定時。以前的文章《pm2進程管理工具使用總結》主要針對的是常駐任務。今天來談談crontab,主要針對的是定時任務。html

實驗環境:centos7node

介紹crontab

crontab的服務進程名爲crond,英文意爲週期任務。顧名思義,crontab在Linux主要用於週期定時任務管理。一般安裝操做系統後,默認已啓動crond服務。crontab可理解爲cron_table,表示cron的任務列表。相似crontab的工具還有at和anacrontab,但具體使用場景不一樣,可參見附錄《讓你學會Linux計劃任務》一文了解更多。程序員

關於crontab的用途不少,如shell

  • 定時系統檢測;
  • 定時數據採集;
  • 定時日誌備份;
  • 定時更新數據緩存;
  • 定時生成報表;
    ...
    等等任務

固然,更多使用場景是要以視具體狀況而定了。畢竟是工具一般都是經常使用規則總結而成的產物。vim

確認crond服務已經安裝與開啓以後,下面開始具體說明centos

簡單示例

先來個簡單示例體驗一下。緩存

  • 目標:每分鐘向/tmp/time.txt文件下寫入當前時間
  • 新建crontab任務
$ crontab -e      // 打開crontab任務編輯
* * * * * date >> /tmp/time.txt
  • 靜靜等待幾分鐘
$ cat /tmp/time.txt
Do 29. Dez 22:45:01 CST 2016
Do 29. Dez 22:46:01 CST 2016
Do 29. Dez 22:47:01 CST 2016
  • 從上面結果看出,每分鐘執行了date並寫入到/tmp/time.txt。

簡單示例演示成功。下面從細節深刻說明crontab使用。bash

使用選項

上面的實驗中使用了crontab命令的-e選項。咱們來看看crontab命令中有哪些選項?服務器

-e 選項 表示打開當前用戶的crontab任務列表配置文件。固然也能夠直接打開,路徑一般是在/var/spool/cron/下,文件以用戶名命名,如/var/spool/cron/root。不過,採用-e方式打開,福利是能夠幫助咱們自動檢查任務配置符合規則。

-u 選項 指定某用戶的任務列表,很好理解。好比我當前是root用戶,想操做poloxue用戶的任務列表。以下:

$ crontab -u poloxue -e

-l 選項 列出某用戶的全部任務列表
-r 選項 刪除某用戶的全部任務列表,這個選項使用當心爲上,估計也只是本身實驗時玩玩而已,正常不使用。

crontab命令的選項中,主要使用的就是以上幾個,理解比較簡單。

任務配置

說完了crontab的命令選項,下面開始真正的大戲,任務列表文件如何配置?

首先,看下crontab任務列表配置格式,示例文件以下:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# 更多細節 man 4 crontabs

# 計劃任務定義的例子:
# .---------------- 分 (0 - 59)
# |  .------------- 時 (0 - 23)
# |  |  .---------- 日 (1 - 31)
# |  |  |  .------- 月 (1 - 12)
# |  |  |  |  .---- 星期 (0 - 7) (星期日可爲0或7)
# |  |  |  |  |
# *  *  *  *  * 執行的命令
* * * * * date >> /time.txt 2>&1

從上面的示例文件可看出,crontab的任務列表主要由兩部分組成:環境變量配置與定時任務配置。可能你們在工做中更可能是隻用到了任務配置部分。

環境變量配置部分

理解環境變量配置這部分能夠幫助咱們減小去踩一些沒必要要的坑。簡單說明上面涉及的環境變量。

SHELL爲/bin/bash,表示使用/bin/bash解釋執行命令

PATH表示到哪些目錄路徑尋找命令程序,此環境變量的值說明了爲何咱們在crontab中執行命令時,儘可能要寫命令全路徑才能執行的緣由。

MAILTO變量做用是當任務執行有輸出時,內容發送到哪一個用戶的郵箱。禁用能夠設置MAILTO=""。

當咱們在使用crontab時,發現某些定時任務不能順利執行,但shell控制檯執行成功,環境變量是否正確是咱們須要首先關注的點之一。具體詳情能夠看後面關於環境變量坑的說明。

定時任務配置部分

這部分是crontab配置核心。

基本配置

以下所示配置共6列,前5列是關於執行時間配置,最後1列是具體執行命令。

.---------------- 分 (0 - 59)
|  .------------- 時 (0 - 23)
|  |  .---------- 日 (1 - 31)
|  |  |  .------- 月 (1 - 12)
|  |  |  |  .---- 星期 (0 - 6) (星期日可爲0或7)
|  |  |  |  |
*  *  *  *  * 執行的命令

第一列單位爲分,表示每時第幾分鐘,範圍爲0-59;
第二列單位爲時,表示天天第幾小時,範圍爲0-23;
第三列單位爲日,表示每個月第幾天,範圍爲1-31;
第四列單位爲月,表示每一年第幾月,範圍爲1-12;
第五列單位爲星期,表示每星期第幾天,範圍0-7,0與7表示星期日,其餘分別爲星期1-6;

時間配置段類型

根據時間列中值的不一樣設置方式,編者總結出如下五種類型:

固定某值,指定固定值,如指定1月1日0時0分執行任務

0 0 1 1 * command

月日時分都指定了固定數值。
<font color=red>注:*在crontab中表示任意值都知足條件。</font>

列表值,時間值是一個列表,如指定一個月內二、十二、22日零時執行任務

0 0 2,12,22 * * command

上述日指定多個值,2號、12號和22號,以逗號分隔;

連續範圍值,時間爲連續範圍的值,如指定每月1至7號零時執行任務

0 0 1-7 * * command

上述日期爲連續範圍的值1-7時

步長值,根據指定數值跳躍步長肯定執行時間,如指定凌晨1時開始每割3個小時0分執行一次任務

0 1-24/3 * * * command

上述指定從凌晨1時每3個小時執行任務,如1點0分,4點0分,7點0分等。

混合值,支持以上類型的組合,如指定每小時0至10分,2二、33分以及0-60分鐘每隔20分鐘執行任務,以下

0-10,22,33,*/20 * * * * command

這裏的分鐘值採起了多種類型組合指定,包括連續範圍值(0-7),列表值(22,33),步長值(*/20)。

<font color="red">聲明:這幾種時間配置類型是編者本身總結,但願能幫助你們更好理解。有錯誤幫忙指出。</font>

定時語句解析工具

一般在使用crontab添加任務時,咱們會依靠本身已有知識編寫定時語句。當須要測試語句是否正確時,總須要必定時間等待證實其正確性。做爲一名牛逼的程序員,這種方式就太不酷了。有沒有一款工具,只要咱們給出語句,其就能告訴具體執行時間呢?下面介紹一款老外開發的crontab在線解析工具。

工具地址:https://crontab.guru

下面是這個工具的截圖

clipboard.png

從上面看出,咱們輸入的語句解析結果爲天天的04:05執行任務。下面有這樣一行文字「next at 2016-12-31 04:05:00」,告訴了咱們最近一次的執行時間。

<font color=red>註明:百度搜索「crontab在線解析」得到的工具備坑,某些語句解析結果錯誤。爲避免你們受騙,這裏提供具體地址:http://tool.lu/crontab/&lt;/font>

使用有坑

crontab使用中常會遇到各類坑。下面列出編者在使用中曾遇到的一些問題。

時間配置誤區

此處介紹兩種坑,一種是因爲基本功不足致使配置錯誤,而另外一種則是多數人對crontab配置都存在的一個理解誤區。

整點時間設置錯誤

其實這個錯誤不用單獨說明,可是編者剛開始接觸crontab時犯過,單獨拿出來講明一下。

如設定天天3點執行一次某任務

下面列出錯誤方式,當咱們聽到天天3點執行一次某任務時,不少人會把重點放在3點,而忽略了執行一次的需求。

下面是個錯誤的例子

* 3 * * * command

這裏會致使在三點的每分鐘都會執行一次任務,也就是執行了60次。
正確方式以下,天天3點0時執行任務

0 3 * * * command

日與星期的關係誤區

這真的是個大誤區,不少人都不知道的大誤區。直接開始說明吧。

好,首先作兩個練習

設置任務一:每個月的1-7天天零時執行某任務,答案以下:

0 0 1-7 * * date >> /tmp/date.txt

設置任務二:每星期的星期一零時執行某任務,答案以下:

0 0 * * 1 date >> /tmp/date.txt

上面兩個任務的設定都是正確的。

下面提出第三個任務,設置每月的第一個星期一零時執行某任務

分解任務要求,首先,第一個星期就是每月的1-7日,而星期一就是星期一。因此咱們理解的crontab任務配置以下

0 0 1-7 * 1 date >> /tmp/date.txt

下面直接使用前面介紹的在線解析工具分析此語句,以下crontab_time.png

解析結果顯示語句執行時間爲每個月的1至7日和每星期一。能夠看到最近執行時間是「next at 2017-01-01 00:00:00」,這個時間也並不是星期一。

這是crontab的一個特別容易誤解之處,下面直接給出結論:

  • 當日和星期任一列包含*時,日與星期二者爲而且的關係;
  • 當日和星期列中不包含*時,日與星期二者爲或者的關係;

<font color=red>請注意,前面提到的那個百度搜索出來的工具分析結果顯示的確是每個月第一個星期一,這是錯誤的。若有朋友持懷疑態度,可自行驗證,若有錯誤,隨時告知。</font>

環境變量問題

當咱們剛使用crontab時,有人會告知全部命令儘可能都使用絕對路徑,以防錯誤。爲何?這就和咱們下面要談的環境變量有關了。

首先,獲取控制檯環境變量看下

$ env
XDG_SESSION_ID=10
HOSTNAME=localhost.localdomain
SHELL=/bin/bash
PERL_MB_OPT=--install_base /root/perl5
USER=root
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php5/bin
PWD=/var/mail
SHLVL=1
HOME=/root
LOGNAME=root
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/env

<font color=red>考慮篇幅,輸出有刪減。</font>

而後,獲取crontab環境變量信息

* * * * * /usr/bin/env > /tmp/env.txt

輸出結果,以下

$ cat /tmp/env.txt
XDG_SESSION_ID=732
SHELL=/bin/sh
USER=root
PATH=/usr/bin:/bin
PWD=/root
LANG=de_DE.UTF-8
SHLVL=1
HOME=/root
LOGNAME=root
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/en

對比分析二者輸出

對比crontab與控制檯輸出,咱們發現二者的環境變量差別很大。若是命令在控制檯執行成功,而在crontab執行失敗,咱們須要考慮是否命令涉及的環境變量在crontab和控制檯間存在差別。

明白crontab使用絕對路徑執行命令緣由了嗎?

咱們知道命令默認查找路徑是由PATH指定的。

從上面輸出結果可知,控制檯的PATH值爲

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php/bin

crontab的PATH值爲

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php/bin

crontab的PATH值爲

PATH=/usr/bin:/bin

/usr/local/php/bin/下面存在php命令,在控制檯執行成功

$ php index.php

因在crontab的PATH變量無/usr/local/php/bin/,其執行php命令則會失敗。

解決方式

已知哪一個環境變量致使問題,能夠直接在crontab配置中加入變量配置。

不知哪一個環境變量致使問題,終極大招是引入控制檯環境變量,以下

* * * * * source /$HOME/.bash_profile && command

固然,對於某特定環境變量或有特定的處理方式,如PATH,命令使用絕對路徑亦可解決。

特殊符號%

%在crontab是特殊符號,具體含義以下:

第一個%表示標準輸入的開始

* * * * * cat >> /tmp/cat.txt 2>&1 % stdin input

執行成功以後,查看/tmp/cat.txt

$ cat /tmp/cat.txt
stdin input

咱們看到標準輸入寫入到了/tmp/cat.txt文件。

<font color=red>理解上面示例,首先需知cat >> /tmp/cat.txt ,做用是將標準輸入重定向至/tmp/cat.txt。</font>

其他%表示換行符

示例以下

* * * * * cat >> /tmp/cat_line.txt 2>&1 % stdin input 1 % stdin input 2 % stdin input 3

查看輸出

$ cat /tmp/cat_line.txt
stdin input 1
stdin input 2
stdin input 3

有三行輸出

解決方式

既然是特殊字符,天然而然就想到了使用進行轉義,以下:

* * * * * cat >> /tmp/cat_special.txt 2>&1 % per cent is \%. 2>&1

查看輸出

$ cat /tmp/cat_special.txt
per cent is %.

執行成功了。自此,你就順利爬出了%特殊字符問題的坑。

關於這個問題的具體說明,能夠參看附錄中的《Crontab and %》。

關於輸出重定向

當咱們不作輸出重定向時,如任務有大量輸出,或許有些沒法解釋的問題。

輸出寫入郵件

crontab任務輸出默認寫入到執行用戶的郵件中,以下演示:

* * * * * date

命令輸出當前日期,下面查看當前用戶的郵件

$ cat /var/spool/mail/$USER
...

Sat Dec 31 17:45:01 CST 2016

因而可知,任務輸出的日期信息寫入到了用戶郵件中。

如任務有大量輸出,會佔用磁盤資源。但編者測試顯示,如磁盤容量不足,任務也會執行,但輸出不會寫入郵件;

關閉郵件功能

如何關閉?設置MAILTO環境變量爲空。以下

MAILTO=""
* * * * * date

是否是關閉郵件寫入就行了?附錄《Linux中的crontab與sendmail》博文代表,關閉mail功能,輸出內容將寫入到/var/spool/clientmqueue中,可能佔滿分區的inode資源,致使任務沒法執行。inode資源使用狀況可經過以下命令獲取

$ df -i
Filesystem Inodes   IUsed  IFree    IUse% Mounted on
/dev/sda1  512000   378    511622   1%    /boot
/dev/sda2  92672000 185351 92486649 1%    /

抱歉!這種狀況編者並未測出!但在公司的生產環境發現過未重定向則任務不執行的狀況,加上後解決了問題。百度也搜索到了相似問題,若有朋友瞭解,歡迎指教,萬分感謝。

固然,爲了不此類問題發生,建議任務都加上輸出重定向,以下

* * * * * date >> /dev/null/ 2>&1

輸出到/dev/null中,標準輸入和標準錯誤都應處理。

如你們對重定向有疑惑,可參見附錄中的《Linux重定向》,對文解釋不錯。

<font color=red>程序員的感悟:在技術的世界,當咱們不按常理作事,事情也不會按常理犯錯。</font>

調試大招

最後的福利,編者根據本身的總結而梳理出一套快速定位crontab錯誤的思路。兩個角度:

  • 任務是否執行
  • 命令是否正確

任務是否執行?

調試思路

首先,經過日誌確認任務是否執行
而後,如未執行則分析定時語句,
最後,定時沒有問題,檢查crond服務是否開啓

下面說明具體分析步驟。

日誌確認

調試錯誤,日誌一般是個利器,crontab也有日誌。
編者的服務器中crontab日誌文件位置爲/var/log/cron

查看日誌
日誌中包含任務執行記錄,配置錯誤提示,任務配置編輯重載記錄,服務開啓等記錄。

下面是日誌的部份內容,

$ vim /var/log/cron
...
Dec 31 19:17:01 localhost crond[1455]: (CRON) bad day-of-week (/var/spool/cron/root)
Dec 31 19:17:01 localhost CROND[4409]: (root) CMD (date)
...

這裏截取了對調試比較重要的兩條記錄,以下介紹

執行記錄

Dec 31 19:17:01 localhost CROND[4409]: (root) CMD (date)

顯示12月21 19時17分1秒執行了date命令

配置錯誤

Dec 31 19:17:01 localhost crond[1455]: (CRON) bad day-of-week (/var/spool/cron/root)

上面顯示/var/spool/cron/root的任務配置有錯,也就是root任務配置有錯。錯誤緣由:bad day-of-week,星期配置有錯。

語句是這樣的

* * * * date >> /dev/null 2>&1

明顯缺乏了星期時間段。

確認定時語句

經過上面的日誌分析,如任務沒有執行,使用定時語句在線分析工具分析定時是否正確,很是簡單。

確認服務開啓
若是定時語句也正確,檢查服務是否開啓。檢測命令以下

Systemd方式(centos7及以上)

$ systemctl status crond.service

SysVinit方式(centos7如下)

$ service crond status

查看命令輸出,如未開啓,執行以下命令開啓

Systemd方式(centos7及以上)

$ systemctl start crond.service

SysVinit方式(centos7如下)

$ service crond start

確認任務成功後,如問題仍未解決,繼續往下看。

命令是否正確

確認命令成功與否,這裏總結步驟大體以下

獲取命令執行輸出

crontab中的命令執行出錯,多數人都不知道如何調試。咱們知道在控制檯執行命令時,可經過輸出獲取錯誤信息調試問題。這種方式在crontab一樣適用,方法就是利用從新向獲取輸出,進行分析。示例以下

* * * * * php /root/index.php >> /tmp/debug.log 2>&1

這條任務老是執行失敗,咱們把輸出重定向到/tmp/debug.log。

查看debug.log,以下

$ cat /tmp/debug.log
/bin/sh: php: command not found
/bin/sh: php: command not found

顯示php命令沒有找到,很明顯的就能夠肯定是環境變量的問題。這種方式定位問題很是有效。

具體問題具體分析

有了命令執行的輸出,下面就是具體問題具體分析了。或許是前面提到的各類坑,也或許是命令自己所獨有的問題。

調試的方法到這裏就說完了。但仍是實踐爲王,需持續總結,同時也但願你們不要在一樣的坑中重複犯錯。

crontab寫了這麼長,但願能切實幫到你們。有哪位朋友看到了最後嗎?表示佩服!

參考附錄

讓你學會Linux計劃任務
Linux中的crontab與sendmail
Crontab and %
Linux重定向

相關文章
相關標籤/搜索