SCHEDULING

راهنمای کامل عبارت Cron: سینتکس، مثال‌های کاربردی و خطاهای رایج

پنج فیلد Cron را با ده‌ها مثال آماده یاد بگیرید: تفاوت روز ماه و روز هفته، منطقه‌ی زمانی، علامت درصد و اشتباه‌هایی که جاب‌ها را بی‌صدا از کار می‌اندازند.

cron قدیمی‌ترین و هنوز رایج‌ترین راه زمان‌بندی کار روی سرورهای لینوکس است، و ساختار پنج‌فیلدی آن با وجود سادگی، جای زیادی برای اشتباه دارد. این راهنما سینتکس را توضیح می‌دهد، مثال‌های پرکاربرد را فهرست می‌کند و خطاهایی را بررسی می‌کند که باعث می‌شوند جاب بدون هیچ پیام خطایی اجرا نشود.

پنج فیلد cron

┌───────────── minute        (0–59)
│ ┌─────────── hour          (0–23)
│ │ ┌───────── day of month  (1–31)
│ │ │ ┌─────── month         (1–12 or JAN–DEC)
│ │ │ │ ┌───── day of week   (0–7 or SUN–SAT; 0 and 7 = Sunday)
│ │ │ │ │
* * * * *   command

در هر فیلد این نمادها مجازند:

نمادمعنیمثال
*همه‌ی مقدارها* * * * *
,فهرست مقدارها0 8,12,18 * * *
-بازه0 9-17 * * *
/گام*/10 * * * *
CRONCron Expression Generatorعبارت را با انتخاب‌های ساده بسازید و چند اجرای بعدی را (میلادی و شمسی) ببینید.

مثال‌هایی که واقعاً لازم می‌شوند

عبارتزمان اجرا
* * * * *هر دقیقه
*/5 * * * *هر ۵ دقیقه
0 * * * *ابتدای هر ساعت
*/15 9-18 * * 1-5هر ۱۵ دقیقه، بین ۹ تا ۱۸، دوشنبه تا جمعه
0 3 * * *هر شب ساعت ۳
0 9 * * 1-5روزهای کاری ساعت ۹ صبح
30 2 * * 0یکشنبه‌ها ساعت ۲:۳۰
0 */6 * * *هر ۶ ساعت (۰، ۶، ۱۲، ۱۸)
0 0 1 * *اول هر ماه، نیمه‌شب
0 0 1 1 *اول ژانویه، نیمه‌شب
@dailyمیان‌بر برای 0 0 * * *
@rebootیک‌بار بعد از هر بوت سیستم
note تقویم cron میلادی است. اگر منظورتان «اول هر ماه شمسی» است، cron نمی‌تواند مستقیم بگوید؛ باید جاب را هر روز اجرا کنید و تاریخ شمسی را داخل خود اسکریپت بررسی کنید.

نکته‌ی مهم: روز ماه و روز هفته با «یا» ترکیب می‌شوند

اگر هر دو فیلد روز ماه و روز هفته مقدار مشخص داشته باشند (یعنی هیچ‌کدام * نباشد)، cron استاندارد جاب را وقتی اجرا می‌کند که هرکدام از دو شرط برقرار باشد، نه هر دو. پس:

0 9 13 * 5     # نه «جمعه‌ی سیزدهم»؛ بلکه هر سیزدهم ماه و هر جمعه، ساعت ۹

اگر واقعاً می‌خواهید «جمعه‌ی سیزدهم»، یکی از دو شرط را به خود اسکریپت منتقل کنید:

0 9 13 * *  [ "$(date +\%u)" = 5 ] && /usr/local/bin/job.sh

به \% دقت کنید؛ این موضوع در بخش بعد توضیح داده شده است.

خطاهایی که جاب را بی‌صدا از کار می‌اندازند

۱. علامت % در crontab

در فایل crontab علامت % یعنی «خط جدید» و ورودی بعد از آن به stdin دستور داده می‌شود. هر % (مثلاً در date +%F) باید با بک‌اسلش نوشته شود: date +\%F.

۲. PATH و محیط حداقلی

cron شل ورودی نمی‌گیرد و PATH معمولاً فقط /usr/bin:/bin است. مسیر مطلق بنویسید یا بالای crontab تعریف کنید:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""

0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

۳. اجرای هم‌پوشان

