什麼是 Cron Job?Linux 定時排程任務完整教學

2024/01/31

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 Jobsystemd 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
DOM1 到 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 等設定檔,這些設定檔會配置 PATHJAVA_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 在終端機可以執行,但排程就不跑?

最常見的三個原因:

  1. PATH 問題:Cron 的 PATH 很短,找不到你的指令。解決方式:在 crontab 頂部加入 PATH=... 或在指令中使用完整路徑。
  2. 權限問題:腳本沒有執行權限(chmod +x script.sh),或者是操作了需要 sudo 的資源。
  3. 相對路徑問題:腳本內用了相對路徑,但 Cron 的工作目錄不一定是你預期的那個。改用絕對路徑。

Q3:Cron 最小只能做到每分鐘,有辦法做到每秒執行嗎?

Cron 本身最小單位是分鐘,無法做到每秒執行。如果你需要每秒執行,有以下替代方案:

  • 腳本內迴圈:在腳本中寫 while true; do command; sleep 1; done,然後用 Cron 啟動這個腳本
  • systemd timer:使用 OnCalendar=*:*:0/5 可以做到每 5 秒執行
  • 背景常駐服務:如果是長期需求,考慮改為寫成 systemd service 讓它持續運行

延伸閱讀: