天天看點

Linux中定時任務

目錄

  • 一.簡介
  • 二.crondtab file
  • 三.crond指令的調試
  • 四.精确到秒的任務計劃

定時任務線上測試網站

定時任務基本概念:

(1).crond是一個daemon類程式,路徑為/usr/sbin/crond。預設會以背景方式啟動,service或systemd方式啟動crond預設也是背景方式的。

(2).crondtab是管理crontab file的工具,而crontab file是定義定時任務條目的檔案。

(3).crontab file存在于多處,包括系統定時任務檔案/etc/crontab和/etc/cron.d/*,還有獨屬于各使用者的任務檔案/var/spool/cron/USERNAME。

crontab指令:

-l:列出定時任務條目
-r:删除目前任務清單終端所有任務條目
-i:删除條目時提示是否真的要删除
-e:編輯定時任務檔案,實際上編輯的是/var/spool/cron/*檔案
-u:操作指定使用者的定時任務
           

執行crontab -e指令編輯目前使用者的crontab file,例如目前為root使用者,則編輯的是/var/spool/cron/root檔案。例如寫入下面這一行。

* * * * * /bin/echo "the first cron entry"  >>/tmp/crond.txt
           

這将會每分鐘執行一次echo指令,将内容追加到/tmp/crond.txt檔案中。

任務計劃中的任務條目如何定義,可以檢視/etc/crontab檔案。

[root@server2 ~]# cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
 
# For details see man 4 crontabs
 
# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed
           

在此檔案中定義了3個變量,其中一個是PATH,該變量極其重要。在最後還給出了任務條目的定義方式:

(1).每個任務條目分為6段,每段以空格分隔,之是以此處多了user-name段是因為/etc/crontab為系統定時任務檔案,而一般定時任務是沒有該段的。

(2).前五段為時間的設定段,分别表示"分時日月周",它們的定義不能超出合理值範圍,第六段為所要執行的指令或腳本任務段。

(3).在時間定義段中,使用""表示每機關,即每分鐘,每小時,每天,每月,每周幾(仍然是每天)。實際上,按man文檔中解釋,""表示的是從每個時間段的起始到結尾,也就是全部時間機關的意思。例如在小時上設定*,表示0,1,2,3...22,23的意思。

(4).每個時間段中,都可以使用逗号","來表示枚舉,例如定義"0,30,50 * * * *"表示每個時辰的整點、第30分鐘和第50分鐘都執行該任務。

(5).每個時間段中,都可以使用"-"定義範圍,可以結合逗号使用。如分鐘段定義了"00,20-30,50"表示每個時辰的整點、第20到30分鐘的每分鐘、第50分鐘都執行該任務。

(6).每個時間段中,使用"/"表示忽略時間,如在小時段定義了"0-13/2"表示在"0/2/4/6/8/10/12"點才滿足時間定義。常使用"*/N"表示每隔多久的意思。例如"00 */2 * * *"表示在每天每隔兩小時的整點執行該任務(嚴格地說是0-23/2,也就是0,2,4,...,22,是以淩晨1點不會執行任務)。

(7).如果定義的日和周沖突了,則會多次執行(不包括因為*号導緻的沖突)。例如每月的15号執行該任務,同時又定義了周三執行該任務,正常無沖突情況下,将在周三和每月15号執行,但如果某月的15号同時是周三,則該任務在此日執行兩次。是以,應該盡力避免同時定義周和日的任務。

(8).指令段(即第6段)中,不能随意出現百分号"%",因為它表示換行的特殊意義,且第一個%後的所有字元串将當作指令的标準輸入。

例如下面的定義:

* * * * * /bin/cat >>/tmp/crond.txt %"the first %%cron entry%"
           

該任務輸出的結果将是:

"the first

cron entry
"
           

是以,在定時任務條目中若以時間定義檔案名時,應當将%使用反斜杠轉義。如:

* * * * * cp /etc/fstab /tmp/`date +\%Y-\%m-\%d`.txt
           

另外一個需要注意的時間段設定是,使用号問題。例如" */2 * * ",它表示每隔兩小時後的每一分鐘都執行任務,也就是淩晨0點的每分鐘執行任務,淩晨1點不執行任務,淩晨2點的每分鐘執行任務,淩晨4點的每分鐘執行任務,依此類推。同理,"/5 */2 * * *"表示每隔2小時後的每5分鐘執行一次任務。

crondtab file為任務定義檔案。

(1).在此檔案中,空行會被忽略,首個非空白字元且以#開頭的行為注釋行,但#不能出現在行中。

(2).可以在crontab file中設定環境變量,方式為"name=value",等号兩邊的空格可随意,即"name = value"也是允許的。但value中出現的空格必須使用引号包圍。

(3). 預設crond指令啟動的時候會初始化所有變量,除了某幾個變量會被crond daemon自動設定好,其他所有變量都被設定為空值。自動設定的變量包括SHELL=/bin/sh,以及HOME和LOGNAME(在CentOS上則稱為USER),後兩者将被預設設定為/etc/passwd中指定的值。其中SHELL和HOME可以被crontab file中自定義的變量覆寫,但LOGNAME不允許覆寫。當然,自行定義的變量也會被加載到記憶體。

(4).除了LOGNAME/HOME/SHELL變量之外,如果設定了發送郵件,則crond還會尋找MAILTO變量。如果設定了MAILTO,則郵件将發送給此變量指定的位址,如果MAILTO定義的值為空(MAILTO=""),将不發送郵件,其他所有情況郵件都會發送給crontab file的所有者。

(5).在系統定時任務檔案/etc/crontab中,預設已定義PATH環境變量和SHELL環境變量,其中PATH=/sbin:/bin:/usr/sbin:/usr/bin。

(6).crond daemon每分鐘檢測一次crontab file看是否有任務計劃條目需要執行。

很多時候寫了定時任務卻發現沒有執行,或者執行失敗,但因為crond是背景運作的,有沒有任何提示,很難進行排錯。但是可以讓crond運作在前端并進行調試的。

先說明下任務計劃程式crond的預設執行方式。

使用下面三條指令啟動的crond都是在背景運作的,且都不依賴于終端。

[root@xuexi ~]# systemctl start crond.service
[root@xuexi ~]# service crond start
[root@xuexi ~]# crond
           

但crond是允許接受選項的。

crond [-n] [-P] [-x flags]
選項說明:
-n:讓crond以前端方式運作,即不依賴于終端。
-P:不重設環境變量PATH,而是從父程序中繼承。
-x:設定調試項,flags是調試方式,比較有用的方式是test和sch,即"-x test"和"-x sch"。
  :其中test調試将不會真正的執行,sch調試顯示排程資訊,可以看到等待時間。具體的見下面的示例。
           

先看看啟動腳本啟動crond的方式。

[root@server2 ~]# cat /lib/systemd/system/crond.service
[Unit]
Description=Command Scheduler
After=auditd.service systemd-user-sessions.service time-sync.target
 
[Service]
EnvironmentFile=/etc/sysconfig/crond
ExecStart=/usr/sbin/crond -n $CRONDARGS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

[Install]
WantedBy=multi-user.target
           

它的環境配置檔案為/etc/sysconfig/crond,該檔案中什麼也沒設定。

[root@server2 ~]# cat /etc/sysconfig/crond
# Settings for the CRON daemon.
# CRONDARGS= :  any extra command-line startup arguments for crond
CRONDARGS=
           

所有它的啟動指令為:/usr/sbin/crond -n。但盡管此處加了"-n"選項,crond也不會前端運作,且不會依賴于終端,這是systemctl決定的。

再解釋下如何進行調試。以下面的任務條目為例。

[root@server2 ~]# crontab -e
* * * * * echo "hello world" >>/tmp/hello.txt
           

執行crond并帶上調試選項test。

[root@server2 ~]# crond -x test
debug flags enabled: test
[4903] cron started
log_it: (CRON 4903) INFO (RANDOM_DELAY will be scaled with factor 8% if used.)
log_it: (CRON 4903) INFO (running with inotify support)
log_it: (CRON 4903) INFO (@reboot jobs will be run at computer's startup.)
log_it: (root 4905) CMD (echo "hello world" >>/tmp/hello.txt )
           

執行crond并帶上調試選項sch。

[root@server2 ~]# crond -x sch
debug flags enabled: sch
[4829] cron started
log_it: (CRON 4829) INFO (RANDOM_DELAY will be scaled with factor 73% if used.)
log_it: (CRON 4829) INFO (running with inotify support)
[4829] GMToff=28800
log_it: (CRON 4829) INFO (@reboot jobs will be run at computer's startup.)
[4829] Target time=1497950880, sec-to-wait=38      # 等待crond daemon下一次的檢測,是以表示38秒後crond将檢測crontab file
user [root:0:0:...] cmd="echo "hello world" >>/tmp/hello.txt "
[4829] Target time=1497950940, sec-to-wait=60
Minute-ly job. Recording time 1497922081
log_it: (root 4831) CMD (echo "hello world" >>/tmp/hello.txt )
user [root:0:0:...] cmd="echo "hello world" >>/tmp/hello.txt "
[4829] Target time=1497951000, sec-to-wait=60
Minute-ly job. Recording time 1497922141
log_it: (root 4833) CMD (echo "hello world" >>/tmp/hello.txt )
           

但要注意,在sch調試結果中的等待時間是crond這個daemon的檢測時間,是以它表示等待下一次檢測的時間,是以除了第一次,之後每次都是60秒,因為預設crond是每分鐘檢測一次crontab file的。例如,下面是某次的等待結果,在這幾次等待檢測過程中沒有執行任何任務。

[4937] Target time=1497951720, sec-to-wait=18
[4937] Target time=1497951780, sec-to-wait=60
[4937] Target time=1497951840, sec-to-wait=60
           

還可以同時帶多個調試方式,如:

[root@server2 ~]# crond -x test,sch
debug flags enabled: sch test
[4914] cron started
log_it: (CRON 4914) INFO (RANDOM_DELAY will be scaled with factor 21% if used.)
log_it: (CRON 4914) INFO (running with inotify support)
[4914] GMToff=28800
log_it: (CRON 4914) INFO (@reboot jobs will be run at computer's startup.)
[4914] Target time=1497951540, sec-to-wait=9
user [root:0:0:...] cmd="echo "hello world" >>/tmp/hello.txt "
[4914] Target time=1497951600, sec-to-wait=60
Minute-ly job. Recording time 1497922741
log_it: (root 4916) CMD (echo "hello world" >>/tmp/hello.txt )
           

這樣在調試定時任務時間時,也不會真正執行指令。

預設情況下,crond執行的任務隻能精确到分鐘,無法精确到秒。但通過技巧,也是能實作秒級任務的。

(1).方法一:不太精确的方法

寫一個腳本,在腳本中sleep3秒鐘的時間,這樣能實作每3秒執行一次指令。

[root@xuexi ~]# cat /tmp/a.sh
#!/bin/bash
#
PATH="$PATH:/usr/local/bin:/usr/local/sbin"
for ((i=1;i<=20;i++));do
ls /tmp
sleep 3
done
           
[root@xuexi ~]# cat /var/spool/cron/lisi
* * * * * /bin/bash /tmp/a.sh
           

但是這樣的方法不是最佳方法,因為執行指令也需要時間,且crond預設會有一個随機延時,随機延時由變量RANDOM_DELAY定義。

(2).方法二:在cron配置檔案中寫入多條sleep指令和其他指令。

[root@xuexi ~]# cat /var/spool/cron/lisi
* * * * * ls /tmp
* * * * * sleep 3 && ls /tmp
* * * * * sleep 6 && ls /tmp
* * * * * sleep 9 && ls /tmp
* * * * * sleep 12 && ls /tmp
* * * * * sleep 15 && ls /tmp
* * * * * sleep 18 && ls /tmp
* * * * * sleep 21 && ls /tmp
* * * * * sleep 24 && ls /tmp
* * * * * sleep 27 && ls /tmp
* * * * * sleep 30 && ls /tmp
…
* * * * * sleep 57 && ls /tmp
           

這種方式很繁瑣,但是更精确。如果定義到每秒級别就得寫60行cron記錄。

由此能看出,秒級的任務本就不是crond所擅長的。實際上能用到秒級的任務也比較少。

本文版權歸作者所有,歡迎轉載,請務必添加原文連結。

繼續閱讀