Biểu thức cron là gì? Cú pháp, các trường và 15 ví dụ
Biểu thức cron là một chuỗi ngắn cho trình lập lịch biết khi nào cần chạy một tác vụ. Dạng chuẩn gồm năm trường, cách nhau bằng dấu cách: phút, giờ, ngày trong tháng, tháng và ngày trong tuần. Ví dụ, */15 * * * * nghĩa là "cứ 15 phút một lần": */15 ở trường phút khớp với các phút 0, 15, 30 và 45, còn bốn dấu sao nghĩa là giờ nào, ngày nào, tháng nào và thứ nào cũng được.
Dùng thử miễn phí: Trình tạo Crontab Miễn phí, không cần tài khoản.
Cái tên này bắt nguồn từ cron, trình lập lịch tác vụ của Unix, nhưng ngày nay cùng cú pháp đó còn điều khiển các pipeline CI, trình lập lịch trên đám mây và framework ứng dụng, đôi khi có thêm trường. Bài hướng dẫn này giải thích cú pháp, 15 ví dụ đã được kiểm tra, các biến thể phổ biến và những lỗi khiến tác vụ chạy sai giờ. Để kiểm tra bất kỳ biểu thức nào trong lúc đọc, hãy dán nó vào Trình tạo Crontab miễn phí, công cụ sẽ mô tả lịch chạy bằng lời dễ hiểu.
Năm trường của một biểu thức cron
Các trường được đọc từ trái sang phải:
| Vị trí | Trường | Giá trị cho phép | Tên |
|---|---|---|---|
| 1 | Phút | 0–59 | – |
| 2 | Giờ | 0–23 | – |
| 3 | Ngày trong tháng | 1–31 | – |
| 4 | Tháng | 1–12 | jan–dec |
| 5 | Ngày trong tuần | 0–7 (cả 0 và 7 đều là Chủ nhật) | sun–sat |
Giờ dùng định dạng 24 giờ, nên 2 giờ chiều được viết là 14. Tên là ba chữ cái đầu của tên tháng hoặc tên thứ trong tiếng Anh, không phân biệt chữ hoa chữ thường. Một số bản cài đặt, trong đó có cron trên macOS, không chấp nhận tên bên trong khoảng hoặc danh sách, vì vậy 1-5 dùng được ở nhiều nơi hơn mon-fri.
Trong tệp crontab, lệnh đứng sau năm trường: 0 2 * * * /home/me/backup.sh. Tệp /etc/crontab dùng chung cho toàn hệ thống trên Linux còn có thêm một trường tên người dùng trước lệnh.
Ký tự đặc biệt: dấu sao, dấu phẩy, gạch nối và gạch chéo
Bốn ký tự đảm nhận gần như toàn bộ công việc:
| Ký tự | Ý nghĩa | Ví dụ | Khớp với |
|---|---|---|---|
* |
Mọi giá trị | * ở trường giờ |
Giờ 0, 1, 2 … 23 |
, |
Danh sách | 8,20 ở trường giờ |
Giờ 8 và 20 |
- |
Khoảng (gồm cả hai đầu) | 1-5 ở trường thứ |
Thứ Hai đến thứ Sáu |
/ |
Bước nhảy | */15 ở trường phút |
Phút 0, 15, 30, 45 |
Bạn có thể kết hợp chúng. 9-17/2 ở trường giờ khớp với 9, 11, 13, 15 và 17, còn 0-4,8-12 là danh sách gồm hai khoảng.
Bước nhảy bắt đầu lại ở mỗi chu kỳ. */7 ở trường phút khớp với 0, 7, 14 … 56, rồi lại khớp với 0 khi sang giờ mới, nên giữa :56 và :00 chỉ cách bốn phút chứ không phải bảy. Bước nhảy nào không chia hết cho 60 phút hoặc 24 giờ thì không bao giờ cho khoảng cách hoàn toàn đều nhau.
15 ví dụ về biểu thức cron
Mỗi ví dụ đều đã được kiểm tra từng trường. Thời gian tính theo múi giờ của trình lập lịch.
| Biểu thức cron | Khi nào chạy |
|---|---|
* * * * * |
Mỗi phút |
*/5 * * * * |
Cứ 5 phút một lần (:00, :05, :10 …) |
30 * * * * |
Mỗi giờ vào phút thứ 30 (00:30, 01:30 …) |
0 */2 * * * |
Cứ 2 giờ một lần, đúng giờ chẵn (00:00, 02:00 … 22:00) |
0 2 * * * |
Mỗi ngày lúc 02:00 |
0 8,20 * * * |
Hai lần mỗi ngày, lúc 08:00 và 20:00 |
0 9 * * 1-5 |
Thứ Hai đến thứ Sáu lúc 09:00 |
*/15 9-17 * * 1-5 |
Cứ 15 phút một lần từ 09:00 đến 17:45, thứ Hai đến thứ Sáu |
0 6 * * 0,6 |
Thứ Bảy và Chủ nhật lúc 06:00 |
0 0 * * 0 |
Mỗi Chủ nhật lúc nửa đêm |
0 22 * * 5 |
Mỗi thứ Sáu lúc 22:00 |
0 0 1 * * |
Ngày 1 hằng tháng lúc nửa đêm |
0 12 15 * * |
Ngày 15 hằng tháng lúc 12:00 |
0 0 1 1,4,7,10 * |
Hằng quý: ngày 1 tháng 1, tháng 4, tháng 7 và tháng 10 lúc nửa đêm |
0 0 1 1 * |
Mỗi năm một lần, ngày 1 tháng 1 lúc nửa đêm |
Có hai chi tiết rất dễ bỏ sót. Thứ nhất, khoảng bao gồm cả hai đầu, nên 9-17 có cả giờ 17: */15 9-17 * * 1-5 chạy 36 lần mỗi ngày và lần cuối là lúc 17:45, không phải 17:00. Thứ hai, 0 0 1 */3 * cho cùng lịch chạy hằng quý, vì */3 ở trường tháng bắt đầu từ 1 và khớp với các tháng 1, 4, 7 và 10.
Macro ngoài chuẩn: @daily, @weekly và @reboot
Nhiều bản cài đặt cron chấp nhận một từ khóa thay cho năm trường:
| Macro | Tương đương | Chạy lúc |
|---|---|---|
@yearly hoặc @annually |
0 0 1 1 * |
Nửa đêm ngày 1 tháng 1 |
@monthly |
0 0 1 * * |
Nửa đêm ngày 1 hằng tháng |
@weekly |
0 0 * * 0 |
Nửa đêm Chủ nhật |
@daily hoặc @midnight |
0 0 * * * |
Nửa đêm mỗi ngày |
@hourly |
0 * * * * |
Phút 0 của mỗi giờ |
@reboot |
– | Một lần, khi tiến trình nền cron khởi động |
Vixie cron, cronie và cron trên macOS hỗ trợ tất cả các macro này, còn CronJob của Kubernetes chấp nhận các macro theo thời gian nhưng không nhận @reboot. Chúng không thuộc chuẩn POSIX, nên hãy xem tài liệu trước khi dùng ở nơi khác.
Các biến thể của cron: giây, năm và ký tự bổ sung
Không phải trình lập lịch nào cũng đọc năm trường, vì vậy hãy kiểm tra xem bạn đang viết cho phương ngữ nào.
- Quartz (Java) dùng sáu hoặc bảy trường: giây, phút, giờ, ngày trong tháng, tháng, ngày trong tuần và năm (không bắt buộc). Một trong hai trường ngày phải là
?("không có giá trị cụ thể"). Quartz còn hỗ trợL(cuối cùng),W(ngày làm việc gần nhất) và#(thứ thứ n trong tháng):0 15 10 L * ?chạy lúc 10:15 vào ngày cuối cùng của mỗi tháng, còn0 15 10 ? * 6#3chạy lúc 10:15 vào thứ Sáu thứ ba, vì Quartz đánh số các ngày trong tuần từ 1 đến 7, bắt đầu từ Chủ nhật. - Biểu thức Spring trong
@Scheduled(cron = "…")có sáu trường: giây đứng đầu, sau đó là năm trường quen thuộc, không có năm. - Quy tắc AWS EventBridge dùng
cron(minutes hours day-of-month month day-of-week year): sáu trường, không có giây, năm ở cuối và một dấu?ở một trong hai trường ngày.cron(0 12 * * ? *)chạy mỗi ngày lúc 12:00 UTC. Quy tắc trên event bus chạy theo UTC, còn EventBridge Scheduler cho phép chọn múi giờ. Giống Quartz, AWS đánh số các ngày trong tuần từ 1 đến 7, với 1 = Chủ nhật. - CronJob của Kubernetes dùng năm trường chuẩn trong
spec.schedule. Nếu không cóspec.timeZone, lịch chạy theo múi giờ của kube-controller-manager. Hãy đặt một tên IANA nhưEurope/Berlinđể cố định múi giờ. - Trình kích hoạt
schedulecủa GitHub Actions dùng năm trường và chạy theo UTC, trừ khi bạn đặt một múi giờ IANA tùy chọn. Khoảng cách ngắn nhất được hỗ trợ là năm phút, các macro như@dailykhông được hỗ trợ, và các lần chạy theo lịch có thể bắt đầu trễ khi GitHub quá tải, nhất là vào đầu mỗi giờ.
Cái bẫy giữa ngày trong tháng và ngày trong tuần
Trong cron chuẩn (Vixie), hai trường ngày được kết hợp bằng HOẶC chứ không phải VÀ khi cả hai đều bị giới hạn. Nếu một trong hai là *, chỉ trường còn lại được tính. Vì thế 0 9 1-7 * 1 không có nghĩa là "thứ Hai đầu tiên của tháng". Tác vụ sẽ chạy lúc 09:00 vào từng ngày từ 1 đến 7 và thêm vào mọi thứ Hai, tổng cộng 10 hoặc 11 lần mỗi tháng.
Để chỉ chạy vào thứ Hai đầu tiên, hãy giữ một trường ngày trong biểu thức và kiểm tra trường kia trong lệnh:
0 9 1-7 * * [ "$(date +\%u)" = 1 ] && /path/to/report.sh
date +%u in ra thứ trong tuần dưới dạng số từ 1 đến 7, với thứ Hai = 1. Dấu phần trăm phải được thoát thành \%, vì cron biến dấu % không được thoát trong lệnh thành ký tự xuống dòng. Quartz và AWS tránh sự mơ hồ này bằng cách bắt buộc dùng ? ở một trong hai trường ngày.
Múi giờ và giờ mùa hè
Cron chạy theo múi giờ của máy hoặc dịch vụ xử lý biểu thức, mà trên máy chủ và nền tảng đám mây thường là UTC chứ không phải giờ địa phương của bạn. 0 9 * * * trên máy chủ dùng UTC sẽ chạy lúc 09:00 UTC, tức 16:00 ở Việt Nam (UTC+7). Công cụ chuyển đổi múi giờ giúp bạn đổi giờ địa phương sang múi giờ của trình lập lịch.
Ở những múi giờ áp dụng giờ mùa hè, có thêm hai trường hợp đặc biệt: khi đồng hồ được vặn lên, một giờ địa phương không tồn tại, và khi vặn lùi, một giờ diễn ra hai lần. Cron của Debian và cronie bù trừ các thay đổi đồng hồ nhỏ cho những tác vụ đặt giờ cố định: lần chạy rơi vào giờ bị bỏ qua sẽ được thực hiện ngay sau khi đổi giờ, còn lần chạy trong giờ bị lặp lại sẽ không chạy lần nữa. Các trình lập lịch khác xử lý theo cách khác, nên an toàn nhất là để máy chủ chạy theo UTC hoặc tránh đặt tác vụ quan trọng vào những giờ đổi đồng hồ, thường là từ 01:00 đến 03:00 giờ địa phương.
Cách kiểm tra một biểu thức cron
Một biểu thức cron sai hiếm khi báo lỗi: tác vụ chỉ đơn giản là chạy sai giờ hoặc không bao giờ chạy. Một quy trình ngắn sẽ phát hiện hầu hết vấn đề:
- Đọc lại bằng lời. Dán biểu thức vào một công cụ mô tả lịch chạy và kiểm tra câu mô tả có đúng ý bạn không, nhất là ở hai trường ngày.
- Thử lệnh với lịch chạy ngắn. Tạm thời dùng
* * * * *để tác vụ chạy trong vòng một phút, sau đó chuyển sang lịch thật. - Lưu lại đầu ra. Thêm
>> /tmp/myjob.log 2>&1sau lệnh để lỗi được ghi vào tệp thay vì bị mất. - Xem nhật ký của cron. Dùng
grep CRON /var/log/sysloghoặcjournalctl -u crontrên Debian và Ubuntu, vàjournalctl -u crondtrên Fedora và RHEL. - Dùng đường dẫn tuyệt đối. Cron khởi chạy tác vụ với môi trường tối giản và
PATHngắn, nên một lệnh chạy được trong terminal của bạn vẫn có thể lỗi khi chạy bằng cron.
Tạo biểu thức với Trình tạo Crontab
Trình tạo Crontab miễn phí chạy hoàn toàn trong trình duyệt của bạn. Hãy nhập một biểu thức hoặc bấm vào một mẫu nhanh như "Mỗi 15 phút", "Hằng ngày lúc 2 giờ sáng" hay "Mỗi thứ Hai", và công cụ sẽ:
- kiểm tra cú pháp và báo khi biểu thức không hợp lệ;
- mô tả lịch chạy bằng lời dễ hiểu (bằng tiếng Anh);
- chấp nhận năm trường chuẩn, một trường giây tùy chọn ở đầu, tên tháng và tên thứ, cùng số 7 cho Chủ nhật;
- chuyển các macro như
@weeklythành biểu thức năm trường tương đương và giải thích@rebootlàm gì; - hiển thị bảng tra cứu nhanh với từng ký hiệu và một ví dụ.
Các công tắc cho phép chọn mô tả chi tiết hơn, định dạng 24 giờ và việc số thứ trong tuần có bắt đầu từ 0 cho Chủ nhật hay không. Công cụ không chấp nhận các ký tự chỉ có trong Quartz (L, W, #) hay trường năm. Công cụ chỉ tạo và kiểm tra biểu thức; để lên lịch cho tác vụ, hãy sao chép biểu thức vào crontab -e hoặc vào cấu hình của nền tảng bạn dùng.
Câu hỏi thường gặp
Cron và crontab khác nhau thế nào?
Cron là dịch vụ chạy nền (daemon) thực thi các tác vụ đã lên lịch. Crontab, viết tắt của "cron table", là tệp liệt kê các tác vụ đó, mỗi dòng một tác vụ: một biểu thức cron, theo sau là một lệnh. Bạn sửa crontab của mình bằng crontab -e và xem nó bằng crontab -l.
Tác vụ cron có chạy được mỗi 30 giây không?
Cron chuẩn thì không, vì đơn vị nhỏ nhất là một phút. Các trình lập lịch có trường giây thì làm được: Spring chấp nhận */30 * * * * *, còn Quartz cần ? ở một trường ngày, như */30 * * * * ?. Với cron cổ điển, cách làm phổ biến là hai dòng cùng chạy mỗi phút, trong đó một dòng chạy sleep 30; trước lệnh.
Làm sao để chạy tác vụ cron vào ngày cuối tháng?
Cron chuẩn không có ký hiệu "ngày cuối", nhưng Quartz và AWS hỗ trợ L ở trường ngày trong tháng. Với cron cổ điển, hãy lên lịch cho các ngày 28 đến 31 và để lệnh kiểm tra xem ngày mai có phải ngày 1 hay không:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/job.sh
date -d tomorrow là cú pháp của GNU date, có trên Linux. Trên macOS, hãy dùng date -v+1d thay thế.
Dùng thử miễn phí: Trình tạo Crontab Miễn phí, không cần tài khoản.