Crontab: أمثلة جدولة المهام في لينكس والصياغة والأخطاء الشائعة
crontab جدول زمني يشغّل به خادم cron الأوامر: كل سطر يتكون من خمسة حقول زمنية (الدقيقة، والساعة، ويوم الشهر، والشهر، ويوم الأسبوع) ثم الأمر، فمثلاً */5 * * * * /opt/backup.sh يشغّل السكريبت كل 5 دقائق. يُحرَّر الجدول بالأمر crontab -e، وأغلب المشاكل سببها بيئة cron المحدودة، لذلك استخدم المسارات الكاملة ووجّه المخرجات إلى سجل.
Cron هو المجدول القياسي للمهام في لينكس: يقرأ الخادم cron جداول الجدولة (crontab) مرة في الدقيقة، ويُشغّل الأوامر التي حان وقتها. بهذه الطريقة تُنفَّذ النسخ الاحتياطية ليلاً، وتُنظَّف الملفات المؤقتة، وتُجدَّد الشهادات، وتُرسل التقارير. فيما يلي الصياغة، وأمثلة crontab للحالات الشائعة، وشرح الأخطاء التي تجعل المهمة تتوقف بصمت دون أن تُنفَّذ.
كيف تفتح جدول crontab وتحرره
لكل مستخدم جدوله الخاص. وتعمل معه عبر الأمر crontab، لا عبر تعديل الملفات مباشرة في /var/spool/cron:
| الأمر | ما يفعله |
|---|---|
crontab -e | فتح جدولك في المحرر (عند أول تشغيل يطلب اختيار المحرر) |
crontab -l | عرض الجدول الحالي |
crontab -r | حذف الجدول كاملاً دون تأكيد |
crontab -i -r | الحذف مع التأكيد (Debian/Ubuntu وRHEL) |
sudo crontab -u alice -e | تحرير جدول مستخدم آخر |
crontab ملف | استبدال الجدول بمحتوى ملف |
بعد الحفظ عبر crontab -e يتحقق البرنامج من الصياغة ويكتب crontab: installing new crontab. وإذا كان في السطر خطأ فسترى errors in crontab file, can't install مع عرض لإعادة التحرير. ولا يلزم إعادة تشغيل الخدمة، إذ تُلتقط التغييرات تلقائياً.
انتبه عند استخدام -r: حرفا e وr متجاوران على لوحة المفاتيح، ولا توجد نسخة احتياطية للجدول. عادة مفيدة هي الاحتفاظ بنسخة:
crontab -l > ~/crontab.bak
# للاستعادة
crontab ~/crontab.bak
صياغة crontab: خمسة حقول زمنية
كل سطر مهمة يبدو هكذا:
# ┌───────── الدقيقة (0–59)
# │ ┌─────── الساعة (0–23)
# │ │ ┌───── يوم الشهر (1–31)
# │ │ │ ┌─── الشهر (1–12 أو jan–dec)
# │ │ │ │ ┌─ يوم الأسبوع (0–7 أو sun–sat؛ 0 و7 هما الأحد)
# │ │ │ │ │
* * * * * الأمر
في كل حقل يمكن استخدام رموز خاصة:
| الرمز | المعنى | مثال |
|---|---|---|
* | أي قيمة | * * * * * — كل دقيقة |
, | قائمة | 0 9,18 * * * — الساعة 9:00 و18:00 |
- | نطاق | 0 9-18 * * * — كل ساعة من 9 حتى 18 |
/ | خطوة | */15 * * * * — كل 15 دقيقة |
يمكن دمج الخطوة مع النطاق: 0 8-20/2 * * * — كل ساعتين من 8 حتى 20 (8 و10 و…و20). يُؤخذ الوقت وفق المنطقة الزمنية للنظام، وتحقق منها بالأمر timedatectl.
أمثلة crontab لكل يوم
| الجدولة | سطر crontab |
|---|---|
| كل دقيقة | * * * * * |
| كل 5 دقائق | */5 * * * * |
| كل 30 دقيقة | */30 * * * * أو 0,30 * * * * |
| كل ساعة عند بداية الساعة | 0 * * * * |
| كل 6 ساعات | 0 */6 * * * |
| كل يوم الساعة 3:00 | 0 3 * * * |
| كل يوم الساعة 23:45 | 45 23 * * * |
| أيام العمل الساعة 9:00 | 0 9 * * 1-5 |
| عطلة نهاية الأسبوع عند الظهر | 0 12 * * 6,0 |
| يوم الاثنين الساعة 8:30 | 30 8 * * 1 |
| أول يوم من كل شهر الساعة 00:00 | 0 0 1 * * |
| مرة كل ربع سنة | 0 0 1 1,4,7,10 * |
| مرة في السنة، 1 يناير | 0 0 1 1 * |
| كل 10 دقائق في ساعات العمل | */10 9-18 * * 1-5 |
سطر المهمة الكامل هو الجدولة مع الأمر:
# نسخة احتياطية لقاعدة البيانات كل ليلة الساعة 3:00
0 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1
# تنظيف الملفات المؤقتة الأقدم من 7 أيام يوم الأحد
0 4 * * 0 /usr/bin/find /tmp/app -type f -mtime +7 -delete
# فحص الموقع كل 5 دقائق
*/5 * * * * /usr/bin/curl -fsS https://example.com/health > /dev/null || /usr/local/bin/alert.sh
الأسماء المختصرة @reboot و@daily وغيرها
بدلاً من الحقول الخمسة يمكن كتابة اسم مختصر:
| الاسم المختصر | المكافئ |
|---|---|
@reboot | مرة واحدة عند بدء خدمة cron (بعد إقلاع النظام) |
@hourly | 0 * * * * |
@daily / @midnight | 0 0 * * * |
@weekly | 0 0 * * 0 |
@monthly | 0 0 1 * * |
@yearly / @annually | 0 0 1 1 * |
@reboot /home/alice/bin/start-tunnel.sh >> /home/alice/tunnel.log 2>&1
أما الخدمات التي يجب أن تعمل باستمرار وتُعاد تشغيلها عند توقفها، فإن @reboot حل ضعيف لها: الأفضل وحدة systemd مع Restart=on-failure.
كيف تشغّل مهمة كل N ثانية
أصغر وحدة في cron هي دقيقة واحدة. إذا احتجت إلى تكرار أسرع فهناك حيلتان. الأولى سطور عدة مع إزاحة عبر sleep:
* * * * * /opt/check.sh
* * * * * sleep 30; /opt/check.sh
والثانية، وهي الأصح، مؤقت systemd مع OnUnitActiveSec=30s. وإذا كانت المهمة تتكرر أكثر من مرة في الدقيقة فهذا غالباً عمل لخدمة دائمة أو مؤقت، لا لـ cron.
الجدول النظامي: /etc/crontab وcron.d/
إلى جانب جداول المستخدمين توجد جداول نظامية، وصيغتها مختلفة: بين الوقت والأمر يوجد اسم المستخدم الذي سيُشغَّل الأمر باسمه.
# /etc/cron.d/certbot-renew
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root certbot renew --quiet
أماكن الملفات:
/etc/crontab— الجدول النظامي الرئيسي؛ وفي Debian/Ubuntu تُشغَّل منه المجلدات الموجودة أدناه./etc/cron.d/— ملفات منفصلة للمهام، وتُضاف غالباً عبر الحزم أو Ansible./etc/cron.hourly/وcron.daily/وcron.weekly/وcron.monthly/— توضع فيها السكريبتات التنفيذية دون جدولة، ويشغّلهاrun-parts(أو anacron).
فخ مع run-parts: في Debian وUbuntu يتجاهل الملفات التي تحتوي على نقطة في اسمها. فالسكريبت backup.sh في /etc/cron.daily/ لن يعمل، وسمّه backup. ولمعرفة ما سيُنفَّذ استخدم run-parts --test /etc/cron.daily.
يجب أن تكون ملفات /etc/cron.d/ مملوكة لـ root، وألا تكون قابلة للكتابة من المجموعة أو من الآخرين، وأن تنتهي بسطر جديد، وإلا تجاهلها cron.
بيئة cron: PATH وSHELL والمتغيرات
السبب الرئيسي لعبارة «في الطرفية يعمل، وفي cron لا يعمل» هو البيئة. يشغّل cron الأوامر عبر /bin/sh دون تحميل .bashrc أو .profile، ومع PATH محدود (غالباً /usr/bin:/bin). لذلك قد لا يُعثر على docker أو node أو php الموجودة في /usr/local/bin.
يمكن تحديد المتغيرات في بداية الجدول:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
CRON_TZ=Asia/Riyadh
0 6 * * * cd /srv/app && ./report.sh
MAILTO— إلى أين تُرسل مخرجات المهمة؛ وMAILTO=""يوقف الرسائل.CRON_TZ— المنطقة الزمنية للجدولة (مدعومة في cronie في RHEL/Fedora، أما نسخة cron في Debian فلا تدعم هذا المتغير).- لا توجد توسعات مثل
$HOME/binداخل قيم المتغيرات، فاكتب المسارات كاملة.
أكثر الخيارات موثوقية هي كتابة المسارات المطلقة لكل البرامج والملفات في المهمة، وكتابة سطر shebang في أول السكريبتات: #!/bin/bash.
أين تقرأ سجلات cron
يسجّل cron في السجل حدوث تشغيل المهمة نفسه، لكنه لا يسجل مخرجاتها:
# Debian/Ubuntu
grep CRON /var/log/syslog
# عبر systemd-journald
journalctl -u cron --since today # Debian/Ubuntu
journalctl -u crond --since today # RHEL وFedora وRocky
# RHEL دون journald
tail -f /var/log/cron
سطر التشغيل يبدو تقريباً هكذا:
Oct 14 03:00:01 web1 CRON[21544]: (root) CMD (/usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1)
لكي ترى ما الذي طبعته المهمة وبأي خطأ توقفت، وجّه الإخراج القياسي والخطأ القياسي معاً إلى ملف: >> /var/log/job.log 2>&1. وبدون ذلك تُرسل المخرجات بالبريد المحلي، وإذا لم يكن برنامج البريد مثبتاً فتضيع (ويظهر في السجل No MTA installed, discarding output).
لماذا لا تعمل المهمة في cron: الأخطاء الشائعة
الأمر غير موجود
العَرَض: command not found في سجل المهمة. السبب هو PATH المحدود. الحل: المسار الكامل للبرنامج (يرشدك إليه which docker)، أو سطر PATH= في بداية الجدول.
علامة النسبة المئوية في الأمر
في crontab يعني الرمز % سطراً جديداً: كل ما بعد أول % يُمرَّر للأمر عبر الإدخال القياسي. لذلك يتعطل السطر الكلاسيكي الذي يضع التاريخ في اسم الملف:
# خطأ — سيُقطع الأمر عند أول %
0 2 * * * tar czf /backup/site-$(date +%F).tar.gz /var/www
# صحيح — مع الهروب
0 2 * * * tar czf /backup/site-$(date +\%F).tar.gz /var/www
والأسهل نقل المنطق إلى سكريبت، فلا تحتاج هناك إلى هروب %.
السكريبت غير تنفيذي أو بنهايات أسطر خاطئة
الخطأ Permission denied يعني أن الملف لا يملك بت التنفيذ x: نفّذ chmod +x script.sh، أو شغّله صراحةً: /bin/bash /opt/script.sh. وإذا كان السكريبت مكتوباً في Windows فنهايات الأسطر CRLF تسبب خطأً غريباً هو /bin/bash^M: bad interpreter، ويُصلح بالأمر sed -i 's/\r$//' script.sh أو الأداة dos2unix.
المسارات النسبية ومجلد العمل
تبدأ المهمة في المجلد الشخصي للمستخدم. فإذا كان السكريبت يقرأ ./config.ini فسيبحث عنه في ~. استخدم cd /srv/app && قبل الأمر، أو المسارات المطلقة داخل السكريبت.
يوم الشهر ويوم الأسبوع معاً
إذا حُدّد الحقلان، فإن cron يعمل عندما يتحقق أي منهما، لا كلاهما معاً. فالسطر 0 9 13 * 5 يعمل يوم 13 من الشهر وكل جمعة، لا يوم الجمعة 13 فقط. والشرط المطلوب يُفحص داخل الأمر:
0 9 13 * * [ "$(date +\%u)" = 5 ] && /opt/friday13.sh
تداخل المهام
إذا استغرقت النسخة الاحتياطية وقتاً أطول من الفاصل الزمني، فإن التشغيل التالي يبدأ فوق السابق. الحماية هي القفل عبر flock:
*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /opt/sync.sh
مع الخيار -n تنتهي النسخة الثانية فوراً إذا كانت الأولى ما زالت تعمل.
خدمة cron غير مشغّلة
تحقق بالأمر systemctl status cron (وفي RHEL الاسم crond). وفي الصور الدنيا، مثل حاويات Docker، لا توجد خدمة cron أصلاً، فلا أحد هناك ينفّذ المهام المكتوبة في crontab.
كيف تختبر مهمة cron
شغّل الأمر في بيئة تشبه بيئة cron قدر الإمكان:
env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=$HOME /bin/sh -c '/opt/backup.sh'
إذا ظهر خطأ هنا فسيظهر نفسه في cron. ضع مؤقتاً الجدولة * * * * *، ووجّه المخرجات إلى ملف، وتابعه عبر tail -f. ولفحص الجدولة نفسها تفيد المواقع التي تحسب تعبيرات cron، لكن الكلمة الأخيرة لسجل النظام.
من يحق له استخدام cron: cron.allow وcron.deny
يُقيَّد الوصول بالملفين /etc/cron.allow و/etc/cron.deny. إذا وُجد cron.allow فلا يُسمح بالـ crontab إلا للمستخدمين المذكورين فيه. وإذا لم يوجد ووُجد cron.deny فيُسمح للجميع عدا المذكورين فيه. أما المستخدم الممنوع فيحصل عند crontab -e على الرسالة You (bob) are not allowed to use this program.
الخلاصة
الـ crontab سطور فيها خمسة حقول زمنية ثم الأمر: */5 * * * * كل 5 دقائق، و0 3 * * * كل ليلة، و0 9 * * 1-5 أيام العمل. حرّر الجدول عبر crontab -e، واستخدم المسارات المطلقة، واهرب الرمز %، ووجّه المخرجات إلى سجل، واحم المهام الطويلة بـ flock. وتكفي أمثلة crontab المعروضة لمعظم مهام الخوادم، ويُشرح التخطيط والأتمتة عملياً في دورة Linux.
الأسئلة الشائعة
ما الفرق بين cron وanacron؟
يشغّل cron المهام في وقتها بالضبط، ويتخطاها إذا كان الجهاز مطفأً. أما anacron فيضمن تنفيذ المهام اليومية والأسبوعية والشهرية بعد تشغيل الجهاز، لذلك يناسب الحواسيب المحمولة والمكتبية.
أيهما أفضل: cron أم مؤقتات systemd؟
cron أبسط وموجود تقريباً في كل مكان. أما مؤقتات systemd فتعطي سجلات في journalctl، وتبعيات، وتحديد للموارد، وخيار Persistent=true للتشغيلات الفائتة، لذلك تُختار أكثر فأكثر على الخوادم الحديثة للمهام المهمة.
أين يُخزَّن جدول crontab للمستخدم فعلياً؟
في Debian وUbuntu في /var/spool/cron/crontabs/الاسم، وفي RHEL وFedora في /var/spool/cron/الاسم. لا يُنصح بتعديل هذه الملفات مباشرة، واستخدم crontab -e.
كيف أوقف مهمة في crontab مؤقتاً؟
علّق السطر بوضع الرمز # في بدايته عبر crontab -e. ولإيقاف كل مهام المستخدم، احفظ الجدول في ملف بالأمر crontab -l ثم احذفه بالأمر crontab -r.
كيف أشغّل مهمة في آخر يوم من الشهر؟
لا يوجد حقل مخصص لذلك. شغّل المهمة في الأيام من 28 إلى 31 وتحقق من أن الغد هو اليوم الأول: 0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/job.sh.
يُشرح هذا الموضوع بالتفصيل مع التطبيق العملي في دورة «Linux: с нуля до уверенного администрирования».
الانتقال إلى الدورة