اگر جابِ هر ۵ دقیقه‌ای گاهی ۷ دقیقه طول بکشد، دو نسخه هم‌زمان اجرا می‌شوند و روی هم می‌نویسند. با flock جلوی این را بگیرید:

*/5 * * * * flock -n /tmp/sync.lock /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1

۴. خروجی که هیچ‌کس نمی‌بیند

بدون >> فایل 2>&1 خروجی و خطاها به ایمیل محلی کاربر می‌روند؛ که معمولاً کسی آن را نمی‌خواند. همیشه لاگ بگیرید و جاب‌های مهم را با یک پایش (مثلاً healthchecks یا یک متریک Prometheus) زیر نظر داشته باشید تا نبودِ اجرا هم هشدار بدهد.

۵. ساعت تابستانی و منطقه‌ی زمانی

در منطقه‌هایی که ساعت تابستانی دارند، جاب ساعت ۲:۳۰ شب ممکن است یک‌بار اجرا نشود و یک‌بار دوبار اجرا شود. جاب‌های حساس را روی UTC بگذارید. در کانتینرها و Kubernetes هم منطقه‌ی زمانی پیش‌فرض اغلب UTC است؛ پس «ساعت ۹ صبح» بدون تنظیم، به وقت UTC اجرا می‌شود.

TSUnix Timestamp Converterیک زمان را بین UTC، زمان محلی و تقویم شمسی تبدیل کنید تا اختلاف منطقه‌ی زمانی را ببینید.

جایگزین‌ها و تفاوت‌ها در سیستم‌های دیگر

  • systemd timers: امکان Persistent=true (اجرای جاب‌های از دست‌رفته بعد از بوت)، لاگ در journal و وابستگی به سرویس‌ها را دارند؛ برای کارهای مهم‌تر انتخاب بهتری هستند.
  • Kubernetes CronJob: از همان پنج فیلد استفاده می‌کند. نسخه‌های جدید فیلد timeZone هم دارند؛ در غیر این صورت زمان بر اساس منطقه‌ی زمانی کنترل‌کننده‌ی کلاستر است.
  • Quartz، AWS EventBridge و بعضی ابزارهای CI: سینتکس‌های شبیه ولی متفاوت دارند (شش یا هفت فیلد، با ثانیه یا سال، و علامت ?). قبل از کپی‌کردن، مستندات همان سیستم را ببینید.
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-backup
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid      # جلوی اجرای هم‌پوشان
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: registry.example.com/backup:1.4.2
              args: ["--full"]

جمع‌بندی

برای زمان‌بندی قابل‌اعتماد، عبارت را پیش از استفاده با پیش‌نمایش اجراهای بعدی بررسی کنید، مسیر مطلق و متغیرهای محیطی را صریح بنویسید، اجرای هم‌پوشان را با flock یا concurrencyPolicy: Forbid مهار کنید و نبودِ اجرا را هم مانیتور کنید. برای پایش جاب‌ها و هشداردهی روی آن‌ها، راهنمای هشداردهی اصولی را ببینید.

پرسش‌های متداول

*/5 * * * * دقیقاً چه زمانی اجرا می‌شود؟
هر پنج دقیقه یک‌بار، یعنی در دقیقه‌های ۰، ۵، ۱۰ و … از هر ساعت. «هر ۵ دقیقه از لحظه‌ی ذخیره‌ی فایل» نیست؛ گام‌ها همیشه از ابتدای بازه شمرده می‌شوند.
فرق 0 0 * * 0 و 0 0 * * 7 چیست؟
در اغلب پیاده‌سازی‌ها هر دو یکشنبه‌اند: هم ۰ و هم ۷ برای یکشنبه پذیرفته می‌شود. برای وضوح بهتر است از نام (SUN) استفاده کنید.
چرا جاب من در cron کار نمی‌کند ولی در ترمینال کار می‌کند؟
معمولاً به‌خاطر محیط: cron با PATH حداقلی و بدون پروفایل شل اجرا می‌شود. مسیر مطلق دستورها را بنویسید، متغیرهای لازم را در crontab تعریف کنید و خروجی را در یک فایل لاگ ببینید.
منطقه‌ی زمانی cron چیست؟
منطقه‌ی زمانی سیستم است (یا CRON_TZ در بعضی نسخه‌ها). روی سرورها و کانتینرها اغلب UTC است. اگر انتظار دارید «ساعت ۹ صبح به وقت تهران» اجرا شود، آن را به‌صراحت تنظیم یا به UTC تبدیل کنید.