寫出不鬧意外的 Cron 表達式
五個欄位及其範圍、名稱與數字、日期欄位的「或」陷阱、週日是 0 還是 7、日光節約的空缺與重疊,以及部署前用未來執行預覽來驗證。
五個欄位,從左向右讀
經典 Cron 表達式是五個以空格分隔的欄位:分鐘(0–59)、小時(0–23)、日(1–31)、月(1–12)、星期(0–7)。每個欄位接受單一值、逗號分隔的清單、連字號範圍、斜線步進,或表示所有值的星號。
大多數 Cron 缺陷並非罕見語法,而是普通欄位被粗心地讀錯:例如在範圍邊界重置的步進,或在按 23 小時計數的系統裡按十二小時制寫下的小時欄位。
- 清單清單挑選離散值:分鐘欄位裡的 0,30 表示整點和半點各觸發一次。
- 範圍與步進範圍挑選一段區間:小時欄位裡的 9-17 涵蓋工作時間,步進可以把區間抽稀——9-17/2 表示區間內每兩小時一次。
- 星號匹配全部星號不是「忽略此欄位」;它主動匹配所有值,一旦兩個日期欄位相互作用,這一點就變得關鍵。
名稱是附文件說明的數字
月份與星期欄位接受三字母英文名稱——JAN 到 DEC、SUN 到 SAT——作為數字的別名。0 9 * JAN MON 與 0 9 * 1 1 是同一個排程,但名稱版能向下一位讀者自我解釋,也更抗「差一」式修改。
- 不區分大小寫名稱解析不區分大小寫——mon、Mon、MON 都可以——但大寫是顯得有意的慣例。
- 範圍裡的名稱名稱也能用在範圍與清單裡:MON-FRI 是標準的工作日區間,JAN,APR,JUL,OCT 選出季度月份。
- 讀解釋Cron 建立與說明工具會正規化表達式並逐欄位解釋——「month: every value」「weekday: 1, 2, 3, 4, 5」——讓你確認解析器按你的本意讀懂了名稱。
兩個日期字段如何配合
此工具採用 Vixie 風格的日期規則:當“日”和“星期”字段都不以 * 開頭時,任一字段匹配即可觸發。0 9 13 * FRI 不是“13 號星期五”,而是每月 13 號以及每個星期五。
說明工具的輸出對此很明確:它把語意標註為 dom-dow-or 並連同計算出的執行一起展示,於是一個會比預期觸發頻繁得多的表達式,會在進入 crontab 之前就露出馬腳。
- 通配步進仍有限制日期字段若以 * 開頭(包括 */2 這樣的步進),此工具要求兩個日期字段同時匹配;步進約束依然有效。
- 都不以星號開頭則為“或”當兩個日期字段都不以 * 開頭時,此工具採用“或”規則:日期或星期任一個匹配即可。
- 「與」需要變通要真正命中「13 號星期五」,讓 cron 選中 13 號,再讓命令本身檢查星期——或者換一個有「與」語意的排程器,經典 cron 不是。
週日是 0,通常也可以是 7
星期欄位把週日編號為 0,週一為 1,依此到週六為 6。多數實作還接受 7 作為第二個週日,照顧從週一開始數的人——Toolars 解析器內部會把 7 正規化為 0——但有些系統只接受其一,別處還存在以週一為 1 的 ISO 式編號。
- 0 與 7在接受 7 的地方,0 和 7 都是週日;寫 0 是跨排程器的可攜選擇。
- 悄悄的差一在拒絕 7 的系統上寫 7 會在部署時響亮地失敗;但把星期誤讀為「從週一起數的第七天」則會在錯誤的那一天悄悄失敗。
- 逐平台再確認當表達式在實作之間遷移——系統 crontab、容器平台、CI 排程器——請重新確認星期編號,而不是假設它安全抵達。
日光節約會扭彎排程
夏令時切換會產生不存在或重復的本機分鐘。此預覽會跳過春季撥快時不存在的 02:30,並在秋季撥回時列出兩次符合條件的分鐘。實際部署的排程器可能補跑或壓掉重復執行,應核對它自己的規則。
- 消失的一小時春季空檔:預覽中沒有對應的 02:30 分鐘;有些排程器會在切換後補跑漏掉的任務。
- 翻倍的一小時秋季重疊:預覽可能列出兩個符合條件的時刻,而實際排程器可能抑制第二次執行。
- UTC 沒有日光節約乾淨的逃生口是 UTC:它沒有日光節約切換,30 2 * * * 在一年裡的每一天都是同一時刻。說明工具提供 UTC、本機時區,以及 America/New_York、Europe/London 等具名時區。
用未來執行驗證,不靠運氣
正確的表達式與看起來像樣的表達式之間,差著一份具體的觸發時刻清單。Cron 建立與說明工具從你選擇的時區出發計算未來十次執行,最遠搜尋 366 天,並在背景工作執行緒中求值,再重的表達式也不會卡住分頁。
- 讀執行清單對著意圖讀頭幾次執行:「工作日 09:00」應該列出週一到週五的九點;清單裡出現一個週六,就是「或」陷阱在自報家門。
- 跨一次切換選擇命名時區;若下一次夏令時切換落在 366 天預覽範圍內,檢查切換前後的執行時刻。工作區從目前時間起算,不能指定自訂起始日期。
- 明確的錯誤無效表達式會顯示錯誤。366 天內沒有匹配項會得到搜尋上限錯誤;找到 1–9 個匹配項時會傳回部分結果,而非空列表。
常見問題
為什麼 cron 在我沒料到的日子執行了?
cron 裡星期天是 0 還是 7?
如何讓排程不受夏令時間影響?
排程與正規表示式共享同一套紀律。
兩者都是破壞半徑遠超體量的小字串——學習正規表示式的編寫—測試—重構循環,在帶逾時的本地工作執行緒中執行。