什麼是 Cron Job?Linux 定時排程任務完整教學
在 Linux 系統中,Cron Job 是自動化排程任務的核心工具。只要設定一次,系統就會在指定時間自動執行備份、清理日誌、監控服務等重複性工作,大幅提升運維效率。本篇從 Cron 基礎原理、時間格式、實用範例到除錯技巧,完整介紹 Cron Job 的所有知識。
什麼是 Cron Job?
Cron 是 Unix-like 系統(包含 Linux)中內建的排程工具,名稱源自希臘文「chronos」(時間)。Cron 守護程序(cron daemon)在系統背景持續運行,每分鐘檢查一次排程設定,當時間符合條件時自動執行對應的指令或腳本。
Cron Job 的常見用途:
- 自動備份:每天凌晨備份資料庫或重要檔案
- 日誌清理:定期刪除超過 30 天的舊日誌,避免磁碟空間不足
- 系統監控:每隔幾分鐘檢查服務是否正常運作
- 報表產生:每週自動產生分析報表並發送 Email
- 憑證更新:自動更新 SSL 憑證(如 Let’s Encrypt 的 certbot)
Cron Job vs systemd timer
現代 Linux 系統(尤其是 Ubuntu 20.04+)也提供 systemd timer 作為 Cron 的替代方案。兩者各有優劣:
| 比較項目 | Cron Job | systemd timer |
|---|---|---|
| 設定難度 | 簡單,一行設定 | 較複雜,需建立兩個設定檔 |
| 日誌記錄 | 需手動導向日誌檔 | 自動整合 journald |
| 錯過執行處理 | 錯過就跳過 | 可設定錯過後補執行(OnBootSec) |
| 相依性管理 | 不支援 | 支援 systemd 服務相依 |
| 適用場景 | 快速設定簡單排程 | 複雜的服務管理排程 |
對於大多數日常排程需求,Cron Job 語法簡單、效果直接,仍是最常用的選擇。
Crontab 時間格式
Cron Job 的時間格式由五個欄位加上指令組成:
* * * * * 要執行的指令
│ │ │ │ │
│ │ │ │ └── 星期幾 (0-7,0 和 7 都是週日)
│ │ │ └──── 月份 (1-12)
│ │ └────── 日 (1-31)
│ └──────── 小時 (0-23)
└────────── 分鐘 (0-59)
欄位說明表
| 欄位 | 說明 | 可設定的值 |
|---|---|---|
| MIN | 分鐘 | 0 到 59 |
| HOUR | 小時 | 0 到 23 |
| DOM | 日 | 1 到 31 |
| MON | 月份 | 1 到 12,也可用英文簡稱(Jan, Feb, Mar…) |
| DOW | 星期幾 | 0(週日)到 6(週六),7 也代表週日,也可用英文簡稱(Sun, Mon…) |
| CMD | 要執行的指令 | 任何可執行的程式或腳本(含參數) |
特殊字符說明
除了直接填寫數字,Cron 還支援以下特殊字符讓排程更靈活:
| 字符 | 名稱 | 說明 | 範例 |
|---|---|---|---|
* | 星號 | 每個可能的值 | * 在分鐘欄位 = 每分鐘 |
, | 逗號 | 列出多個值 | 1,15,30 = 第 1、15、30 分鐘 |
- | 連字號 | 指定範圍 | 9-17 = 9 點到 17 點 |
/ | 斜線 | 指定間隔步長 | */5 = 每 5 個單位 |
Crontab 常用排程速查表
| 排程描述 | Crontab 寫法 |
|---|---|
| 每分鐘執行一次 | * * * * * |
| 每 5 分鐘執行一次 | */5 * * * * |
| 每 15 分鐘執行一次 | */15 * * * * |
| 每小時整點執行 | 0 * * * * |
| 每天凌晨 2 點執行 | 0 2 * * * |
| 每天早上 8 點 30 分執行 | 30 8 * * * |
| 每週一早上 9 點執行 | 0 9 * * 1 |
| 每週一、三、五凌晨 1 點執行 | 0 1 * * 1,3,5 |
| 每月 1 日凌晨 0 點執行 | 0 0 1 * * |
| 每月最後一天(近似)凌晨執行 | 0 0 28-31 * * |
| 每年 1 月 1 日 0 點執行 | 0 0 1 1 * |
| 工作日(週一到週五)每天 8 點 | 0 8 * * 1-5 |
Crontab 指令操作
基本指令
# 編輯當前使用者的 crontab(會開啟預設編輯器)
crontab -e
# 列出當前使用者的所有排程
crontab -l
# 刪除當前使用者的所有排程(危險!操作前請先備份)
crontab -r
# 備份目前的 crontab 到檔案
crontab -l > ~/crontab_backup.txt
# 從檔案還原 crontab
crontab ~/crontab_backup.txt
管理其他使用者的 crontab
# 以 root 身分編輯指定使用者的 crontab
sudo crontab -u username -e
# 查看指定使用者的排程
sudo crontab -u username -l
# 刪除指定使用者的排程
sudo crontab -u username -r
不知道怎麼設定的話,可以使用 Crontab Guru
Crontab Guru- 它可以幫你快速編輯並即時預覽 cron 時間格式的含義,適合不熟悉語法的初學者
實用排程範例
基本範例
# 每天凌晨 3 點執行備份腳本
0 3 * * * /home/benz/scripts/backup.sh
# 每週一凌晨 2 點清理超過 30 天的舊日誌
0 2 * * 1 find /var/log/app -name "*.log" -mtime +30 -delete
# 每小時檢查磁碟使用量(輸出到日誌)
0 * * * * df -h >> /var/log/disk_usage.log
# 每 5 分鐘確認 nginx 是否在運行,如果沒有就重啟
*/5 * * * * systemctl is-active nginx || systemctl restart nginx
# 每天早上 8 點發送系統狀態報告 Email
0 8 * * * /home/benz/scripts/daily_report.sh | mail -s "Daily Report" admin@example.com
進階範例(輸出日誌與錯誤處理)
在 Cron 中,預設情況下指令的輸出會被丟棄(或透過 MAILTO 寄送 Email)。建議將輸出和錯誤都導向到日誌檔,方便日後追蹤:
# 將 stdout 和 stderr 都寫入日誌檔
0 3 * * * /home/benz/scripts/backup.sh >> /var/log/cron_backup.log 2>&1
# 2>&1 的意思:將 stderr (2) 導向到 stdout (1) 所指向的地方
# >> 是附加模式,不會覆蓋舊內容
# 只記錄錯誤
0 3 * * * /home/benz/scripts/backup.sh > /dev/null 2>> /var/log/cron_errors.log
# 帶時間戳的日誌
0 * * * * echo "$(date '+%Y-%m-%d %H:%M:%S') - 執行完畢" >> /var/log/hourly.log
在 Cron 中設定環境變數
# 在 crontab 頂部設定環境變數(對所有排程有效)
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com
# 有了 MAILTO,每次 cron 有輸出時就會自動發 Email 通知
# 設為空字串可以關閉 Email:
MAILTO=""
0 3 * * * /home/benz/scripts/backup.sh
Cron Job 環境變數
這是 Cron Job 最常見的「陷阱」——在終端機可以正常執行的指令,放到 Cron 裡卻失敗了。原因幾乎都是環境變數不同。
為什麼 Cron 的環境變數和終端機不同?
當你在終端機登入時,系統會載入 .bashrc、.bash_profile 等設定檔,這些設定檔會配置 PATH、JAVA_HOME 等環境變數。但 Cron 執行時是非互動式的最小化環境,不會載入這些設定檔,所以 PATH 非常短:
# 終端機中的 PATH(很長,包含許多自訂路徑)
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/home/benz/.local/bin
# Cron 中預設的 PATH(很短)
# /usr/bin:/bin
解決方法
方法一:在 crontab 頂部設定 PATH(推薦)
# 在 crontab -e 編輯器頂部加入以下設定
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
# 之後的排程都會套用這個 PATH
0 3 * * * backup.sh
方法二:在腳本中使用完整路徑
# 不要寫
0 3 * * * node /home/benz/app/index.js
# 要寫完整路徑
0 3 * * * /usr/local/bin/node /home/benz/app/index.js
方法三:在腳本中載入環境設定
#!/bin/bash
# 在腳本開頭載入 bash 環境設定
source /home/benz/.bashrc
# 或
source /etc/profile
# 接著執行正式邏輯
/usr/local/bin/node /home/benz/app/index.js
Cron Job 除錯技巧
當排程沒有如預期執行時,以下方法可以幫助找出問題:
1. 查看系統日誌中的 Cron 記錄
# Ubuntu/Debian 系統查看 cron 日誌
grep CRON /var/log/syslog | tail -50
# 或使用 journalctl(systemd 系統)
journalctl -u cron --since "1 hour ago"
# 即時監看 cron 日誌
tail -f /var/log/syslog | grep CRON
2. 確認 Cron 守護程序正在運行
# 確認 cron 服務狀態
systemctl status cron
# 如果沒在運行,啟動它
sudo systemctl start cron
sudo systemctl enable cron # 設定開機自動啟動
3. 手動以相同環境測試指令
# 模擬 Cron 的執行環境來測試
env -i PATH=/usr/bin:/bin HOME=/home/benz SHELL=/bin/bash /home/benz/scripts/backup.sh
# 確認腳本有可執行權限
ls -l /home/benz/scripts/backup.sh
chmod +x /home/benz/scripts/backup.sh
4. 排查常見問題清單
| 問題 | 可能原因 | 解決方法 |
|---|---|---|
| Cron 完全沒執行 | cron 服務未啟動 | systemctl start cron |
| 指令找不到(command not found) | PATH 不包含指令路徑 | 在 crontab 頂部設定 PATH 或使用絕對路徑 |
| 腳本沒有執行權限 | 檔案缺少 x 權限 | chmod +x script.sh |
| 找不到檔案或目錄 | 使用了相對路徑 | 改用絕對路徑(如 /home/benz/data/) |
| 執行了但沒效果 | 環境變數缺失 | 在腳本中 source ~/.bashrc |
系統層級的 Cron 設定
除了使用者層級的 crontab,Linux 系統還有幾個系統層級的排程設定位置:
/etc/crontab — 系統全域排程
/etc/crontab 格式比使用者 crontab 多了一個使用者欄位:
# /etc/crontab 格式(多了 username 欄位)
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
/etc/cron.d/ — 套件專屬排程
套件安裝時常會在 /etc/cron.d/ 放置自己的排程設定,格式與 /etc/crontab 相同:
# 查看所有系統排程
ls /etc/cron.d/
# 常見的有 certbot(SSL 憑證更新)等
cat /etc/cron.d/certbot
/etc/cron.daily/, cron.weekly/, cron.monthly/
這三個目錄存放的是腳本,系統會自動在每天、每週、每月執行其中的所有腳本:
ls /etc/cron.daily/
ls /etc/cron.weekly/
ls /etc/cron.monthly/
# 手動觸發測試(模擬每天排程執行)
run-parts /etc/cron.daily
anacron — 適合不常開機的電腦
如果電腦不是 24 小時開機(如筆記型電腦),Cron 可能會因為電腦關機而錯過排程。anacron 會在開機後補執行錯過的排程:
# 查看 anacron 設定
cat /etc/anacrontab
# 格式說明:
# 1 5 cron.daily run-parts --report /etc/cron.daily
# 週期天數 延遲分鐘 任務識別碼 指令
常見問題(FAQ)
Q1:Cron Job 和 systemd timer 該用哪個?
對於大多數簡單排程,Cron Job 語法直覺、設定快速,是首選。如果你需要以下功能才考慮 systemd timer:
- 需要精確的日誌記錄(整合 journald)
- 排程任務有相依性(需要在某個服務啟動後才執行)
- 需要在開機後補執行錯過的任務(類似 anacron 的功能)
Q2:為什麼我的 Cron Job 在終端機可以執行,但排程就不跑?
最常見的三個原因:
- PATH 問題:Cron 的 PATH 很短,找不到你的指令。解決方式:在 crontab 頂部加入
PATH=...或在指令中使用完整路徑。 - 權限問題:腳本沒有執行權限(
chmod +x script.sh),或者是操作了需要 sudo 的資源。 - 相對路徑問題:腳本內用了相對路徑,但 Cron 的工作目錄不一定是你預期的那個。改用絕對路徑。
Q3:Cron 最小只能做到每分鐘,有辦法做到每秒執行嗎?
Cron 本身最小單位是分鐘,無法做到每秒執行。如果你需要每秒執行,有以下替代方案:
- 腳本內迴圈:在腳本中寫
while true; do command; sleep 1; done,然後用 Cron 啟動這個腳本 - systemd timer:使用
OnCalendar=*:*:0/5可以做到每 5 秒執行 - 背景常駐服務:如果是長期需求,考慮改為寫成 systemd service 讓它持續運行
延伸閱讀: