شارح تعبيرات cron
يشرح أي تعبير cron بلهجات Vixie وQuartz وEventBridge وJenkins، ويسرد التشغيلات القادمة في أي منطقة زمنية، ويشير إلى المزالق.
قُرئ بلهجة Vixie cron.
ما معناه
في 00:00 في اليوم 13 من الشهر أو يوم الجمعة
حقلاً حقلاً
| الحقل | المكتوب | يطابق |
|---|---|---|
| الدقيقة | 0 | 0 |
| الساعة | 0 | 0 |
| يوم الشهر | 13 | 13 |
| الشهر | * | أي قيمة |
| يوم الأسبوع | 5 | الجمعة |
ما ينبغي الانتباه له
- حقلا اليوم كلاهما مقيد، لذا ينفذ cron هذا عند تطابق أي منهما: يوم الشهر أو يوم الأسبوع، لا كليهما معاً. لهذا يعني 13 مع الجمعة اليوم الثالث عشر وكل جمعة، لا الجمعة الثالث عشر.
التشغيلات القادمة
ما الذي تفعله هذه الأداة
تقرأ لك تعبير cron بالكلمات، وتسرد المرات القادمة التي سيعمل فيها فعلاً في المنطقة الزمنية التي تعمل فيها المهمة، وتسمّي الأشياء التي يلمّح إليها التعبير ولا يقولها. هذا الجزء الأخير هو المقصود: تعبير cron لا يكون خاطئاً أبداً بطريقة تنتج خطأً. إنه يعمل في الأيام الخاطئة، بصمت، إلى أن ينتبه أحد.
تُفهم أربع لهجات لا واحدة. الصيغة ذات الحقول الخمسة التي يقبلها crontab، وQuartz بحقل الثواني وقواعده التقويمية، وAWS EventBridge بسنة في آخره، وJenkins بتجزئته. لصق واحدة منها في أداة لا تعرف إلا غيرها هو الطريق الذي يكتشف به معظم الناس أن هناك أكثر من لهجة.
الحقول، وكم عددها
للتعبير الكلاسيكي خمسة حقول تفصلها مسافات، بترتيب ثابت: الدقيقة، الساعة، يوم الشهر، الشهر، يوم الأسبوع. كل حقل إما نجمة تعني «كل»، أو رقم، أو قائمة مفصولة بفواصل، أو مدى بشَرطة، أو خطوة بشَرطة مائلة. تُكتب الشهور والأيام بأسماء من ثلاثة أحرف، وهو أوضح من الأرقام في معظم الأحيان.
- النجمة تعني أي قيمة يمكن أن يأخذها الحقل.
- مدى مثل 9-17 يعني كل القيم من 9 إلى 17 ضمناً.
- خطوة مثل */15 تعني كل قيمة خامسة عشرة ابتداءً من أسفل الحقل؛ و9-17/2 تخطو داخل مدى، و5/15 تخطو من 5 إلى أعلى الحقل.
- قائمة مثل 0,30 تعني تلك القيم بالضبط.
- مدى منحل مثل 13-13 هو ببساطة 13 — وبعض المكتبات تخطئ هنا وتعامله كنجمة.
الحقل السادس هو الموضع الذي تفترق عنده اللهجات. يضعه Quartz في المقدمة ويقرؤه ثوانيَ؛ ويضعه EventBridge في النهاية ويقرؤه سنةً. ستة حقول عارية مبهمة حقاً إذن، وهذه الأداة تقول كيف قرأتها بدل أن تختار بصمت. ووضع التعبير داخل cron(...) — وهو تركيب EventBridge نفسه — يحسم المسألة.
الماكروهات اختصارات: @hourly و@daily و@midnight و@weekly و@monthly و@yearly و@annually. تتوسع إلى تعبيرات عادية تعرضها الأداة. و@reboot هو الاستثناء وليس جدولةً على الإطلاق.
حقلا اليوم، و«أو» التي تبدو كـ«و»
يمكن اختيار التاريخ بطريقتين مختلفتين — بيوم الشهر وبيوم الأسبوع — ولدى cron الحقلان معاً. وحين يكون كلاهما مقيداً، تعمل المهمة عند تطابق أي منهما. لا كليهما.
لذا فإن 0 0 13 * 5 ليس «الجمعة الثالث عشر». إنه اليوم الثالث عشر من كل شهر، وكل جمعة أيضاً: نحو 64 يوماً في السنة بدل يوم أو يومين. يبدو التعبير عطفاً ويتصرف كفصل، ولا شيء يبلّغ عن خطأ، وتعمل المهمة ببساطة أكثر مما قُصد.
وللقاعدة نصف ثانٍ أغرب، وهو الذي لا يكاد أحد يعرفه. لا يفحص cron ما إذا كان حقل اليوم مقيداً؛ بل يفحص ما إذا كان الحرف الأول من الحقل نجمة. فخطوة مثل */14 تُحسب نجمةً وإن قيّدت الحقل إلى الأول والخامس عشر والتاسع والعشرين — ووجودها يعيد حقلي اليوم من «أو» إلى «و». عندئذ يتصرف تعبيران يبدوان مقيدين بالقدر نفسه على نحوين مختلفين تماماً، وهذه الأداة تبيّن أي القاعدتين سارية.
ويتفادى Quartz وEventBridge المسألة كلها برفض أن يقول الحقلان شيئاً معاً: لا بد أن يكون أحدهما بالضبط علامة استفهام، ومعناها «لا قيمة بعينها، الحقل الآخر هو الذي يقرر».
خطوات لا تقسم حقلها
تُكتب الخطوة كأنها فترة، وداخل دورة واحدة من الحقل هي كذلك بالفعل. أما عبر الحد فغالباً لا.
*/7 في حقل الساعات تعني الساعات 0 و7 و14 و21. وبعد 21 ينتهي الحقل، فيكون التشغيل التالي عند الساعة 0 من اليوم التالي — بعد ثلاث ساعات لا سبع. تنتهي كل دورة بفجوة قصيرة واحدة، ويحدث الشيء نفسه مع */7 في الدقائق (فجوة أربع دقائق بعد 56) ومع أي خطوة أخرى لا تقسم مداها قسمة تامة. و*/15 في الدقائق و*/6 في الساعات آمنتان؛ أما معظم الأرقام التي تتبادر إلى الذهن فلا.
ويهم هذا حين تُختار الخطوة لتوزيع العمل. فمهمة على */7 ساعات لا تعمل كل سبع ساعات، وحدّ معدل مصمم على سبع سيُخترق مرة كل يوم.
أيام الأسبوع تُرقَّم بطرق مختلفة في أماكن مختلفة
يرقّم Vixie cron الأيام من 0 إلى 7 والأحد على الطرفين، فيكون 0 و7 اليوم نفسه ويكون 1 هو الاثنين. أما Quartz وEventBridge فيرقمانها من 1 إلى 7 والأحد عند 1، فيكون 1 هناك هو الأحد و2 هو الاثنين.
ولذلك يعمل تعبير مَنقول من إعداد Quartz إلى crontab قبل موعده بيوم، ولا يشتكي شيء في أي مكان، لأن الكتابتين رقمان صحيحان في النظامين. والأسماء الثلاثية — MON وFRI — تعني اليوم نفسه في كل مكان، وهي الحماية البسيطة.
التوقيت الصيفي: صباحان في السنة
خدمة cron لا تجدول لحظات. إنها تنظر إلى ساعة الحائط المحلية كل دقيقة وتسأل هل يطابق التعبير. ومرتين في السنة لا تكون تلك الساعة متتالية مهذبة.
حين تقفز إلى الأمام، لا تحدث ساعة كاملة من القراءات أبداً. فمهمة مضبوطة على 02:30 ليس لها 02:30 في ذلك اليوم. وحين تعود إلى الوراء، تحدث ساعة من القراءات مرتين، ومهمة مضبوطة على 01:30 لها اثنتان منها.
ويميّز Vixie cron هنا بين نوعين من المهام، والتمييز جدير بالمعرفة لأنه هو الذي يحدد أي مهامك تتأثر. فالمهمة المثبّتة على ساعة حائط — ساعة ثابتة ودقيقة ثابتة — تُنفَّذ مرة واحدة بالضبط في الحالتين: بعد القفزة إلى الأمام تعمل فوراً، وبعد العودة إلى الوراء يحرص cron على ألا يكررها. أما المهمة التي فيها نجمة في الساعة أو الدقيقة فهي مهمة فترية تتبع الساعة ببساطة: تفقد ساعة من التشغيلات في الربيع وتكرر ساعة في الخريف.
ولذا فإن نسخة احتياطية كل ساعة تعمل فعلاً مرتين في ليلة واحدة من السنة، ومهمة ليلية عند 02:30 تعمل فعلاً في لحظة غير معتادة في ليلة أخرى. وهذه الأداة تقارن الأشهر الاثني عشر القادمة من المنطقة المختارة بتعبيرك وتقول أي الحالتين تنطبق، مع التاريخ.
والمخرج الموثوق هو جدولة المهمة بتوقيت UTC، أو خارج النطاق المحلي من 01:00 إلى 03:00 حيث لا يقع أي تحول.
على أي ساعة تقع المهمة فعلاً
يعمل ملف crontab على المنطقة المحلية للجهاز، أو على ما يقوله CRON_TZ أو TZ في أعلى الملف. ونادراً ما تكون تلك منطقة من يقرأ التعبير، ولهذا فإن المنطقة الزمنية هنا إعداد لا منطقة متصفحك.
أما CronJob في Kubernetes فشيء آخر: يقرأ جدولته بتوقيت UTC ما لم يحدد الملف حقل timeZone، مهما قالت ساعة العقدة. وجدولة كُتبت لساعات العمل المحلية ونُشرت بلا ذلك الحقل تعمل في الوقت الخاطئ من اليوم في كل مكان خارج UTC — ولا تتغير مع الفصول، وهو خلل أو راحة بحسب المهمة.
Jenkins والرمز H
يضيف Jenkins رمزاً لا تملكه أي لهجة أخرى: H، الذي يبدو عشوائياً وليس كذلك. يجزّئ Jenkins اسم المهمة إلى قيمة ثابتة داخل مدى الحقل، فتعمل المهمة في الوقت نفسه كل يوم — وقتها هي — بينما تتوزع مئة مهمة كُتبت H * * * * توزعاً منتظماً على مدار الساعة بدل أن تنطلق كلها عند الدقيقة صفر.
ولأن القيمة تأتي من اسم المهمة، والاسم ليس جزءاً من التعبير، لا تستطيع أي أداة حساب الأوقات الدقيقة. وهذه الأداة تحل كل H إلى أسفل مداه وتقول ذلك: شكل الجدولة — كل كم، وفي أي أيام — دقيق، وحده الإزاحة داخل كل فترة هي البديل المؤقت.
ويعيد Jenkins أيضاً تعريف الماكروهات. فـ@daily عنده هو @midnight، وهو ليس منتصف الليل بل دقيقة مجزّأة في مكان ما من الساعات الثلاث الأولى من اليوم.
ما لا يفعله cron
- لا يعوّض ما فات. إذا كان الجهاز نائماً أو الخدمة متوقفة حين حان موعد تشغيل، فذلك التشغيل لا يحدث لاحقاً — إنه يفوت ببساطة. وanacron موجود لهذا بالضبط وهو برنامج آخر.
- لا يمنع التشغيلات المتداخلة. إذا استغرقت المهمة أطول من الفترة، تبدأ التالية على أي حال، وبعد قليل تصير عدة مهام تعمل معاً. وملف القفل هو الجواب المعتاد.
- لا مفهوم للثواني في الصيغة الكلاسيكية. أدق تحبيب هو دقيقة واحدة؛ وأي شيء أسرع يحتاج Quartz أو systemd أو حلقة داخل المهمة نفسها.
- @reboot ليس جدولة. يعمل مرة واحدة عند بدء الخدمة، فليس له تشغيل قادم، وعلى جهاز لا يُعاد تشغيله أبداً لا يعمل إطلاقاً.
- تعبير rate في EventBridge يُحسب من لحظة إنشاء القاعدة، وتلك اللحظة ليست في التعبير، فلا تستطيع أي أداة أن تقول متى يعمل في المرة القادمة.
الأسئلة الشائعة
- كيف أشغّل مهمة في أول اثنين من الشهر؟
- ليس بتعبير cron عادي: فذلك يحتاج «و» بين حقلي اليوم، وcron يعطي «أو». والالتفاف المعتاد هو جدولة 0 0 1-7 * * وترك الأمر نفسه يفحص يوم الأسبوع. أما Quartz وEventBridge فيعبّران عنه مباشرة بالصيغة 0 0 12 ? * MON#1.
- لماذا يعمل 0 0 13 * 5 بهذه الكثرة؟
- لأن معناه اليوم الثالث عشر أو كل جمعة، لا «الجمعة الثالث عشر». فحين يكون حقلا اليوم مقيدين ولا يبدأ أي منهما بنجمة، ينفذ cron المهمة عند تطابق أي منهما. ولا سبيل لكتابة «و» في اللهجة الكلاسيكية.
- ما الفرق بين */5 و0-59/5؟
- لا فرق في حقل الدقائق: كلاهما يعطي 0 و5 و10 وهكذا. يظهر الفرق في حقلي اليوم، حيث يحسم cron بين «و» و«أو» بالنظر إلى ما إذا كان الحقل يبدأ بنجمة — فـ*/5 و0-59/5 يختاران الأيام نفسها لكنهما يتحدان مع حقل اليوم الآخر على نحو مختلف.
- هل تعمل مهمتي مرتين حين تعود الساعة إلى الوراء؟
- إن كان فيها نجمة في الساعة أو الدقيقة فنعم: هي تتبع الساعة، وتلك الساعة تقع مرتين. وإن كانت لها ساعة ودقيقة ثابتتان فلا — ينفذ Vixie cron مهمة كهذه مرة واحدة. ولوحة الملاحظات تقول أي الحالتين حالتك، مع التاريخ الذي تنطبق فيه.
- أي منطقة زمنية يستخدمها تعبير cron؟
- منطقة الجهاز، ما لم يكن CRON_TZ أو TZ محدداً في ملف crontab. ويستخدم CronJob في Kubernetes توقيت UTC ما لم يحدد الملف timeZone. وبما أن المنطقة ليست جزءاً من التعبير، تسأل هذه الأداة عنها بدل أن تفترض منطقة متصفحك.
- هل يمكنني جدولة شيء كل 90 دقيقة؟
- ليس بتعبير واحد، لأن كل حقل يدور بنفسه و90 دقيقة لا تستقيم داخل ساعة. والكتابة المعتادة تعبيران: 0 0,3,6,9,12,15,18,21 * * * و30 1,4,7,10,13,16,19,22 * * *، وهما معاً يعطيان تشغيلاً كل 90 دقيقة.
- هل يُرسل شيء مما ألصقه إلى خادم؟
- لا. التحليل والشرح وحساب الجدولة كله يجري في متصفحك؛ لا شيء يُرفع أو يُسجّل، وهي تعمل بلا اتصال بالشبكة.