
cron 表達式寫法:五個欄位、排程例子及時區陷阱
一行 * * * * *,五個欄位便能指定工作何時執行。但把同一行設定搬到另一個平台,未必會得到相同結果。本文從常見的五欄位格式入手,解說 POSIX、Cronie 及雲端排程服務的分別,並用具體例子說明日期、時區及執行環境如何影響排程。
日文原文發布: 2026-05-11
cron 是甚麼?每分鐘檢查一次的背景服務
cron 是 UNIX 類作業系統的時間排程服務,通常以背景常駐服務(daemon)運作。它每分鐘檢查排程表 crontab,符合條件時,就以子程序執行指定的指令。標準輸出與標準錯誤會依設定傳送電郵或寫入日誌。
即使使用容器、無伺服器服務或 CI/CD,仍會遇到 cron 的概念:
- Kubernetes CronJob:依排程建立 Job,再由 Job 執行 Pod。
- GitHub Actions:以
schedule事件定期觸發工作流程。 - AWS EventBridge Scheduler:由託管服務解讀 cron 表達式並呼叫目標。
- systemd timer:另一種排程方式,使用自己的
OnCalendar語法;轉換 cron 設定需要 systemd-cron 等額外工具。
沿用 cron 的概念,不代表語法與執行保證完全相同。移植排程時,仍須查看目標環境的規格。
先分清 cron 表達式與 crontab 的完整設定
POSIX 的 crontab 規格定義「分、時、日期、月份、星期」五個時間欄位,後面再接要執行的指令,不含秒欄位。Vixie Cron 系列的軟件另有環境變數設定及擴充記法;Cronie 文件描述的是該軟件的行為,不應套用到所有 UNIX 系統與雲端服務。
cron 表達式只是排程部分。用戶的 crontab 在五個時間欄位後面接指令;Cronie 的系統 crontab 則還要在時間欄位與指令之間指定執行帳戶。本文表格只列時間部分,含指令的例子則用於用戶 crontab。
# 用戶 crontab:五個時間欄位後面接指令
0 9 * * 1-5 /home/user/scripts/report.sh
請將例子中的路徑換成實際存在、且執行帳戶有權限存取的檔案。單獨貼上五個時間欄位,並不會形成可執行的工作。
五個欄位的範圍及符號寫法
以下包含 Cronie 系列的擴充語法。POSIX 的星期範圍是 0–6,基本語法不包含斜線步進值,也不包含月份與星期的英文名稱。
分 時 日期 月份 星期
0–59 0–23 1–31 1–12 0–7(Cronie)
* * * * *
如表格未能完整顯示,可左右捲動。
| 欄位 | 範圍 | 注意事項 |
|---|---|---|
| 分 | 0–59 | 一小時內的分鐘 |
| 時 | 0–23 | 24 小時制 |
| 日期 | 1–31 | 指一個月中的第幾天;不存在的日期不會符合此條件,但仍須注意與星期的 OR 組合 |
| 月份 | 1–12 | JAN–DEC 屬於部分軟件支援的擴充語法 |
| 星期 | 0–7(Cronie) | 0 與 7 都是星期日,1 是星期一,6 是星期六 |
如表格未能完整顯示,可左右捲動。
| 符號 | 意義 | 例子 |
|---|---|---|
* | 欄位內的所有值 | * * * * *:每分鐘 |
, | 列出多個值 | 日期欄位的 1,15:1 日與 15 日 |
- | 包含兩端的範圍 | 星期欄位的 1-5:星期一至星期五 |
/ | 在欄位範圍內依步進值取值 | 分鐘欄位的 */15:0、15、30、45 分 |
0 9 * * 1-5 表示星期一至星期五 09:00,不會排除公眾假期。30 4 * * 0 是每週日 04:30;0 */2 * * * 是設定時區的 00:00、02:00、04:00,依此類推。0 0 1,15 * * 則是每月 1 日與 15 日的午夜。
常用 cron 排程例子速查
下表適用於支援 Cronie 系列語法的五欄位排程器。用於用戶 crontab 時,還要接上指令;用於其他服務前,請先確認支援的語法。
如表格未能完整顯示,可左右捲動。
| 執行時間 | cron 表達式 |
|---|---|
| 每分鐘 | * * * * * |
| 每 5 分鐘 | */5 * * * * |
| 每 10 分鐘 | */10 * * * * |
| 每 15 分鐘 | */15 * * * * |
| 每小時的 0 分與 30 分 | 0,30 * * * * |
| 每小時整點 | 0 * * * * |
| 每天午夜 | 0 0 * * * |
| 每天 09:00 | 0 9 * * * |
| 每天 02:00 | 0 2 * * * |
| 星期一至星期五,09:00 至 18:00 的每個整點 | 0 9-18 * * 1-5 |
| 每週一 09:00 | 0 9 * * 1 |
| 每週日 03:00 | 0 3 * * 0 |
| 每月 1 日午夜 | 0 0 1 * * |
| 每月 1 日與 15 日 09:00 | 0 9 1,15 * * |
| 1、4、7、10 月的 1 日午夜 | 0 0 1 1,4,7,10 * |
| 每年 1 月 1 日午夜 | 0 0 1 1 * |
| 星期一至星期五中午 | 0 12 * * 1-5 |
每月 1 日與 15 日是每月兩次,不是每隔 14 天。星期一至星期五的排程仍包含假日;每天 02:00 的備份也需要確認時區與夏令時間的處理方式。
基本五欄位 cron 沒有「月底最後一天」的運算符號。0 0 28-31 * * 會在每月實際存在的 28 至 31 日都執行。可以在指令中判斷日期,或使用 EventBridge Scheduler 的 L 等有明確文件的擴充語法。
由五欄位到七欄位:簡寫及平台語法差異
Cronie 提供以下簡寫,用來取代時間欄位:
如表格未能完整顯示,可左右捲動。
| 簡寫 | 對應排程或行為 |
|---|---|
@yearly | 0 0 1 1 * |
@monthly | 0 0 1 * * |
@weekly | 0 0 * * 0 |
@daily | 0 0 * * * |
@hourly | 0 * * * * |
@reboot | 系統啟動後執行一次;Cronie 通常不會因為單純重新啟動 cron 服務而再次執行 |
Cronie 透過鎖定檔避免重複執行啟動工作。不要把 @reboot 當成服務重新啟動時的觸發機制;換用其他 cron 軟件時,也要確認啟動條件。
Spring 在最前面加入秒欄位;Quartz 有六個必要欄位,最後可選填年份。EventBridge Scheduler 也是六欄位,但增加的是年份,不是秒。
# Spring:秒 分 時 日期 月份 星期
0 0 9 * * MON-FRI
# Quartz:秒 分 時 日期 月份 星期 年份
0 0 9 ? * MON-FRI 2026
如表格未能完整顯示,可左右捲動。
| 環境 | 欄位數 | 秒/年份 | 需確認的擴充語法 |
|---|---|---|---|
| POSIX cron | 5 | 皆無 | 基本語法 |
| Cronie | 5 | 皆無 | 步進值、@ 簡寫 |
| GitHub Actions | 5 | 皆無 | 步進值;不支援 @ 簡寫 |
| Kubernetes CronJob | 5 | 皆無 | 步進值、@monthly 等簡寫;? 等同 * |
| Spring | 6 | 只有秒 | L、W、#、? |
| Quartz | 6–7 | 秒;年份可省略 | L、W、#、? |
| EventBridge Scheduler | 6 | 只有年份 | ?、L、W、# |
EventBridge Scheduler 的順序是「分、時、日期、月份、星期、年份」。其中 ? 表示不指定特定值,用在日期或星期其中一欄。星期日的編號也不同:POSIX 是 0,Cronie 接受 0 與 7,Quartz 與 EventBridge Scheduler 則是 1。移植時不能直接沿用星期編號。
日期及星期同時設限,為何會多執行幾次?
在 Vixie Cron/Cronie 系列的比對規則中,日期與星期兩欄若都不是以 * 開頭,只要任一欄符合就會執行。若其中一欄以 * 開頭,包括 */n,則要求兩欄條件都符合。
# 原本想要:只在 13 日且為星期五的 09:00 執行
0 9 13 * 5
# 實際:每月 13 日,或每個星期五的 09:00 都執行
以 2025 年 1 月為例,除了星期一的 13 日,也會在星期五的 3、10、17、24、31 日執行。1-31 與 * 在日期欄位單獨表示相同範圍,搭配限定的星期時,組合規則卻可能不同。應依所用軟件的規格判斷,不必推測設計者的意圖。
若要限定「13 日星期五」,可以排定每月 13 日,再由指令檢查星期。排程與指令必須使用相同時區:
# 用戶 crontab:當天是星期五才執行腳本
0 9 13 * * [ "$(date +\%u)" = "5" ] && /path/to/script.sh
date +%u 以 1 表示星期一、5 表示星期五。在 crontab 的指令欄位內,即使寫在引號裡,百分比符號也要以反斜線轉義。若移到獨立的 shell 腳本,則要移除這個 cron 專用的轉義字元。
時區及夏令時間:同一個鐘面時間未必只出現一次
傳統 cron 服務預設使用系統本地時區。若工作要求的時區與伺服器設定不同,執行時間就會偏移。
夏令時間(DST)會造成不存在或重複的當地時間。例如從 02:00 跳到 03:00,當天就沒有 02:30;從 03:00 撥回 02:00,則會出現兩次 02:30。各地的切換時刻不同,這只是時鐘變化的例子,不能直接推論所有排程器都會漏跑或跑兩次。
- Cronie:cron(8) 說明了少於三小時的時鐘變動補正,適用於固定時刻等符合條件的工作,包括向前調整時補執行、向後調整時避免重複。
- EventBridge Scheduler:春季跳過不存在的時間,秋季重複的時間只執行一次。
- GitHub Actions 的 timezone 設定:春季跳過的時段會順延至下一個有效時間。
以 CRON_TZ 指定個別時區時:Cronie 的原始碼亦記載了限制:工作所用時區切換夏令時間時,可能漏執行或執行兩次。不要把服務預設時區的補正行為,直接套用到另一個時區。請按所用軟件、版本及實際時區驗證;一律避開 02:00–03:00 也不是通用解法。
# 日本時間例子:排程與指令均採用東京時區
CRON_TZ=Asia/Tokyo
TZ=Asia/Tokyo
0 9 * * * /path/to/job.sh
# 另一種設定:排程與指令都使用 UTC
CRON_TZ=UTC
TZ=UTC
0 0 * * * /path/to/midnight-utc-job.sh
CRON_TZ 控制支援此變數的 cron 軟件如何解讀排程;TZ 則讓 date 等子程序使用同一時區。不是所有 Vixie Cron 衍生版本都支援 CRON_TZ。Kubernetes 應設定 .spec.timeZone,不要把時區變數寫進排程字串。
UTC 適合以 UTC 為基準的工作,可避開該排程的 DST 問題。但對採用 DST 的地區,固定 UTC 時刻無法全年維持相同的當地時間;若需求是當地每天早上 9 點,應使用對應的當地時區。
這裏保留原文的日本例子,使用日本標準時間 JST(UTC+9),日本目前沒有夏令時間。以 UTC 解讀時,0 0 * * * 對應日本 09:00。GitHub Actions 預設使用 UTC,也支援 IANA timezone 設定;最短排程間隔為 5 分鐘,負載高時可能延遲或省略執行。設定前請查閱官方規格。
終端能執行,cron 卻失敗:檢查環境及輸出
「終端裡能執行,cron 卻失敗」常與執行環境不同有關。cron 通常不會讀取互動式 shell 的啟動設定。Cronie 會設定 HOME、LOGNAME 等變數,並非完全沒有環境變數;PATH 視乎軟件及設定而定,預設指令 shell 通常是 /bin/sh。
# 容易出錯:依賴 PATH 與目前工作目錄
0 9 * * * python script.py
# 使用執行帳戶可存取的絕對路徑
0 9 * * * /usr/bin/python3 /home/user/scripts/script.py
也可以明確設定需要的環境:
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 9 * * * python3 /home/user/scripts/script.py
傳送電郵需要可用的郵件傳送設定。MAILTO 決定輸出內容傳送至哪個地址,不是單靠退出狀態碼判斷成功或失敗;工作失敗但沒有輸出時,可能不會收到通知。
# 選擇其中一種設定,套用到後續工作
MAILTO=""
# 或將工作輸出傳送至此地址
MAILTO=admin@example.com
空字串會停用輸出電郵。若依上述順序同時寫入兩行,後續工作會使用第二個值。
# 將標準輸出與標準錯誤附加到可寫入的日誌
0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
# 捨棄所有輸出,也會失去有用的診斷資訊
0 * * * * /path/to/job.sh > /dev/null 2>&1
確認日誌的寫入權限及輪替設定。依系統不同,可檢查 /var/log/syslog,或使用 journalctl -u cron;服務名稱也可能是 crond。crontab -l 可列出目前用戶的排程。日誌顯示工作已啟動,並不等於指令執行成功。
託管排程服務能代勞甚麼?
如表格未能完整顯示,可左右捲動。
| 服務 | 排程格式 | 常見用途與功能 |
|---|---|---|
| AWS EventBridge Scheduler | 六欄位,含年份 | 呼叫目標、重試、死信隊列(DLQ) |
| Google Cloud Scheduler | 五欄位 unix-cron | HTTP、Pub/Sub、App Engine |
| GitHub Actions schedule | 五欄位 | CI/CD 工作流程 |
| Kubernetes CronJob | 五欄位 | Job 與 Pod、並行執行控制 |
託管排程服務能減少維護排程伺服器的負擔,但無法消除執行目標、權限或網絡的故障。監察、警報、重試、並行限制、配額、費用與日誌保存期限,仍須逐項設定與確認。
Kubernetes 也明確說明,某些情況下 CronJob 可能建立兩個 Job,或沒有建立 Job。cron 表達式不保證「恰好成功一次」。工作應盡可能具有冪等性:重複執行不應破壞結果,也不應重複產生原本只應發生一次的外部效果。
單一伺服器的清理、日誌維護等小型工作,若希望減少外部依賴與設定成本,傳統 cron 仍可能是合適的選擇。
五種容易寫錯的 cron 設定
1. 想每天執行,卻設定成每分鐘
# 每分鐘(以下只列時間欄位)
* * * * *
# 每天午夜
0 0 * * *
星號表示欄位中的所有值。若要指定每天的一個時刻,就要同時限定分鐘與小時。
2. 以為星期日的編號都一樣
POSIX 五欄位語法可用 0 表示星期日;Cronie 也接受 7,但 Quartz 與 EventBridge Scheduler 使用 1。應先確認語法種類,再選擇星期編號。
3. 把「前七天」與「星期一」直接組合
# 錯誤:這是 1–7 日,或每個星期一
0 9 1-7 * 1
# 用戶 crontab:由指令確認是否為星期一
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /path/to/script.sh
上例才會限定每月第一個星期一;前提是指令與排程使用同一時區。
4. 把步進值當成持續累計的時間間隔
*/30 9-17 * * * 包含 17:30。若要在 17:00 結束,應拆成兩組排程:
0,30 9-16 * * *
0 17 * * *
*/35 * * * * 會選中每小時的 0 分與 35 分,09:35 到 10:00 只隔 25 分鐘。日期欄位的 */2 也會從每月 1 日重新取值,不代表連續每隔 48 小時。
5. 用 31 日代表每個月的最後一天
0 0 31 * * 不會在 2、4、6、9、11 月執行;指定 28–31 日則會在多個日期執行。可在指令中檢查隔天是否為 1 日:
# Cronie 搭配 GNU date;排程與指令皆使用 UTC
CRON_TZ=UTC
TZ=UTC
0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/script.sh
這會在 UTC 每月最後一天的開始時刻執行。GNU date 的 -d 語法不能直接用於 BSD/macOS 預設的 date;此例採用 UTC 也避開了當地 DST 的影響。若工作依當地營業日曆執行,需另外驗證該時區的日期判斷。
保存設定、避免重複執行及監察完成狀態
保留經過檢查的設定副本
crontab -l > crontab.txt
# 確認匯出成功,檢查內容及機密資訊後才加入版本控制
git add crontab.txt
git commit -m "加入定時備份設定"
# 先確認內容:這會取代目前用戶的整份 crontab
crontab crontab.txt
用註解寫清楚用途及負責團隊:
# [2026-01-15] 數據庫備份;負責團隊:系統維護團隊
0 2 * * * /opt/scripts/db-backup.sh >> /var/log/db-backup.log 2>&1
# [2026-03-01] 每日報表;負責團隊:報表團隊
0 8 * * 1-5 /opt/scripts/daily-report.sh >> /var/log/report.log 2>&1
決定輸出目的地、通知規則與保存期限,確認工作帳戶能寫入日誌,並管理累積的檔案容量。
用 flock 避免工作重疊
# Linux 的 util-linux flock:已有工作持有鎖定時就跳過
* * * * * /usr/bin/flock -n /home/user/.local/state/myjob/job.lock /path/to/long-job.sh
先建立由工作執行帳戶擁有的父目錄,並調整例子中的路徑。使用期間不要刪除或替換鎖定檔。flock -n 若無法取得鎖定,就立即結束而不等待;它只在持有鎖定期間,協調共用同一把鎖的程序。跨主機或不同檔案系統的行為,必須另外確認。
偵測工作沒有執行的情況
工作完成時向外部監察系統送出訊號,若預期時間內沒收到,就發出警報。Healthchecks.io、Cronitor 提供工作監察服務,也可以自行建立監察 API。訊號應代表你真正關心的完成條件,不能只把「已啟動」當成成功。
用預覽協助檢查,不把預覽當成執行保證
NanToo 的日文版 Cron 表達式生成器支援有限範圍的數字五欄位語法。手動更新預覽後,會以瀏覽器本地時間或 UTC,列出基準時間之後、八年內最多五次符合條件的時間。畫面不會自動更新,也不會安裝或實際排定工作。
工具不支援月份與星期名稱、@ 簡寫、L/W/#/?、秒與年份欄位。預覽也不會模擬正式環境 cron 服務的 DST 補正或重試;最後仍要在實際執行環境確認。
實際使用前,把三項設定一併核對
五個欄位能簡潔地表達許多定期工作,但共用基本概念不代表所有細節都能直接移植。請把表達式、時區與執行環境視為一組設定。
- 確認日期與星期是 OR 還是同時符合的條件。
- 移植時重新檢查欄位數、星期編號與擴充語法。
- 依業務需求選擇時區,並讓指令中的日期判斷使用同一時區。
- 確認 PATH、shell、路徑、權限與輸出處理。
- 將工具預覽與正式環境的規格交叉確認。
- 準備日誌、避免重疊的機制、重試及漏執行監察。
核對時間條件只是第一步。把環境設定、日誌及完成狀態一起管理,才較容易發現排程沒有執行,或執行後未達預期的情況。
參考資料及來源
- Cronie implementation: startup and per-job time zones ↗
- Google Cloud Scheduler: cron format ↗
- POSIX crontab ↗
- Cronie crontab(5) ↗
- Cronie cron(8): clock changes ↗
- GitHub Actions: schedule ↗
- Kubernetes CronJob ↗
- EventBridge Scheduler schedule types ↗
- Spring scheduling ↗
- Quartz CronTrigger ↗
- GNU date: relative items ↗
- util-linux flock ↗
編輯說明
本文使用 AI 協助編輯,並在發布前由編輯核對。內容仍可能包含事實、理解或時效上的錯誤;作出重要決定前,請查閱所列的一手資料或官方文件。

