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.

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 đề:

  1. Đọ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.
  2. 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.
  3. Lưu lại đầu ra. Thêm >> /tmp/myjob.log 2>&1 sau lệnh để lỗi được ghi vào tệp thay vì bị mất.
  4. Xem nhật ký của cron. Dùng grep CRON /var/log/syslog hoặc journalctl -u cron trên Debian và Ubuntu, và journalctl -u crond trên Fedora và RHEL.
  5. 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à PATH ngắ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ẽ:

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.