關於本系列
通常,UNIX® 管理員都擁有一套他/她經常使用協助管理進程的實用程序、技 巧和系統。提供了各種用於簡化不同過程的關鍵實用工具、命令行鏈和腳本。這些工具中有一部分來自於 操作系統,而大部分的技巧則來源於長期的經驗和簡化系統管理員工作的要求。本系列文章主要專注於最 大限度地利用各種 UNIX 環境中可用的工具,包括簡化異類環境中的管理任務的方法。
及時管理 中的計劃
典型的系統管理員會花很多時間進行重復的任務。至少在沒有可在合適的時間點運行各 種任務的任務計劃系統的情況下,他們會這樣做。
典型的例子包括運行備份之類的日常任務,也 包括要每周或每個月執行的各種任務,如清理日值、生成報告以及在各種情況下要執行的大量其他命令。
另外還有其他希望以特定時間間隔運行的任務,例如監視當前運行的進程或當前磁盤使用量列表 的命令,所有此類任務都可以用於幫助在出現故障或其他問題時診斷和確定問題。或者,可能存在希望在 自己不在場的特定時間執行的命令。例如,您可能會希望在夜間關閉為第二天工作做准備的計算機,但您 可能不希望僅為了關閉計算機而一直等到午夜。
有可用於所有這些情況的解決方案,但在了解如 何執行這些操作前,應該注意到計劃方法的一些缺點和不足。
由於所有任務都已自動化,計劃執 行的一個主要問題是,如果發生了任何錯誤或意外的情況,並沒有機會對出現的問題加以解決。另外,您 還完全依賴於系統及其在特定時間執行特定任務的功能。在命令何時執行方面有一些限制,而處理 “如果發生了X,則執行 Y,否則執行 Z”之類的不可預測的情況要求進行腳本編寫,並需要 進行很多次試驗,可能遇到很多錯誤。
不過,計劃可以節約大量的時間,因此這些選項值得考慮 。
計劃一次性執行的任務
cron系統可處理所有基於時間的命令執行計劃,並提供了可用於 在特定時間運行命令的兩個不同解決方案。at命令可計劃在特定的時間執行某項任務,且僅執行一次。 crontab系統允許指定命令的執行計劃,可以在指定的時間運行,可以在特定某天運行,還可以將二者結 合使用。
可采用兩種方法使用at命令。第一個是直接鍵入at和希望命令運行的時間(可以選擇指 定日期)。例如:
$ at 17:20
echo It's 17:20!
job 1 at Tue Apr 11 17:20:00 2006
輸入了at命令後,它將等待您輸入要在指定時間運行的命令。可以輸入任 意多的命令,這些命令將作為 Shellscript 執行。要終止輸入,請使用end-of-file命令(通常為 Control-D)。
您所鍵入的命令將在調用at命令的環境副本中執行。這意味著,將記錄您的活動 PATH、庫和其他環境設置,並用於執行您生成的腳本。通常會在命令完成時將結果通過電子郵件發送給您 。
指定時間時,可以使用標准時間格式(如前面的示例中所示),也可以使用其他各種縮寫技術 。如果指定了時間,則將使用該時間的下一個匹配項。例如,如果當前時間是 17:00,而您指定的時間是 17:20,命令將在20 分鐘後執行。如果指定 09:00,則命令將在第二天的上午 9 點執行。
通常可 以使用以下特殊的替換項:
midnight——12:00 a.m./00:00
noon——12:00 p.m.
now——立即執行
還可以使 用today和 tomorrow。一些環境(BSD和 Linux®)還可能支持通過加上表示分、時、天、周、月和年 的數值來增加指定值的時間的選項。例如,可以使用以下命令來指定某個作業在一周後的當前時間運行:
$ at now + 1 week
如果您希望重新計劃作業在上次執行之後的一段時間執 行,則可以使用這種指定方式。例如,您可能在運行一個運行時間長達數小時的報告,但又希望在一周後 再次運行次報告。
檢查計劃作業
要獲取當前的計劃作業列表,請使用-l命令行選項:
$ at -l
1 Tue Apr 11 17:20:00 2006
2 Wed Apr 12 09:00:00 2006
輸出中的數字是作業 ID。不過,不可能從標准命令的這個列表中確定每個作業將進 行的操作。
刪除計劃作業
通過使用-r 選項,並指定作業首次提交時生成的(或對計劃程 序中的條目進行列表操作時顯示的作業列表列出的)作業號,可以刪除計劃作業。例如,要刪除上面示例 中將在4 月 12 日星期三 09:00 執行的作業,可以使用以下命令:
$ at -r 2
請注意,大部分系統將不會提供作業已從隊列中刪除的可視指示,因此可能需要再次對 作業進行列表操作,以確定已取消了相應的作業:
$ at -l
1 Tue Apr 11 17:20:00 2006
計劃定期執行的任務
為了定期執行,要設置一個 cron 表(稱為 crontab),以定義每個命令執行的間隔和順序。該文件的格式是這樣的,每個命令占單獨的一行,每行 包括六個字段,如下所示:
minute hour day month dayofweek command
應 根據以下規則使用數字進行時間指定:
Minute:0-59
Hour:0-23
Day:1- 31
Month:1-12
Day:0-6(其中 0 是星期日)
對於任何字段,都可以指定單個數 字、用逗號分隔的數字列表或星號(指示應匹配任何值)。
通過指定時間,只要匹配當前時間, 就會執行命令。例如,通過指定以下時間:0 * * * * do-something,命令將在當前時間的分鐘值為 0 時(例如整點時)執行。
而指定以下時間:0 23 * * * do-something,命令將在每晚 11 點時運 行。
如果指定多個值,則會對每個值進行匹配。例如,要每 15 分鐘執行一次命令,請使用以下 語句:
0,15,30,45 * * * * do-something
或者,可以通過使用以下命令指 定命令從星期一到星期五每六個小時運行一次:
0 0,6,12,18 * * 1,2,3,4,5 do- something
可以在crontab 中包含任意多的行,如果希望,可以多次引用相同的命令,而 這在其他情況下則相當難於實現。例如,一個命令要在星期一到星期四的下午 6 點運行,但在星期五時 要在午餐時間運行,則可以使用以下兩行語句:
0 18 * * 1,2,3,4 do-something
0 12 * * 5 do-something
對於前兩個選項(分和時)應該小心處理;如果未指定這兩個選 項(使用星號),將導致在匹配其他指定值的情況下,每分鐘運行一次。例如,一個常見的錯誤是,希望 在月初運行命令,但卻使用了以下語句:
* * 1 * * do-something
這裡的 問題是,上述指定語句實際上會在每個月的第一天每分鐘運行該命令一次。如果希望命令只運行一次,必 須指定應執行命令的分和時的值:
0 12 1 * * do-something
省略分鐘值, 將至少讓命令在匹配指定的時(和日期)的情況下每分鐘運行命令一次。
雖然有這麼大的靈活性 ,但仍然會出現很難(甚至不可能)使用crontab系統執行命令的情況。
處理手動計劃
cron 的問題在於,盡管 crontab 中可用的所有不同選項提供了各種不同的可能性,但仍然有一 些惱人的局限。
每月的最後一天
例如,要在cron 中每月最後一天運行命令很困難,因為 沒有辦法直接指定此信息。相反,您必須單獨指定月份和其對應的最後一天。例如,在不是閏年的年份, 可以結合使用以下三行語句:
59 23 31 1,3,5,7,8,10,12 * do-something
59 23 30 4,6,9,11 * do-something
59 23 28 2 * do-something
在 上面的示例中,通過手動的方式選擇了每個月的最後一天,但管理這三行可能有些麻煩,必須在閏年手動 修改 crontab 定義,以確保該信息能計算出正確的日期。
相應的解決辦法是使用echo命令(而不 是 cron)執行日期檢查。要實現這一點,需要使用cal(用於輸出當前月份的日歷)和 awk(用於確定該 月的最後一天)。如果運行以下命令,應該可獲得月份的最後一天:
$ echo `cal`|awk '{print $NF}'
以上命令首先通過 echo命令(會將通常的多行輸出作為一行輸出 )輸出日歷,然後對輸出的數字進行計數;最後的數字就是當前月份的最後一天。
要在crontab 中使用此命令,應采用以下方式:
59 23 * * * [`echo \`cal\`|awk '{print $NF}'` -eq `date +\%d`]
&& do-something
方括號將在用於運行命令的外殼程序中啟動一個測試。另外要注意,cron 將篩選出 % 符號,因此在 crontab 中使用時必須進行轉義。該測試的第一部分就是前面演示的內容,其第二部分使用date命令來輸 出當前日期。重復的 && 可確保 && 右側的命令僅在左側的測試結果為真時執行。
給定周中的特定天
另一個常見的需求是,僅在每月中的特定星期數運行。例如,可能希望 在每個月的第一個星期一或星期五運行一個報告。為了完成此任務,可以使用與上面類似的過程。對於給 定周中的任何天,它一定屬於以下日期范圍之一:
第 1 周:第 1 天到第 7 天
第 2 周: 第 8 天到第 14 天
第 3 周:第 15 天到第 21 天
第 4 周:第 22 天到 28 天
要 確定當前日期是否在給定范圍內,例如是否在第四周范圍內,可以使用與以下所示類似的測試:
[ `date +\%e` -gt 21 -a `date +\%e` -lt 29 ]
%e 用於返回當天的號數,如果數字小於 10 則用一個空格(而不是零)作為其前綴,以確保對數字( 而非字符串)進行比較。
現在可以將此與 crontab 定義一起使用,以嘗試每周星期五運行命令:
59 23 * * 5 [ `date +\%e` -gt 21 -a `date +\%e` -lt 29 ]
&& do- something
命令將在每周星期五運行,但由於測試將僅在每個月的第四周返回 True,命令將實際在第三個星期五 執行。
Cron作業執行環境
盡管可以更改執行 cron作業時使用的環境,但經常最好創建一 個包裝腳本,以在運行實際需要的命令前定義任何環境變量(如 PATH)。
這樣做的部分原因是出 於安全考慮;向cron作業開放的區域越多,越可能得到包含可疑內容的東西。另一個原因是,這樣可確保 即使更改了環境中的一個依賴關系,您的 cron作業仍然將執行。
通過使用獨立的包裝腳本,還可 以利用不同外殼程序的擴展和功能,而不僅限於通常用於運行大部分 cron作業的標准 Bourne 外殼程序 。
最後,通過使用獨立的包裝腳本,還允許您為不同命令定義不同的環境。如果您希望在可能使 用相同應用程序或工具的不同版本的不同用戶環境中運行命令,這將非常有用。
記錄輸出的技巧
缺省情況下,crontab 運行的生成輸出(到標准輸出和標准錯誤的輸出)的命令都會將輸出以電 子郵件的形式發送給該作業的用戶。不過,這並非總是方便的解決方案,對於某些結果,您可能只需要部 分輸出,或者可能希望忽略標准輸出,而僅報告錯誤。甚至可能希望將輸出發送到不同的用戶或電子郵件 別名。
可以在crontab 指定語句中使用重定向來將輸出信息發送到特定文件或忽略來自不同源的 輸出。要直接將輸出記錄到文件中,可以使用以下語句:
1 1 * * * do-something >/var/logs/do-something.log
上述語句會覆蓋信息,因此,如果希望保持較長時間的 記錄,請使用追加:
1 1 * * * do-something >>/var/logs/do- something.log
要忽略輸出,請重定向到特殊的 /dev/null 設備。對於標准輸出,請嘗試 使用以下語句:
1 1 * * * do-something >/dev/null
對於標准輸出和 錯誤,請嘗試使用以下語句:
1 1 * * * do-something >/dev/null 2>&1
如果希望收集按照日期組織的日志,則請將 date命令和指定日志文件的語句 結合使用,例如:
1 1 * * * do-something >/var/logs/something.`date +\%Y\%m\% d`.log
要從 cronjob 中的一系列命令拾取和選擇輸出,或創建基於內容的自定義電子郵件,請使用包裝腳本 ,以將您希望保存的信息寫入到臨時文件中,並同時忽略其他輸出。可以隨後將該文件的內容以電子郵件 的方式發送給任何希望的用戶。
要創建臨時文件,請使用時間和進程 ID 生成唯一的文件名,如 下所示:
LOGFILE=/tmp/`datetime +%Y%m%d`.$$.tempfile
do-something >$LOGFILE 2>&1
mailx -s "Results of do-something report" reportees <$LOGFILE
rm -f $LOGFILE
將文件發送到相關人員後,請記住刪除該文件。在上 面的示例中,使用了mailx(而不是 mail)來允許設置主題。
結束語
通過將 crontab和 at命令結合使用,可以指定任何所需的命令的執行時間或間隔。使用at 時,可以在給定時間僅運行一次 命令或腳本。通過使用crontab,可以指定執行的時間間隔,這個間隔可以隨意指定,可以間隔很長,也 可以間隔很短。但應仔細處理,以確保命令在所需的准確時間運行。忽略分和時可能帶來問題,或者可能 導致您的命令在預期外的時間或時間間隔運行。
在crontab 不夠具體或靈活的時候,可以使用其 他一些替代方法,以處理更為復雜的情況,例如在每月的最後一天或特定周的特定天運行命令。
進行計劃可節省時間,在細心組織下,還能幫助減少您的工作負擔,減少重復工作。