اذهب إلى المحتوى

رشا سعد

الأعضاء
  • المساهمات

    24
  • تاريخ الانضمام

  • تاريخ آخر زيارة

5 متابعين

المعلومات الشخصية

  • النبذة الشخصية
    مهندس اتصالات من سوريا

آخر الزوار

1929 زيارة للملف الشخصي

إنجازات رشا سعد

عضو نشيط

عضو نشيط (3/3)

11

السمعة بالموقع

  1. سنناقش بهذا المقال الخيارات المتنوعة للاتصال بخادم MongoDB، سواءً كان يتضمن قاعدة بيانات وحيدة، أو عدة قواعد بيانات تتمتع كل منها بمَنْفَذٍ خاص. سنعرض أيضًا طريقة الاتصال مع خوادم تعمل على أجهزة متعددة ضمن أمر اتصال واحد، لكن البداية ستكون من تشغيل خادم MongoDB. تشغيل خادم MongoDB يمكن تشغيل خادم MongoDB من الملف التنفيذي mongod الموجود في المجلد bin ضمن المجلد الخاص بملفات برنامج MongoDB، حيث بعد تنفيذ الأمر mongod، لن تظهر أي معلومات جديدة على شاشة موجه الأوامر لبعض الوقت حتى يتأسس الاتصال مع خادم MongoDB. وبمجرد إنشاء الاتصال، سيبدأ سجل معلومات الخادم بالظهور. يمكن الاتصال مع خادم MongoDB بواسطة الصدفة MongoDB Shell، وعند بدء التشغيل سيكون المنفذ 27017 هو المنفذ الافتراضي للاتصال بخادم MongoDB؛ أما إذا أردنا الوصول إلى الخادم من واجهة الويب فيمكننا ذلك باستخدام المنفذ الناتج عن إضافة 1000 إلى رقم منفذ بدء التشغيل، فنكتب عندها الرابط http://localhost:28017 في متصفحنا. الاتصال بخادم MongoDB من الصدفة Shell يبين السطر أبسط صيغة لكتابة أمر الاتصال مع خادم MongoDB بعد أن شغلناه، علمًا أننا اكتفينا بخيار واحد فقط من خيارات الاتصال المتعددة، وهو خيار لا غنى عنه؛ إذ إنه يشير إلى اسم الحاسوب المضيف hostname، وهو في حالتنا localhost: mongodb://localhost وبمجرد تنفيذ الأمر سنحصل على الخرج التالي: إذا عُدنا الآن إلى نافذة موجه الأوامر التي شغلنا فيها خادم MongoDB، وهو نفسه الملف التنفيذي mongod، سنجد أنها تتضمن خرجًا يشبه التالي: يمكننا ملاحظة السطر الأخير، فهو يشير إلى إنشاء اتصال جديد مع خادم MongoDB ويتضمن معلومات عن الجهة المتصلة. ملاحظة: سيظهر هذا السطر عندما ينجح اتصالنا مع الخادم صيغ متنوعة لأمر الاتصال مع خادم MongoDB يمكن ذكر الصيغ الآتية لأوامر الاتصال مع خادم MongoDB الاتصال بقاعدة البيانات الافتراضية باستخدام اسم مستخدم وكلمة مرور يمكننا الاتصال مع خادم MongoDB بواسطة اسم مستخدم وكلمة مرور محددين وفق الصيغة العامة username@hostname/dbname، بحيث نعدل اسم المستخدم username إلى اسم المستخدم الذي نود الاتصال بواسطته مع كلمة المرور الخاصة به، كما نبدل اسم الحاسوب المضيف hostname إلى اسم الحاسوب المضيف الفعلي، و dbname إلى اسم قاعدة البيانات؛ وإذا أردنا الاتصال بقاعدة البيانات الافتراضية، يكفي أن نترك اسم قاعدة البيانات فارغ، كما هو الحال في المثال التالي: mongodb://mongo_admin:AxB6_w3r@localhost/ ولتوضيح الشيفرة أعلاه، يشير mongo_admin إلى اسم المستخدم، و AxB6_w3r إلى كلمة المرور الخاصة به؛ وهو يحاول الاتصال بقاعدة البيانات الافتراضية الموجودة على الحاسوب المحلي localhost. وسيكون الخرج كما يلي: الاتصال بقاعدة بيانات معينة باستخدام اسم مستخدم وكلمة مرور سنستخدم الصيغة العامة السابقة نفسها username@hostname/dbname مع تعديل كلمة dbname إلى اسم قاعدة البيانات التي نريد بالاتصال بها، وفق التالي: mongodb://mongo_admin:AxB6_w3r@localhost/w3r وباستخدام هذا الأمر سيتصل المستخدم mongo_admin صاحب كلمة المرور AxB6_w3r بقاعدة البيانات w3r الموجودة على الحاسوب المحلي، وسيكون الخرج كما يلي: وتجدر الإشارة هنا إلى أننا تستطيع الاتصال بأكثر من مُضيف hostname ضمن أمر اتصال واحد. الاتصال بقاعدة بيانات معينة باستخدام اسم مستخدم وكلمة مرور ومَنفَذ معين يجري هذا الاتصال بطريقة مشابهة للسابق، بحيث نستخدم الصيغة username@hostname/dbname ونضع اسم المستخدم وكلمة المرور؛ وبدلًا من dbname، نكتب اسم قاعدة البيانات التي نريد الاتصال بها، يتبعها رقم المَنفَذ port الذي حددناه للاتصال معها، كما هو الحال في المثال التالي: mongodb://mongo_admin:AxB6_w3r@localhost/w3r:29000 بمعنى أن المستخدم mongo_admin الذي كلمة مروره AxB6_w3r سيحاول الاتصال بقاعدة البيانات w3r الموجودة على الحاسوب المحلي، وذلك عبر المنفذ 29000. وفي الحالات التي لا نذكر فيها رقم منفذ محدد، سيجري الاتصال عبر المنفذ الافتراضي 27107، بحيث سيكون خرج الأمر كما يلي: الاتصال بخوادم MongoDB متعددة تعمل على أجهزة متعددة تعمل حالة الاتصال بخوادم MongoDB على أجهزة متعددة عند استخدام مجموعات النسخ المتماثلة replica sets، التي تتألف من عدة عقد تُعدّ نسخًا متطابقة عن بعضها؛ إذ تكتشف المجموعة العقدة الرئيسية Master node تلقائيًا وفي حال فشلت، فإن العقد الثانوية تنوب عنها، وتساعدنا على استرداد النظام من حالات الفشل. في المثال التالي صيغة الأمر المناسب لهذا النوع من الاتصال: mongodb://example_host1.com:27017,example_host2.com:27017 الاتصال بخوادم MongoDB متعددة تعمل على الجهاز نفسه ولكل منها منفذ خاص سنستخدم الأمر التالي للاتصال بخوادم MongoDB المتعددة التي تعمل على الجهاز نفسه، لكن على منافذ ports مختلفة. سيفيدنا هذا أيضًا في مجموعات النسخ المتماثلة replica sets: mongodb://example_host1.com:27110,example_host1.com:27111 الخيارات المرتبطة بالصيغ السابقة يبين الجدول التالي الخيارات التي يمكننا استخدامها مع صيغ أوامر الاتصال السابقة: الخيار Option الوصف replicaSet=name عند العمل مع أي لغة برمجة يدعمها MongoDB فإن مُشَغِّل driver لهذه اللغة سيستخدم اسم مجموعة النسخ المتماثلة ليعرف العقد التي تنتمي إليها؛ علمًا أن مُشَغِّل لغة البرمجة أو driver هو جزء التعليمات البرمجية المسؤول عن الاتصال بين لغة برمجة التطبيق ونظام MongoDB. slaveOk=true|false‎ يستخدم هذا الخيار للتعامل مع حالتي الكتابة والقراءة في أنظمة النسخ المتماثلة ذات الخوادم المتعددة، فعند الكتابة تُرسل البيانات إلى الخادم الرئيسي primary وعند القراءة تُرسل البيانات إلى جميع الخوادم الاحتياطية أو الثانوية slaves. safe=true|false إذا كانت قيمة safe هي true، فإن مُشَغِّل لغة البرمجة سيُرسل الأمر getLastError بعد تحديث للبيانات ليتأكد من إتمامه بدون أخطاء؛ أما إذا كانت قيمته false، فلن يُرسل المشغل getLastError بعد التحديثات. w=n يُضاف { w : n } إلى الأمر getLastError الذي يرسله مُشَغِّل لغة البرمجة، وهو يفرض بطبيعة الحال إسناد القيمة true للخيار safe=true. wtimeoutMS=ms يُضاف { wtimeout : ms } إلى الأمر getLastError الذي يرسله مُشَغِّل لغة البرمجة، وهو يعني حكمًا أن safe=true. fsync=true|false إذا كانت قيمة هذا الخيار هي true، فسيُضاف الخيار { wtimeout : ms } إلى الأمر getLastError الذي يُرسله مُشَغِّل لغة البرمجة، وهذا يفرض بالطبع أن تكون safe=true؛ أما إذا أعطينا fsync القيمة false، فإن الأمر getLastError لن يُرسل أبدًا. journal=true|false إذا أُعطيت القيمة true لهذا الخيار فستجري مزامنة مع يومية قاعدة البيانات journal التي تتضمن عادةً إدخالات قاعدة البيانات التي لم تُثَبَّت بعد uncommitted، وهذه الحالة تفرض دائمًا إسناد القيمة true للخيار safe=true. connectTimeoutMS=ms يُحدد الزمن الأقصى لمحاولة الاتصال مع خادم MongoDB قبل تصنيفه على أنه اتصال غير ممكن. socketTimeoutMS=ms يحدد الزمن الأقصى لمحاولة الإرسال أو الاستقبال على مِقبس التوصيل socket قبل إنهاء المحاولة. خاتمة تعرفنا في هذا المقال على الطرق المتنوعة للاتصال مع خادم MongoDB عبر الصدفة mongo shell. وسواءٌ كان ذلك على مُضيف واحد أو على مضيفين متعددين، يرتكز الاتصال على تحديد أربع أمور أساسية هي بيانات المستخدم الذي سيُنجِز الاتصال، واسم قاعدة البيانات، والمَنْفَذ الخاص بها، واسم المُضيف؛ ويمكننا استخدام الخيارات التي عرضناها في الجدول حسب ما تقتضيه طبيعة مشروعنا. ترجمة -وبتصرف- لمقال MongoDB connections من موقع w3resource. اقرأ أيضًا طريقة تثبيت MongoDB على ويندوز ولينكس كيفية تثبيت وتأمين MongoDB على أوبونتو 18.04 كيفية إنشاء واستخدام النسخ الاحتياطي لقواعد بيانات MongoDB على نظام أوبنتو دليلك الشامل إلى قواعد البيانات DataBase
  2. سنعرض في هذا المقال طريقة تثبيت MongoDB على نظامي تشغيل ويندوز ولينكس، مع تمهيد بسيط لبدء العمل مع الصدفة mongo shell التي تمكننا من التفاعل مع قاعدة البيانات. التحميل Download يمكن تحميل ذ الثنائية المُعدّة مسبقًا Pre-built binary packages مباشرةً من موقعها الرسمي، لذا ما علينا سوى اختيار النسخة المناسبة ورقم الإصدار، ونظام التشغيل ويندوز أم لينكس، ونوعه 32 بت أم 64 بت؛ وعند انتهاء التحميل سيكون من السهل تثبيتها كما سنعرض في الفقرات التالية. فك الضغط Unzip الخطوة التالية بعد تحميل حزمة MongoDB هي فك ضغطها إلى المجلد المراد تثبيت MongoDB ضمنه؛ وهنا سننشئ مجلدًا خاصًا لهذا الغرض، وسيكون اسمه في مثالنا هو mongodb. إنشاء مجلد بيانات MongoDB سننشئ أيضًا مجلدًا خاصًا بالبيانات نسميه data، ثم ننشئ بداخله مجلدًا آخر يسمى db، وهذا هو الاسم الافتراضي للمجلد الذي تخزن فيه MongoDB البيانات، ولأن هذا المجلد لا ينشأ تلقائيًا عند تثبيت MongoDB، فقد أنشأناه يدويًا. ينبغي إنشاء المجلد data على الجذر مباشرةً، أي على (\:C أو \:D أو نحو ذلك) في ويندوز، وعلى (/) في لينكس، كما يلي: إنشاء المجلدات في ويندوز (أنشأنا المجلدات على القرص \:D في مثالنا): إنشاء المجلدات في لينكس: يمكن إنشاء المجلدات في كلا النظامين من الواجهة الرسومية ومستعرض الملفات في حال الرغبة بذلك من دون الحاجة لموجه الأوامر. تشغيل خادم MongoDB من موجه الأوامر لتشغيل خادم MongoDB من موجه الأوامر، لا بد من فتح المجلد mongodb الذي أجرينا فيه عملية فك الضغط سابقًا، ثم نفتح المجلد الفرعي ضمنه bin، ونشَغِّل الملف التنفيذي mongod.exe. تشغيل خادم MongoDB كخدمة ويندوز سنحتاج لتنفيذ الأمر التالي بصلاحيات مدير نظام administrator لتشغيل خادم MongoDB كخدمة ويندوز: mongod --bind_ip yourIPadress --logpath "C:\data\dbConf\mongodb.log" --logappend --dbpath "C:\data\db" --port yourPortNumber --serviceName "YourServiceName" --serviceDisplayName "YourServiceName" --install لا ننسَ ضبط قيم الوسطاء Arguments ضمن الأمر بما يناسب حالتنا كما هو موضح في الجدول التالي: الوسيط Argument الوصف ‎--bind_ip يُكتب بعده عنوان IP الخاص بالحاسوب الذي نستخدمه ‎--logpath يليه مسار تخزين ملف تسجيل الأحداث logfile لخادم MongoDB ‎--logappend يُفَعِّل هذا الوسيط كتابة سجلات الأحداث logs التي تتولد ضمن ملف تسجيل الأحداث logfile الذي حددناه بالوسيط السابق ‎--dbpath يُحَدَّدْ بعده مسار وجود المجلد db ضمن المجلد data ‎--port يُكتب بعده المنفذ port الذي خصصناه لخادم MongoDB، وإذا لم نضع أي قيمة هنا، فإن MongoDB سيستخدم المنفذ الافتراضي وهو 20127 ‎--serviceName يوضع بعده اسم خدمة MongoDB الذي اخترناه ‎--serviceDisplayName يُكتب بعده اسم عرض الخدمة أو اسم الإظهار، فلكل خدمة ويندوز اسمين، ويكون هذا الاسم عادةً أوضح ويعبر أكثر عن طبيعة الخدمة. تتضح فائدة هذا الوسيط عندما يكون لدينا أكثر من خدمة MongoDB مُشغلة على نفس الحاسب install-- يشير هذا الوسيط إلى أمر تثبيت أو إنشاء MongoDB كخدمة من خدمات ويندوز تمهيد لاستخدام الصدفة Shell الخاصة بنظام MongoDB لا بد لنا من الحرص على العمل من مستخدم يملك صلاحيات مدير نظام، ثم نشَغَّل الملف التنفيذي mongo.exe الموجود ضمن المجلد bin، أحد المجلدات الفرعية للمجلد الذي ثَبَّتنا فيه MongoDB، وبمجرد تشغيله ستفتح أمامنا الصدفة shell الخاصة بإدارة نظام MongoDB، وهي من نوع JavaScript shell، كما في الصورة التالية: يمكننا ملاحظة أن MongoDB قد اتصل مباشرةً بقاعدة بيانات مستندية document database تجريبية تدعى test. يوفر MongoDB لمستخدميه إمكانية الاتصال المباشر بهذا المستند التجريبي أو قاعدة البيانات التجريبية بعد التثبيت. سنجري الآن بعض العمليات البسيطة لاختبار الصدفة shell، ونكتب مثلًا عمليةً حسابيةً طالما أن الصدفة مكتوبة بلغة JavaScript، ويمكن أيضًا استخدام الأمر db فهو يعطينا اسم قاعدة البيانات المتصل بها: لنجرب الآن إدخال سجل بسيط وإرجاع بعض المعلومات منه: أدخلنا في السطر الأول القيمة 8 إلى الحقل z التابع للمجموعة w3r، ثم استعلمنا عن البيانات؛ علمًا أن المجموعات collections في MongoDB تكافئ الجداول في قواعد البيانات العلائقية. واجهة الويب لنظام MongoDB يمكن الوصول إلى واجهة الويب الخاصة بإدارة MongoDB ومراقبته عبر منفذٍ port خاص، يكون رقمه هو نفسه رقم المنفذ المخصص لخادم MongoDB مضافًا إليه 1000. فعلى سبيل المثال، إذا كنا نستخدم المنفذ الافتراضي لخادم MongoDB وهو 27017 فمعنى ذلك أنه يمكن الوصول إلى واجهة الويب من المنفذ 28017. الخاتمة اطلعنا في هذا المقال على خطوات تثبيت MongoDB على نظامي تشغيل ويندوز ولينكس وهي خطوات بسيطة ومتشابهة، ثم تعرفنا على أمر تشغيل خادم MongoDB كخدمة ويندوز وطريقة تعديله ليناسب بيئتك، ووضحنا آلية التعامل مع صفة MongoDB. ترجمة -وبتصرف- لمقال Install MongoDB on Windows ومقال Install MongoDB on Linux من موقع w3resource. اقرأ أيضًا كيفية تثبيت وتأمين MongoDB على أوبونتو 18.04 كيفية إنشاء واستخدام النسخ الاحتياطي لقواعد بيانات MongoDB على نظام أوبنتو دليلك الشامل إلى قواعد البيانات DataBase
  3. أَحدَثَت مشاريع الحوسبة السحابية الأصيلة Cloud-native Computing تغييرًا كبيرًا في طريقة بناء التطبيقات ونشرها، ووفرت وسائل متنوعة تُسَهِّل العمل، بدايةً من أدوات التكامل Integrating وتحزيم Packaging شيفرة التطبيق تمهيدًا لنشره، ووصولًا إلى أدوات التوسعة Scaling وغير ذلك. تندرج هذه الأعمال جميعها تحت مظلة DevOps وتُعدّ الحاويات في عصرنا الحالي أشهر مفاهيم DevOps الحديث وأكثرها تأثيرًا، وسنعرض في مقالنا هذا مثالًا عمليًّا عن نشر تطبيق ريآكت React بمساعدة الحاويات؛ إذ سننشئ صورة دوكر Docker للتطبيق ثم نرفعها إلى سجل الحاويات Container Registry وننشرها باستخدام خادم Droplet من منصة DigitalOcean. متطلبات العمل من أجل إنشاء حاوية دوكر لتطبيق ريآكت ورفعها إلى سجل الحاويات في DigitalOcean، سنحتاج إلى المتطلبات التالية: إنشاء حساب على منصة DigitalOcean. تثبيت كل من دوكر Docker ومدير الحزم npm على جهازننا تشغيل خادم Droplet على DigitalOcean إعداد سجل الحاويات على DigitalOcean تثبيت Docker تساعدنا توثيقات دوكر وهذا الدليل على تثبيت دوكر على الجهاز. وعند إتمام التثبيت، سنستخدم الأمر docker --version للاستعلام عن إصدار دوكر والتحقق من نجاح تثبيته، وذلك وفق التالي: docker --version Docker version 26.0.0, build 2ae903e إنشاء تطبيق ريآكت خطوتنا التالية هي تشغيل تطبيق ريآكت React، فإذا كان التطبيق الذي نخطط لاستخدامه موجودًا على الجهاز المحلي، فلا بد من تشغيله مباشرةً. وإذا كان موجودًا على GitHub، فيمكن استنساخ مستودع التطبيق إلى الجهاز مباشرةً؛ أما في حال لم نكن نمتلك تطبيقًا، فلا بد من إنشاء واحد باستخدام Vite كما يلي: npm create vite@latest react-app -- --template react سيُنشئ هذا الأمر تطبيقًا تجريبيًّا ضمن المجلد react-app، وبعدها يمكن الانتقال للمجلد وتشغيل التطبيق بواسطة الأوامر التالية: cd react-app npm install npm run dev وعند الانتهاء من التشغيل سنحصل على رسالة شبيهة بالتالي: OutputVITE v5.2.11 ready in 712 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help يمكن الآن فتح التطبيق من المتصفح بكتابة العنوان localhost:5173 كما يلي: بهذا يكون تطبيقنا قد أصبح جاهزًا الآن، ونستطيع الانتقال للخطوة التالية لنبدأ بتحزيمه ضمن صورة Docker، ثم نشره في بيئة الإنتاج. إنشاء الملف Dockerfile يساعدنا دوكر Docker على تغليف شيفرة تطبيقنا ضمن حاوية وتشغيله، وذلك باستخدام صور دوكر وحاويات دوكر. تُنشَئ صور دوكر باستخدام ملف خاص يدعى Dockerfile يتضمن التعليمات البرمجية اللازمة لبناء الصورة؛ إذ تتكون الصورة image من عدة طبقات layers للقراءة فقط. وعند إضافة أي تعليمة إلى الملف Dockerfile، تُضَاف طبقة جديدة إلى الصورة. تُخَزَّن هذه الطبقات بهيئة قيم مُعَمَّاة hash من نوع SHA-256، ويمكن معرفة المزيد عن بنية صور الحاويات بمطالعة مقال ما هي صورة الحاوية container image؟ إذًا تُبنى صور دوكر بإنشاء الملف Dockerfile ويشمل ذلك خطوتين أساسيتين: بناء التطبيق إعداد خادم nginx وتشغيله لخدمة التطبيق تنويه: ستُكتَب جميع التعليمات التي ننفذها في هاتين الخطوتين ضمن الملف Dockerfile. 1. بناء التطبيق سنوجه Docker في البداية ليستخدم أحدث نسخة من Node.js لتكون الركيزة التي سيبني عليها تطبيق React وفق التالي: FROM node:latest AS builder بعد ذلك سنحَدِّد مجلد العمل WORKDIR الذي ستُنَفَّذ فيه جميع التعليمات اللاحقة، والذي سيكتمل فيه تطبيقنا: WORKDIR /app سننسخ بعدها الملف package.json إلى الحاوية builder ونشَغِّل npm install لتثبيت جميع الاعتماديات المطلوبة في الملف package.json وبذلك نضمن أن تطبيق React يمتلك كل ما يحتاجه ليعمل بصورة سليمة: COPY package.json . RUN npm install والآن. يمكن نسخ بقية ملفات المشروع إلى الحاوية وتشغيل npm run build لتصريف compile التطبيق وفق الأمر التالي الذي سيُحَوِّل الشيفرة المصدرية لتطبيقنا إلى حزمة bundle جاهزة لبيئة الإنتاج ومُخَزَّنة في المجلد /dist ومُضبوطة لتعمل بكفاءة وأداء جيد: COPY . ./ RUN npm run build 2. إعداد خادم nginx للتطبيق وتشغيله يشتهر NGINX بالكفاءة والسرعة، لذا يُعدّ خيارًا مناسبًا للاستخدام كخادم ويب لتطبيقنا حتى يصله بالمستخدمين، وسنعتمد لبنائه أحدث صورة nginx متوفرة؛ ثم نستبدل ملف الإعدادات الافتراضي الذي يأتي مع الصورة default.conf بملف إعدادات مشروعنا ليتمكن خادم nginx من تلبية طلبات المستخدمين الواردة إلى التطبيق. وذلك وفق الأوامر التالية: FROM nginx:latest RUN rm -rf /etc/nginx/conf.d/default.conf COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf سننسخ بعد ذلك تطبيق ريآكت الذي حوّلناه إلى حزمة في الخطوة السابقة، وذلك من المجلد dist في الحاوية builder إلى المجلد الذي يتوقع nginx أن يجد ملفات تطبيقنا فيه، ليستطيع خدمة المستخدمين، وذلك وفق الأمر التالي: COPY --from=builder /app/dist /usr/share/nginx/html سنضبط بعد ذلك قيمة مجلد العمل WORKDIR داخل حاوية nginx ليشير إلى مجلد التطبيق: WORKDIR /usr/share/nginx/html ثم نشَغِّل خادم nginx بواسطة الأمر التالي، الذي يوجه Docker لبدء تشغيله والحفاظ عليه في حالة عمل: CMD ["/bin/bash", "-c", "nginx -g "daemon off;""] إذا جمعنا كل التعليمات السابقة معًا، فستشكل الملف Dockerfile: # ------------------------ # Step 1: Build react app # ------------------------ # Use node:latest as the builder image FROM node:latest AS builder # Set the working directory WORKDIR /app # Copy package.json and install app dependencies COPY package.json . RUN npm install # Copy other project files and build COPY . ./ RUN npm run build # -------------------------------------- # Step 2: Set up nginx to serve the app # -------------------------------------- # Use nginx:latest as the base image FROM nginx:latest # Overwriting nginx config with our own config file RUN rm -rf /etc/nginx/conf.d/default.conf COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf # Copy over the build created in the Step 1 COPY --from=builder /app/dist /usr/share/nginx/html # Set the working directory WORKDIR /usr/share/nginx/html # Start nginx server CMD ["/bin/bash", "-c", "nginx -g \"daemon off;\""] لذا سننشئ ملفًا نصيًّا باسم Dockerfile ضمن المجلد الجذر لتطبيقنا كما يلي: touch Dockerfile مع نسخ التعليمات السابقة ضمنه. إنشاء ملف إعدادات NGINX ذكرنا في الخطوة السابقة الخاصة بإعداد خادم NGINX أننا سنستبدل ملف الإعدادات الافتراضي للخادم بملف إعدادات مشروعنا، فكيف ننشئ ملف إعدادات المشروع؟ سنتوجه في البداية إلى مجلد الجذر لتطبيقنا ثم ننشئ بداخله مجلدًا جديدًا باسم nginx، وننشئ بداخله ملفًا نصيًّا يدعى default.conf كما يلي: mkdir nginx cd nginx && touch default.conf ثم ننسخ الإعدادات التالية والصقها ضمنه: server { listen 80; add_header Cache-Control no-cache; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; expires -1; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } تُحَدِّد هذه الإعدادات البوابة التي سيستقبل خادم nginx الطلبات عبرها، وهي البوابة 80، وأيضًا الملفات التي سيتعامل معها، بالإضافة إلى الأخطاء الافتراضية التي قد تعترضه مصنفةً حسب رمز الخطأ. وفي هذا المجال، يُنصح بالاطلاع على مقال فهم بنية ملف إعدادات nginx وسياقات الإعدادات، كما توفر DigitalOcean لمستخدميها أداة رسومية تساعدهم في إنشاء ملف إعدادات nginx. بناء صورة التطبيق image سنستخدم هننا تعليمة cd للعودة إلى مجلد الجذر لتطبيقنا حيث يوجد الملف Dockerfile، ثم ننفِّذ الأمر التالي: docker image build -t react-app:v1.0 . --platform linux/amd64 تتميز كل صورة دوكر عن غيرها باسم name ووسم tag خاصين بها يُكتَبان بعد الراية t-؛ فالصورة في مثالنا تدعى react-app، ولها الوسم v1.0؛ أما الراية platform-- فتساعدنا على تحديد المنصة التي تُبنى الصور من أجلها، وتُعَدُّ خيارًا مهمًا في الحالات التي نحتاج فيها لبناء صورة متعددة المنصات cross-platform حتى تُحَِّدد الأنظمة التي ستتوافق معها. سنحصل على نتيجة تشبه التالي بعد الانتهاء من بناء الصورة: [+] Building 113.9s (17/17) FINISHED => [internal] load build definition from Dockerfile => => transferring dockerfile: 982B ... ... => => writing image sha256:d125b102b094224c82ddb69f9ece98c7161c8660fe72fd5aec57e41a6d72cf2f => => naming to docker.io/library/react-app:v1.0 يمكن استخدام الأمر التالي الذي يعرض صور دوكر الموجودة لتتحقق من صحة بناء الصورة: docker image list وسنحصل على النتيجة التالية: OutputREPOSITORY TAG IMAGE ID CREATED SIZE react-app v1.0 d125b102b094 6 minutes ago 188MB وكما نلاحظ، فصورة التطبيق التي بنيناها ضمن قائمة الصور الموجودة. وبهذا يمكن القول أن إذًا صورة التطبيق قد أصبحت جاهزةً للنشر والتوزيع، والمغزى الأساسي من استخدام الصور هو أن الصورة تتمتع بالاكتفاء الذاتي، إذ تتضمن كل ما نحتاجه للعمل بسلاسة في أي منصة تدعم دوكر ومن دون أي متطلبات إضافية. لننتقل الآن إلى الخطوة التالية وهي تخزين الصورة في مكان مركزي يُسَهِّل توزيعها ونشرها، وهنا يأتي دور سجل الحاويات Container Registry. رفع صورة التطبيق إلى سجل الحاويات سجل الحاويات Container Registry هو مخزون مركزي من صور الحاويات، يمكن رفع صورنا إليه وسحبها منه عندما نحتاجها من أي مكان وفي أي وقت، وذلك بعدة طرق. إما محليًّا، أو عبر عنقود Kubernetes، أو عبر خطوط أنابيب CI/CD، أو غير ذلك. لنبدأ الآن خطوات رفع push الصورة react-app إلى السجل. لرفع صورنا إلى السجل ينبغي امتلاك سجل خاص أو أن نستخدم سجلًا عامًا مثل Docker Hub أو خدمة أخرى نحو سجل الحاويات لمنصة ديجيتال أوشن كما فعلنا في هذا المثال، والذي يمكننا استخدامه بالمجان للمشاريع الصغيرة؛ إذ يمكننا حجز مستودع واحد بمساحة تخزينية لا تتجاو 500 ميجا بايت، وهذا يناسب في الواقع يناسب الصورة الوحيدة التي سنرفعها إلى السجل. يمكننا البدء إذًا بإعداد سجل الحاويات المجاني free-tier registry، واختيار أحد مواقع مراكز البيانات في المنطقة، مع تسمية السجل باسم مناسب مثل my-container-registry، كما هو الحال في الصورة التالية: تنويه: لا بد من توفر حساب خاص على منصة ديجيتال أوشن لاستخدام سجل الحاويات، ويمكن الاسترشاد بدليل فتح الحساب لمزيدٍ من المعلومات عن هذه الخدمة. سنحتاج الآن لمفتاح الواجهة البرمجية API للاتصال بالسجل، لذا سنتوجه إلى تبويب tokens من الصفحة الرئيسية لحسابنا على المنصة وننشئ مفتاحًا جديدًا خاصًا بنا كما هو الحال في الصورة التالية: سنملأ الآن النموذج الظاهر أمامنا بعد الضغط على زر إنشاء رمز جديد Generate New Token ونفَعِّل خيار التحكم الكامل Full Access طالما أنه لن يكون هناك أكثر من مستخدم وحيد للرمز، إذ لا توجد أي مخاطر أمنية. سينشأ الآن الرمز الجديد، وما علينا سوى نسخه والاحتفاظ به في مكانٍ آمن؛ إذ سنحتاجه لاحقًا عند تسجيل الدخول إلى السجل. والآن يمكننا تسجيل الدخول إلى سجل الحاويات باستخدام بيانات حسابنا، وذلك عبر كتابة الأمر التالي في الطرفية terminal: docker login registry.digitalocean.com سيُطلب منا عندها إدخال اسم المستخدم وكلمة المرور: OutputUsername: ravish.foo@bar.com Password: Login Succeeded وبإدخالهما تكتمل عملية تسجيل الدخول ويمكن رفع صورة تطبيقنا إلى السجل؛ ويجري ذلك بخطوتين، أولاهما عبر وسم tag الصورة بوسم خاص للرفع كما يلي: docker tag react-app:v1.0 registry.digitalocean.com/my-container-registry/react-app وثانيهما رفع الصورة إلى السجل كما يلي: docker push registry.digitalocean.com/my-container-registry/react-app وسنحصل على خرج يشبه التالي يؤكد لنا نجاح العملية: OutputUsing default tag: latest The push refers to repository [registry.digitalocean.com/my-container-registry/react-app] 5f70bf18a086: Pushed fafde3127bc5: Pushed 155c640ab606: Pushed 993afd9f06ad: Pushed 9fd54926bcae: Pushed 175aa66db4cc: Pushed e6380a7057a5: Pushed 1db2242fc1fa: Pushed b09347a1aec6: Pushed bbde741e108b: Pushed 52ec5a4316fa: Pushed latest: digest: sha256:227a8c3ede3fc5c81b281228600bec84939f8a4a0cc770fc6e5527b3261e77f4 size: 2608 بهذا تكون صورة التطبيق أصبحت قد صارت محفوظةً الآن ضمن سجل الحاويات الخاص بنا، وهي متاحة للسحب، وسنراها مباشرةً من لوحة تحكم حسابنا: يمكننا ملاحظة أن الصورة react-app التي رفعناها إلى السجل تحمل الوسم الافتراضي latest لأننا لم نعطها وسمًا خاصًا عند الرفع، فأي صورة تُرفع للسجل دون وسم تُمنح افتراضيًا الوسم latest، ويمكن بالتأكيد تغيير هذا الوسم إلى أي وسم آخر تريده بواسطة خاصية الإصدار Versioning. فعلى سبيل المثال، يمكن وسم صورة الإصدار الأول من تطبيقنا بالوسم v1.0 وعند إجراء تغيرات لاحقة على شيفرة التطبيق نستطيع إعطاء الصورة الجديدة الوسم v1.1 ثم v1.4 أو v2.0، وهكذا. فننشئ بذلك سجلًا زمنيًا خاصًا بإصدارات تطبيقنا يساعدنا على توثيق تطور التطبيق ويُسَهِّل علينا تتبع إصداراته وإدارتها وخصوصًا على المدى الطويل. بهذا نكون انتهينا من بناء صورة التطبيق وأصبحت جاهزة على سجل الحاويات، كل ما علينا الآن تشغيل خادم Droplet لنشر deploy الحاوية باستخدام هذه الصورة. تشغيل خادم Droplet تنويه: لقد استخدمنا في هذا المثال خادمًا من ديجيتال أوشن لكن يمكن اختيار الاستضافة المناسبة لتأمين الخادم، كما يمكن الاستعانة بمقال التهيئة الأولية لخادم أوبونتو لتجهيزه. عمليًّا، ستكفينا الخطة ذات 6$ لبناء خادم Droplet مناسب لنشر الحاوية، وسيساعدنا هذا الدليل على إتمام العملية؛ إذ سنختار في البداية نظام التشغيل وليكن أوبنتو، وعند اكتمال تشغيل الخادم سيظهر أمامنا في لوحة تحكم حسابنا كما في الصورة التالية: ننقر على الخادم لرؤية تفاصيله، وننسخ عنوان IP الخاص به، والذي نجده تحت الاسم الخادم كما في الصورة التالية: يمكن الآن فتح جلسة اتصال ssh مع الخادم كما يلي، وذلك باستخدام عنوان IP المخصص له وكلمة المرور لحساب الجذر root التي تُطلب منا أثناء إعداد الخادم: ssh root@143.244.130.234 root@143.244.130.234's password: يمكن أيضًا اختيار عدم المطالبة بكلمة مرور عند الاتصال مع الخادم ببروتوكول ssh إذا أردنا ذلك. ستظهر أمامنا رسالة الترحيب التالية بمجرد نجاح الاتصال: Welcome to Ubuntu 23.10 (GNU/Linux 6.5.0-9-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Mon Apr 29 08:07:53 UTC 2024 System load: 0.32 Processes: 106 Usage of /: 9.3% of 23.17GB Users logged in: 0 Memory usage: 40% IPv4 address for eth0: 143.244.130.234 Swap usage: 0% IPv4 address for eth0: 10.47.0.5 47 updates can be applied immediately. To see these additional updates run: apt list --upgradable *** System restart required *** Last login: Mon Apr 29 08:07:55 2024 from 198.211.111.194 root@ubuntu-s-1vcpu-1gb-blr1-01:~# وبهذا صرنا متصلين بالخادم ويمكننا تنفيذ التعليمات عليه. سحب صورة التطبيق من سجل الحاويات من المهم في البداية التأكد من وجود Docker ضمن الخادم، وذلك بكتابة الأمر التالي: root@ubuntu-s-1vcpu-1gb-blr1-01:~# docker --version Docker version 24.0.5, build ced0996 وإذا لم يكن موجودًا يمكننا تثبيته باستخدام الأمر التالي: root@ubuntu-s-1vcpu-1gb-blr1-01:~# snap install docker ثم نسجل الدخول إلى سجل الحاوية بالخطوات نفسها التي اتبعناها قبل قليل: root@ubuntu-s-1vcpu-1gb-blr1-01:~# docker login registry.digitalocean.com Username: ravish.foo@bar.com Password: Login Succeeded بعد ذلك نستطيع سحب pull صورة التطبيق من السجل كما يلي: root@ubuntu-s-1vcpu-1gb-blr1-01:~# docker pull registry.digitalocean.com/my-container-registry/react-app وسنحصل على خرج يشبه التالي: OutputUsing default tag: latest latest: Pulling from my-container-registry/react-app b0a0cf830b12: Already exists 8ddb1e6cdf34: Already exists 5252b206aac2: Already exists 988b92d96970: Already exists 7102627a7a6e: Already exists 93295add984d: Already exists ebde0aa1d1aa: Already exists 6156e0586e61: Already exists 88bdf71f3911: Pull complete f5524cce8c8a: Pull complete 4f4fb700ef54: Pull complete Digest: sha256:227a8c3ede3fc5c81b281228600bec84939f8a4a0cc770fc6e5527b3261e77f4 Status: Downloaded newer image for registry.digitalocean.com/my-container-registry/react-app:latest registry.digitalocean.com/my-container-registry/react-app:latest وكما نلاحظ، فالأمر السابق قد سَحَبَ الصورة ذات الوسم latest أي الصورة الأحدث، لأننا لم نُحَدِّد أي وسم آخر في أمر السحب، وفي مثل هذه الحالة يسحب docker الصورة latest تلقائيًا. تشغيل الحاوية لدينا الآن صورة عن أحدث إصدار من التطبيق تشمل كل الاعتماديات اللازمة لعمله، ويمكننا تشغيل الحاوية بواسطتها وفق الأمر التالي: docker run -dp 80:80 registry.digitalocean.com/my-container-registry/react-app وكتوضيح للشيفرة أعلاه، تجعل الراية d- الحاوية تعمل في الخلفية، وتصل الراية p 80:80 البوابة 80 في الخادم المضيف بالبوابة 80 في الحاوية لتتجه حركة مرور البيانات الواردة للخادم إلى بوابة الحاوية. البوابة 80 هي البوابة المعيارية لحركة البيانات وفق البروتوكول HTTP وقد ضبطنا الخادم nginx ليستقبل الطلبات عبرها. سننفِّذ الآن الأمر التالي للتأكد من أن الحاوية قيد التشغيل: root@ubuntu-s-1vcpu-1gb-blr1-01:~# docker ps -a يَعرِضُ هذا الأمر الحاويات المُشَغّلة حاليًا على الخادم، سنجد حاويتنا بينها مع عبارة مثل "UP 2 seconds" تبين الزمن الذي أمضته الحاوية في وضع التشغيل. وحالما نتأكد من عمل الحاوية سيكون تطبيقنا متاحًا على الإنترنت للراغبين باستخدامه. يمكننا تجربة ذلك عبر كتابة عنوان IP لخادم Droplet في متصفحنا وستفتح أمامنا واجهة تطبيق React: يمكننا إيقاف تشغيل الحاوية بواسطة الأمر: docker stop <container_id> وإذا أردنا نستطيع حذفها بعد إيقافها بكتابة التالي: docker rm <container_id> الخلاصة يمكن الانطلاق مما تعلمناه هنا وتطوير آلية نشر تطبيقاتنا عبر أتمتة عمليات بناء النسخ ورفعها إلى سجل الحاويات وسحبها منه؛ حيث يمكن مثلًا كتابة سكريبت Bash يتكامل مع GitHub فيبني تلقائيًا صورة جديدة لتطبيقنا ويرفعها إلى سجل الحاويات مع كل عملية إيداع commit تنفذها على التطبيق، كما نستطيع إنشاء خط أنابيب للنشر المستمر والتسليم المستمر CI/CD يؤتمت عملية سحب الصور من السجل ونشرها على السحابة، فضلًا عن إمكانية استخدام عناقيد Kubernetes في عمليات النشر واسعة النطاق، والتي تتيح لنا إمكانية إدارة مجموعة كاملة من الحاويات بدلًا واحدة. ترجمة -وبتصرف- لمقال How to deploy your React app using Container Registry لصاحبيه Ravish Ahmad Khan و Anish Singh Walia. اقرأ أيضًا مدخل إلى الحاويات مدخل إلى دوكر Docker التعامل مع حاويات Docker أساسيات تنسيق الحاويات
  4. نشرح في هذه المقالة طريقة التعامل مع نظام إدارة قواعد البيانات MongoDB الذي يمتاز بكونه نظام غير علاقي وموجه نحو المستندات document-oriented، أي أنه يخزن البيانات في هيئة مستندات، ومثله مثل بقية أنظمة إدارة قواعد البيانات فهو يتعامل مع البيانات عبر أربع عمليات أساسية نسميها اختصارًا عمليات CRUD تشمل: عمليات الإنشاء Create أي كتابة بيانات جديدة لقاعدة البيانات عمليات القراءة Read‎ وهي عمليات الاستعلام التي نحصل بواسطتها على البيانات المخزنة عمليات التحديث Update وتعني العمليات التي تسبب تغييرًا في البيانات الموجودة ضمن قاعدة البيانات عمليات الحذف Delete وهي العمليات التي تؤدي إلى إزالة البيانات نهائيًا من قاعدة البيانات لنوضح من خلال الأمثلة العملية طريقة تنفيذ هذه كل من هذه العمليات على قاعدة بيانات من نوع MongoDB. متطلبات العمل لمتابعة سير العمل في هذا المقال يتوجب امتلاك معرفة أساسية بكيفية استخدام واجهة أوامر Mongodb المعروفة باسم Mongodb Shell وتنفيذ أوامر قاعدة البيانات.كما يجب توفّر خادم يعمل بنظام Ubuntu يحتوي على مستخدم عادي غير الجذر يمتلك صلاحيات sudo، مع تفعيل جدار الحماية الافتراضي UFW على الخادم، ويجب تثبيت MongoDB على الخادم، وتأمين قاعدة البيانات بإنشاء مستخدم إداري administrative user على قاعدة البيانات لديه صلاحية مسؤول وتفعيل المصادقة authentication في قاعدة البيانات لأنها تكون مُعطلة تلقائيًا. يمكن الاستعانة بمقال كيفية تثبيت وتأمين MongoDB على أوبونتو فهو يتضمن كل الخطوات اللازمة. الاتصال بخادم MongoDB قبل البدء بتنفيذ عمليات CRUD على البيانات، علينا الاتصال بقاعدة بيانات MongoDB عبر الصدفة MongoDB Shell. فإذا كان خادم MongoDB الذي نعمل عليه مبني على خادم بعيد وليس على جهازنا المحلي، نفتح جلسة SSH للاتصال به كما يلي: $ ssh sammy@your_server_ip ولمطالعة المزيد من المعلومات حول SSH ننصح بمشاهدة الفيديو التالي: بعدها نتصل بتطبيق MongoDB المُثَبَّت على الخادم بواسطة مستخدم يتمتع بصلاحيات القراءة والكتابة على البيانات، وسيكون أمر الاتصال بالصيغة التالية: $ mongo -u AdminSammy -p --authenticationDatabase admin ستُطلب منا كلمة المرور لمستخدم قاعدة البيانات وبعد كتابتها ستفتح الصدفة Shell مباشرة، ويمكن التأكد من ذلك بتغيّر شكل الموجّه في النافذة الطرفية ليصبح على هذا الشكل < عندها تكون الصدفة جاهزة لاستقبال الأوامر التي نريد توجيهها إلى الخادم ملاحظة: بمجرد إنشاء اتصال جديد مع خادم MongoDB سيصلنا تلقائيًا بقاعدة البيانات الاختبارية لنظام MongoDB تدعى test، ويمكن الانتقال إلى قاعدة بياناتنا الخاصة باستخدام الأمر use متبوعًا باسم قاعدة البيانات كما في المثال التالي: use database_name والآن بعد أن جهزنا الاتصال بخادم MongoDB، يمكننا البدء بالعملية الأولى من عمليات CRUD وهي Create لإنشاء مستندات جديدة. إنشاء المستندات سننشئ مستندات MongoDB ونستخدامها لاحقًا في تطبيق الأمثلة على عمليات القراءة والتحديث والحذف. لنفترض أننا نريد إنشاء مستندات تخزن معلومات عامة عن بعض المعالم الأثرية مثل: اسم المعلم الأثري، والبلد الذي يوجد فيه، والمدينة، وإحداثيات الموقع الجغرافي. ستكون المستندات التي تتضمن معلومات عن أهرامات الجيزة The Pyramids of Giza مثلًا وفق الصيغة التالية: { "name": "The Pyramids of Giza", "city": "Giza", "country": "Egypt", "gps": { "lat": 29.976480, "lng": 31.131302 } } تُكتب مستندات MongoDB وتعالج بصيغة BSON، وهي تمثيل ثنائي لملفات JSON سهل القراءة والفهم ويوفر بعض الإضافات التي تسمح بتخزين أنواع بيانات إضافية لا تدعمها صيغة JSON. تُمثَّل البيانات الموجودة في هذه المستندات بهيئة أزواج حقل وقيمة field: value. يتضمن المستند الذي أنشأناه أربعة حقول يشير الحقل الأول إلى اسم المعلم الأثري، يليه المدينة في الحقل الثاني، والبلد في الحقل الثالث، وتحتوي هذه الحقول الثلاثة على سلاسل نصية strings، أما الحقل الأخير والمسمى gps، فهو مستند متداخل nested document أي مستند داخل مستند يعرض معلومات عن الموقع الجغرافي للمكان الأثري ويتضمن إحداثيات خطوط الطول والعرض يمثلها الحقلان lat و lng والقيم ضمنهما من نوع عدد عشري. تنتمي المستندات في MongoDB دائمًا إلى مجموعات collections، وهي تكافئ الجداول tables في قواعد البيانات العلاقية مثل MySQL أما المستندات في MongoDB فهي تشبه الصفوف rows ضمن كل جدول وبناءً على ذلك سننشئ المستند الذي كتبنا صيغته في المثال السابق ضمن مجموعة باسم monuments باستخدام الأمر insertOne الذي ينشئ مستندًا واحدًا فقط، وهناك أمر آخر يسمح لنا بإنشاء عدة مستندات بنفس الوقت. لنكتب إذًا أمر الإنشاء التالي في نافذة الصدفة: > db.monuments.insertOne( > { > "name": "The Pyramids of Giza", > "city": "Giza", > "country": "Egypt", > "gps": { > "lat": 29.976480, > "lng": 31.131302 > } > } > ) نلاحظ هنا أننا لم ننشئ المجموعة monuments مُسبقًا قبل إدراج المستندات ضمنها، فنظام MongoDB مرن من هذه الناحية فالمجموعة ستنشأ تلقائيًا مع إدراج أول مستند فيها، وبذلك فإن التابع insertOne()‎ لم يُنشئ المستند فحسب بل أنشأ المجموعة أيضًا. سنحصل على الخرج التالي الذي يعلمنا بنجاح تنفيذ التابع insertOne()‎ وإنشاء المستند المطلوب، بالإضافة إلى إرجاع مُعَرّف الكائن ObjectId الخاص به والذي ينشأ تلقائيًا لكل كائن أو مستند جديد: { "acknowledged" : true, "insertedId" : ObjectId("6105752352e6d1ebb7072647") } ينبغي أن يتضمن كل مستند في مجموعة MongoDB حقلًا خاصًا بالمُعَرِّف الفريد id_ للمستند والذي كمفتاح رئيسي primary key، يمكنك إعطاء الحقل id_ أي قيمة نختارها طالما أننا ملتزمون تمامًا بالحفاظ على قيمة فريدة له وعدم تكراره بين المستندات، وبكل الأحوال إذا أهملنا هذا الحقل عند إنشاء أي مستند جديد فإن MongoDB ستُنشئه نيابةً عنا ويكون بهيئة كائن ObjectId كما شاهدنا في الخرج السابق. للتأكد من صحة الإنشاء، يمكن تفقد عدد المستندات في المجموعة monuments باستخدام الأمر التالي: > db.monuments.count() سيُرجع التابع count القيمة 1 لأننا أدخلنا مستندًا واحدًا فقط في المجموعة: 1 توفر MongoDB طريقةً سهلة لإدخال عدة مستندات إلى المجموعة بعملية واحدة عبر التابع insertMany فالإدخال الفردي قد يصبح مرهقًا إذا كانت المستندات معقدة وكثيرة العدد. على سبيل المثال ينشئ الأمر التالي ستة مستندات دفعة واحدة ضمن المجموعة monuments: > db.monuments.insertMany([ > {"name": "The Valley of the Kings", "city": "Luxor", "country": "Egypt", "gps": { "lat": 25.746424, "lng": 32.605309 }}, > {"name": "Arc de Triomphe", "city": "Paris", "country": "France", "gps": { "lat": 48.873756, "lng": 2.294946 }}, > {"name": "The Eiffel Tower", "city": "Paris", "country": "France", "gps": { "lat": 48.858093, "lng": 2.294694 }}, > {"name": "Acropolis", "city": "Athens", "country": "Greece", "gps": { "lat": 37.970833, "lng": 23.726110 }}, > {"name": "The Great Wall of China", "city": "Huairou", "country": "China", "gps": { "lat": 40.431908, "lng": 116.570374 }}, > {"name": "The Statue of Liberty", "city": "New York", "country": "USA", "gps": { "lat": 40.689247, "lng": -74.044502 }} > ]) تُحيط الأقواس المربعة [] بالمستندات الستة في المثال أعلاه، فهي تشكل مصفوفة من العناصر، ويفصل بين كل عنصر وآخر فاصلة , فعندما نريد تمرير عدة كائنات في MongoDB أيًّا كان نوعها نمررها بشكل مصفوفة بين قوسين مربعين. بمجرد تنفيذ أمر الإنشاء السابق سيظهر الخرج التالي ليعلمنا بنجاح العملية ويرجع المعرّفات الفريدة لكل مستند: { "acknowledged" : true, "insertedIds" : [ ObjectId("6105770952e6d1ebb7072648"), ObjectId("6105770952e6d1ebb7072649"), ObjectId("6105770952e6d1ebb707264a"), ObjectId("6105770952e6d1ebb707264b"), ObjectId("6105770952e6d1ebb707264c"), ObjectId("6105770952e6d1ebb707264d") ] } لنتأكد من صحة أمر إنشاء المستندات الجديدة بالاستعلام عن عدد المستندات في المجموعة monuments: > db.monuments.count() سنحصل على الخرج التالي الذي يوضح أن عدد المستندات في المجموعة أصبح 7 بعد إضافة ستة مستندات جديدة إلى المستند الأول: 7 بعد أن أنشانا المستندات، لننتقل للخطوة التالية التي تشرح كيفية قراءة هذه البيانات من هذه المستندات. قراءة المستندات أصبح لدينا مجموعة تتضمن سبعة مستندات، ويمكننا الاستعلام عنها من قاعدة البيانات، سنعرض في البداية طريقة الاستعلام عن جميع مستندات المجموعة من دون استثناء، ثم سنتعلم تضييق نطاق الاستعلام لنحصل على مجموعة أقل من المستندات. لنبدأ بقراءة المستندات السبعة من المجموعة monuments بعملية واحد باستخدام التابع ()find: > db.monuments.find() يؤدي استخدام التابع ()find بدون وسطاء إلى إرجاع جميع البيانات الموجودة في المجموعة المحددة وهي في حالتنا المجموعة monuments وسنحصل بالتالي على هذا الخرج: { "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 } } { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } } { "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } } { "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 } } { "_id" : ObjectId("6105770952e6d1ebb707264b"), "name" : "Acropolis", "city" : "Athens", "country" : "Greece", "gps" : { "lat" : 37.970833, "lng" : 23.72611 } } { "_id" : ObjectId("6105770952e6d1ebb707264c"), "name" : "The Great Wall of China", "city" : "Huairou", "country" : "China", "gps" : { "lat" : 40.431908, "lng" : 116.570374 } } { "_id" : ObjectId("6105770952e6d1ebb707264d"), "name" : "The Statue of Liberty", "city" : "New York", "country" : "USA", "gps" : { "lat" : 40.689247, "lng" : -74.044502 } } تعرض الصدفة MongoDB Shell المستندات السبعة بكامل حقولها، ونلاحظ أن لكل حقل مُعَرّف خاص id_ رغم أننا لم نُعَرِّفه عند إنشاء المستندات، وذلك لأن MongoDB كما ذكرنا تُنشئ المُعرفات تلقائيًا مع أوامر الإدخال وهي تعمل كمفاتيح رئيسية للمستندات الخاصة بها. نلاحظ أيضًا أن طريقة عرض البيانات ضمن الصدفة MongoDB Shell صعب القراءة نوعًا ما، وخاصة إذا كانت المستندات كثيرة الحقول أو تتضمن مستندات متداخلة فالمستند الواحد سيظهر بجميع بياناته بسطر واحد، ويمكن تحسين الإظهار وجعله أسهل في القراءة باستخدام الخاصية ()pretty مع التابع ()find كما يلي: > db.monuments.find().pretty() الآن ستعرض الصدفة MongoDB Shell الخرج بالطريقة التالية، حيث تفرد لكل مستند عدة أسطر يبدأ كل منها بمسافة بادئة indentation: { "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 } } { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } } . . . لنستخدم بعض الوسطاء مع التابع ()find لتضييق نتائج البحث فمثالنا السابق أرجع كافة مستندات المجموعة من دون أي تصفية. ذكرنا سابقًا أن MongoDB تعطي مُعَرِّفًا فريدًا لكل مستند، لنأخذ مثلًا المستند المُسمى "The Valley of the Kings" والذي يشير لأحد المعالم الأثرية في مجموعتنا وله المُعرِّف ‎ObjectId("6105770952e6d1ebb7072648")‎ الذي يميزه عن غيره، علمًا أن مُعرِّف الكائن ObjectId ليس من مجرد سلسلة ست عشرية فهو بحد ذاته كائن أيضًا وهو نوع خاص من البيانات تستخدمه MonogoDB لتخزين مُعَرِّفات الكائنات، ويمكن مطالعة المزيد عن أنواع البيانات التي تتعامل معها MongoDB. سُنمَرِّر الآن هذا المُعَرِّف كوسيط للتابع ()find بهيئة مستند يسمى مستند تصفية الاستعلام query filter document، تتمتع مستندات تصفية الاستعلام بنفس بنية مستندات مجموعة البيانات أي بنية حقل وقيمة لكنها تستخدم لغرض خاص هو تصفية نتائج الاستعلام. يتضمن مستند تصفية الاستعلام في حالتنا الحقل id_ والقيمة التي تمثل Objectid للمستند "The Valley of the Kings"، وسيأخذ الاستعلام عندها الصيغة التالية: > db.monuments.find({"_id": ObjectId("6105770952e6d1ebb7072648")}).pretty() يستخدم مستند تصفية الاستعلام في هذا المثال شرط المساواة لإرجاع النتائج، فستُرجع ()find المستند الذي يطابق معرفه id_ القيمة ‎ObjectId("6105770952e6d1ebb7072648")‎ فقط ولن تُرجع معه أي مستند آخر. سنحصل في هذه الحالة على الخرج التالي: { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } } لنجرب مثالًا آخر، سنستعلم هنا عن جميع المعالم الأثرية الموجودة في فرنسا أي التي تحقق الشرط التالي: > db.monuments.find({"country": "France"}).pretty() سيُرجع هذا الاستعلام نتيجتين كما يلي: { "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } } { "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 } } الخطوة 4: تحديث المستندات يُقصد بتحديث المستندات Updating Documents في قواعد بيانات MongoDB إما تغيير بنية المستند نفسه بإضافة أو تعديل الحقول لتناسب متطلبات جديدة في التطبيق مثلًا، أو تعديل قيم البيانات المُخَزَّنة ضمن المستند، سنناقش أمثلةًعن كلا الحالتين إذ سنُعَدِّل بعض القيم في المستندات وسنضيف حقلًا جديدًا إلى مستندات المجموعة. توفر MongoDB طريقتين لتحديث المستندات إما فرديًا أو جماعيًا، في التحديث الجماعي للمستندات يتعين علينا تمرير القيم التي نريد تحديثها كوسيط لتابع التحديث، وفي التحديث الفردي نمرر مُعَرِّف المستند الذي نود تعديله إضافةً إلى القيم الجديدة. تستخدم MongoDB آلية مستند تصفية الاستعلام query filter document نفسها التي استخدمناها في الفقرة السابقة، وذلك للسماح للمستخدم بتحديد المستندات التي يود تحديثها. يبين المثال التالي طريقة تغيير اسم أحد المعالم الأثرية وليكن مثلًا قوس النصر Arc de Triomphe حيث سنغير اسمه إلى Arc de Triomphe de l’Étoile، باستخدام التابع ()updateOne المخصص لتحديث مستند واحد فقط في كل عملية: > db.monuments.updateOne( > { "name": "Arc de Triomphe" }, > { > $set: { "name": "Arc de Triomphe de l'Étoile" } > } > ) سيكون اسم المستند الذي نرغب بتعديله هو الوسيط الأول للتابع ()updateOne وهو في حالتنا { "name": "Arc de Triomphe" } وهو مستند تصفية استعلام مع شرط المساواة، وبالتالي سيُرجع لنا المستند الذي يطابق اسمه تمامًا القيمة المذكورة، وسيكون هو المستند الذي ننفذ عليه التعديل. أما الوسيط الثاني فهو مستند التحديث update document الذي يتضمن التحديثات التي ينبغي أن تُطَبَّق عند تنفيذ التابع، يتكون مستند التحديث من عوامل تحديث operators تلعب دور المفاتيح keys، ومعاملات parameters مقابلة تمثل القيم الجديدة values. في حالتنا عامل التحديث هو set$ وهو المسؤول عن إعطاء قيم جديدة لحقول المستند التي تُكتب بصيغة كائنات JSON مثل { "name": "Arc de Triomphe de l'Étoile" } الذي يعني ضمنًا تعديل قيمة الحقل name لتصبح Arc de Triomphe de l'Étoile. يُرجع تابع التحديث ()updateOne خرجًا يخبرنا بنتيجة عمله، فقد عثر على مستند واحد مطابق للشروط، وحَدَّث قيمته بنجاح، كما يلي: { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 } تنويه: قد لا يكون مستند تصفية الاستعلام المستخدم كوسيط أول للتابع ()updateOne مُحَددًا ودقيقًا بما يكفي لإعطائنا نتيجة واحدة فيُرجِع عدة مستندات تتوافق مع شروط الاستعلام بدلًا من واحد، في هذه الحالة سيأخذ التابع ()updateOne أول مستند من هذه المستندات ويطَبِّق التعديلات عليه ويهمل البقية لأنه في نهاية الأمر مُخصص للعمل مع مستند واحد. لنتأكد الآن من نجاح عملية التحديث، سنطلب جميع المعالم الأثرية الموجودة في فرنسا France وفق الآتي: > db.monuments.find({"country": "France"}).pretty() يؤكد لنا الخرج التالي تغيير طريقة كتابة Arc de Triomphe وبالتالي نجاح عملية التحديث: { "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe de l'Étoile", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } } . . . سنستعمل الآن التابع ()updateMany لتعديل أكثر من مستند في نفس الوقت. لنفترض مثلًا أننا نريد إضافة حقل إلى كل مستند من مستندات المجموعة، يبين هذا الحقل اسم المُحرِّر editor الذي أدخل بيانات المعلم الأثري. طالما أننا سنُنَفِّذ التعديل على جميع المستندات في المجموعة، فسنستخدم إذًا مستند تصفية استعلام فارغ بلا أي شروط وبسببه سيؤثر ()updateMany على جميع مستندات المجموعة monuments ويُضيف إلى كل واحد منها حقلًا جديدًا يدعى editor ويحمل القيمة Sammy وفق التالي: > db.monuments.updateMany( > { }, > { > $set: { "editor": "Sammy" } > } > ) سيعطي هذا التابع الخرج التالي الذي يعلمنا بنتيجة التنفيذ: { "acknowledged" : true, "matchedCount" : 7, "modifiedCount" : 7 } عثرنا على سبعة مستندات مطابقة للشروط، وعَدَّلنا المستندات السبعة بنجاح. يمكن التأكد من إضافة الحقل الجديد إلى مستندات المجموعة بواسطة الأمر التالي: > db.monuments.find().pretty() وسيكون الخرج كما يلي: { "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 }, "editor" : "Sammy" } { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 }, "editor" : "Sammy" } . . . كما نلاحظ فقد أُضيف حقل جديد إلى كل مستندات المجموعة، فإعطاء اسم حقل غير موجود للعامل set$ في تابع التحديث يعني أننا نطلب منه إنشاء هذا الحقل ومنحه القيمة المحددة. يُعدّ set$ عاملًا شائع الاستخدام وسيفيدنا في كثير من الحالات، مع ذلك توفر MongoDB قائمة طويلة من عوامل التعديل التي تساعدنا على إجراء تحديثات متنوعة ومعقدة على المستندات ويمكن معرفة المزيد عنها من توثيقات MongoDB الرسمية. حذف المستندات إذا احتجنا لحذف بعض البيانات القديمة من قاعدة بيانات MongoDB فيمكن استخدام تابعين للحذف الأول هو التابع ()deleteOne لحذف مستند واحد من المستندات المطابقة لشروط مستند تصفية الاستعلام، والثاني هو التابع ()deleteMany لحذف عدة مستندات دفعة واحدة. لنحاول حذف المستند الخاص بقوس النصر Arc de Triomphe de l’Étoile الذي عَدَّلنا اسمه قبل قليل، وذلك وفق الأمر التالي: > db.monuments.deleteOne( > { "name": "Arc de Triomphe de l'Étoile" } > ) نلاحظ أن مستند تصفية الاستعلام المستخدم هنا مشابه لما استخدمناه في الأمثلة السابقة عند القراءة والتحديث، ويمكن بالتأكيد استخدام أي استعلام مكتوب بطريقة صحية لتحديد المستند الذي نريد حذفه. نحصل بعد التنفيذ على خرج يشبه التالي: { "acknowledged" : true, "deletedCount" : 1 } يبين هذا الخرج عدد المستندات المحذوفة بموجب الأمر السابق. والآن لنتأكد من حذف المستند بالاستعلام عن الآثار الموجودة في فرنسا وفق التالي: > db.monuments.find({"country": "France"}).pretty() ستتضمن نتيجة الاستعلام المستند الخاص ببرج إيفل The Eiffel Tower فقط لأننا حذفنا مستند قوس النصر Arc de Triomphe de l’Étoile: { "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 }, "editor" : "Sammy" } لنشرح الآن طريقة حذف مستندات متعددة، ولنحذف مثلًا كافة المستندات التي أنشأها المحرر الذي اسمه Sammy، طبعًا سيؤدي هذا إلى حذف جميع مستندات المجموعة لأن Sammy هو المحرر الذي أضفناه لكافة مستندات المجموعة monuments: > db.monuments.deleteMany( > { "editor": "Sammy" } > ) سيعلمنا الخرج بحذف ستة مستندات: { "acknowledged" : true, "deletedCount" : 6 } ويمكن التأكد من النتيجة بالاستعلام عن عدد مستندات المجموعة monuments كما يلي: > db.monuments.count() سنحصل على الخرج التالي: 0 تعني النتيجة 0 حذف جميع المستندات في المجموعة. الخاتمة تعرفنا في هذا المقال على طريقة تطبيق عمليات CRUD على قاعدة بيانات MongoDB، إذ تُعَدّ هذه العمليات أساسية في إدارة البيانات ويمكن من خلالها إنشاء مستندات جديدة وتعديلها والاستعلام عنها وحذفها عند الضرورة. وننوه في الختام أننا عرضنا في هذا المقال طريقةً واحدة فقط من طرق تصفية الاستعلام، لكن MongoDB توفر طرقًا أخرى أقوى وأكثر تعقيدًا ويمكنكم تعلم المزيد عنها بمراجعة قسم MongoDB على أكاديمية حسوب وتوثيقات MongoDB الرسمية. ترجمة -وبتصرف- لمقال How To Perform CRUD Operations in MongoDB لصاحبه Mateusz Papiernik.
  5. غالبًا ما نركز اهتمامنا على معرفة كيفية بناء التطبيق ونشره في البيئة السحابية، وقد نغفل دون قصد الإجراءات الأمنية الأساسية التي ينبغي علينا توفيرها على الخادم السحابي قبل نشر التطبيقات عليه وإتاحتها للعامة. سنشارك في هذا المقال بعض التوصيات التي يمكننا تنفيذها على الخادم لحمايته من الهجمات الإلكترونية وجعله بيئة آمنة للنشر. بالطبع لن تغني هذه التوصيات العامة عن التدابير المصممة خصيصًا للتطبيق والتي يمكن تنفيذها بعد النشر، لكنها تُعَدّ انطلاقة جيدة يمكن البناء عليها. استخدام مفاتيح SSH SSH هي اختصار لعبارة Secure Shell، وهو بروتوكول آمن ومُشفر للاتصال بالخوادم وإدارتها، وهو شائع الاستخدام فأي شخص يتعامل مع الخوادم سيقضي معظم وقته متصلًا بالخادم عبر جلسة اتصال طرفية terminal تستخدم برتوكول SSH، أما مفاتيح SSH أو SSH Keys فهي بديل آمن لعمليات تسجيل الدخول المستندة إلى كلمة مرور أي تلك التي تعتمد الاستيثاق بكلمة المرور، لأن هذه المفاتيح تستخدم التشفير لتأمين تسجيل الدخول إلى الخادم، وتُعَدُّ إجراءً ضروريًا يوصى به لجميع مستخدمي SSH. لإجراء الاستيثاق authentication من خلال مفاتيح SSH نحتاج لتوليد مفتاحين، مفتاح عام public key مكن مشاركته مع الطرف الآخر، ومفتاح خاص private key سري يحتفظ به المستخدم ولا يشاركه مع أحد، تُسمى هذه الآلية التشفير غير المتماثل asymmetric encryption وقد تصادفنا في حالات استخدام أخرى. بعد توليد مفتاح SSH العام ينبغي لنا وضعه على الخادم ضمن المسار المخصص له وهو عادةً المجلد ‎~/.ssh/authorized_keys، وبعدها يمكننا الاتصال بالخادم باستخدام مفاتيح SSH، ومعرفة المزيد عن الاستيثاق باستخدام كلمة المرور والاستيثاق باستخدام المفاتيح ننصح بمشاهدة الفيديو التالي: كيف تعزز مفاتيح SSH أمان الاتصال مع الخادم إن جميع أنواع الاستيثاق في SSH مُشفرة بالكامل حتى تلك المعتمدة على كلمات المرور فقط، لكن بالرغم من التشفير لن يُعدّ الاستيثاق باستخدام كلمة المرور آمنًا بما فيه كفاية، فالمخترق سيحاول تخمين كلمة المرور مرارًا وتكرارًا باستخدام برمجيات متخصصة وقد ينجح في كسرها والوصول إلى الخادم وخاصةً إذا كان للخادم عنوان IP عام، وعلى الرغم من إمكانية تقليل عدد المحاولات المتاحة أمام المخترق بضبط إعدادات الخادم ليمنع تلقائيًا الاتصالات الواردة من أي عنوان IP يجري عددًا معينًا من محاولات الاتصال الفاشلة، إلّا أن ذلك يبقى غير آمن تمامًا فترك الخادم عرضة لهجمات القوة الغاشمة brute force attacks يُعدّ خطرًا أمنيًا لا يستهان به. وهنا تبرز أهمية الاستيثاق باستخدام مفاتيح SSH فهو يسمح بتعطيل الاستيثاق المستند إلى كلمة المرور ويلغي المخاطر المرتبطة به، وحتى إذا عينا كلمة مرور لمفتاح SSH الخاص بنا فإنها تُعدّ هدفًا صعب المنال في هجوم القوة الغاشمة بسبب عدد المحارف الكبير إذ يمكننا إنشاء تقطيع أو تجزئة hash بطول يصل إلى 128 محرف لمفتاح SSH ذو كلمة مرور بطول 12 محرف فقط، ورغم أن التشفير يُستخدم لحماية كلمات المرور، إلا أن بعض خوارزميات التشفير ليست آمنة بالكامل. فبعضها يمكن اختراقه باستخدام تقنيات الهندسة العكسية، وذلك من خلال تحليل تجزئات كلمات المرور password hashes على أجهزة قوية لفترات طويلة حتى الوصول إلى كلمة مرور مطابقة. في المقابل، توجد خوارزميات أكثر أمانًا حتى الآن، مثل خوارزمية RSA الافتراضية التي تعتمدها تطبيقات SSH الحديثة. تنفيذ الاستيثاق بمفاتيح SSH على الخادم يوصى باستخدام مفاتيح SSH للاتصال عن بعد بأي خادم لينكس، ويمكنك تنفيذ ذلك بخطوتين: توليد زوج من مفاتيح SSH على الحاسوب المحلي باستخدام الأمر ssh وفق الخطوات الواردة في مقال العمل مع خواديم SSH: العملاء والمفاتيح، ثم نقل المفتاح العام public key إلى الخادم البعيد. أما في أجزاء النظام التي تحتاج لاستخدام كلمة مرور أو التي تكون عرضةً لهجمات القوة الغاشمة brute force attacks فيمكن تنفيذ حلول مخصصة لذلك مثل استخدام Fail2ban للحد من محاولات تخمين كلمة المرور، ولمطالعة المزيد ننصح بمطالعة مقال كيفية حماية SSH باستخدام Fail2Ban على Ubuntu. من ناحية أخرى يُعدّ عدم السماح لمستخدم الجذر root بتسجيل الدخول مباشرةً عبر SSH واحدًا من أفضل الممارسات الأمنية الموصى بها، وذلك طبقًا لمبدأ إعطاء الامتيازات الأقل فبدلاً من المستخدم root نسجل الدخول بمستخدم عادي لا يملك امتيازات عالية، ثم نرفع امتيازاته حسب الحاجة بواسطة أداة مثل sudo، ويمكن تطبيق ذلك كما يلي: بعد التأكد من الاتصال بالخادم وإنشاء حساب لا يتمتع بامتيازات عالية لكنه قادر على العمل مع SSH، يمكن تعطيل عمليات تسجيل الدخول بواسطة مستخدم الجذر عبر تعديل قيمة السطر الخاص بذلك ضمن الملف ‎/etc/ssh/sshd_config على الخادم ليصبح PermitRootLogin no ثم نعيد تشغيل SSH باستخدام أمر مثل Sudo systemctl Restart sshd ليأخذ التعديل مفعوله. الجدران النارية Firewalls الجدار الناري Firewall هو تطبيق برمجي أو عتاد يتحكم بإتاحة الخدمات على الشبكة، وبأنواع حركة مرور البيانات المسموح لها بالدخول والخروج من وإلى خادم معين أو مجموعة خوادم معينة، وعند ضبطه بشكل صحيح، يعمل الجدار الناري على حماية النظام بفعالية، حيث يمنع أي اتصال وارد من خارج الشبكة ما لم يكن مصرحًا به، ولا يسمح إلا بالوصول إلى الخدمات المخصصة لذلك فقط. تُصَنَّف الخدمات الموجودة على أي خادم تقليدي إلى الفئات التالية: خدمات عامة Public متاحة لأي شخص عبر الإنترنت مثل خوادم الويب خدمات خاصة Private محصورة بالاتصالات من مواقع محددة أو حسابات مصرح لها بالوصول مثل لوحات تحكم قواعد البيانات خدمات داخلية Internal غير متاحة للعامة وتقبل الاتصالات المحلية فقط، مثل قواعد البيانات المحلية تتحكم الجدران النارية بالوصول إلى التطبيقات تبعًا للفئات المذكورة أعلاه، فتساعد على إتاحة الخدمات العامة للجميع عبر الإنترنت، وعلى تقييد الوصول إلى الخدمات الخاصة بناءً على معايير مختلفة مثل أنواع الاتصالات أو غيرها، بالإضافة إلى عزل الخدمات الداخلية تمامًا عن الإنترنت، وإغلاق المنافذ غير المستخدمة التي عادة ما تغلق في معظم إعدادات الحماية. كيف يحمي الجدار الناري التطبيقات لا غنى عن استخدام الجدار الناري لحماية الأنظمة البرمجية، حتى وإن كانت التطبيقات نفسها مزوّدة بميزات أمنية أو تقيّد الوصول إلى واجهات معينة فقط. فالجدار الناري يشكّل طبقة حماية أساسية تعمل على تصفية الاتصالات الواردة والصادرة، وتمنع حركة المرور غير المصرح بها من الوصول إلى التطبيق أساسًا، قبل معالجتها من قبل النظام أو الخوادم المعنية. ويتحكم الجدار الناري المُعَدّ بطريقة سليمة بإمكانية الوصول إلى مكونات النظام الخاص بنا حسب طبيعة العمل، فلا يتيح الوصول لكل الخدمات والاتصالات على الخادم، بل يسمح بالوصول إلى خدمات معينة ويمنع الوصول إلى سواها، وذلك عبر فتح المنافذ ports الخاصة بها وإغلاق بقية المنافذ، مثلًا يمكن فتح المنفذ 22 لبروتوكول الاتصال SSH، والمنفذين 80 و 443 لخدمات الويب HTTP/HTTPS التي تعمل من المتصفح وغيرها، عمومًا كلما اختصرنا عدد المنافذ أو الخدمات المفتوحة كلما قللنا من الثغرات الأمنية التي يمكن أن يستغلها المخترق ومن احتمالية تعرضنا للهجمات الإلكترونية. تشغيل الجدار الناري على الخادم يوفر نظام لينكس أنواع متعددة من جدران الحماية تختلف عن بعضها في درجة التعقيد وطبيعة الحماية التي تؤمنها، وكل ما علينا هو فهمها وضبط إعداداتها بحسب خدماتنا ثم التعديل عليها مع أي تغيير تجريه على هذه الخدمات، ومن أشهر الخيارات المتاحة الجدار الناري غير المُعقَّد Uncomplicated Firewall -أو UFW اختصارًا- حيث يتوفر هذا النوع افتراضيًا على بعض توزيعات لينكس مثل أوبنتو، ويمكن إعداده بالرجوع إلى مقال كيف تضبط جدارًا ناريًا في أوبنتو باستخدام UFW ومقال أساسيات UFW: قواعد وأوامر شائعة للجدار الناري. تحتفظ العديد من الجدران النارية البرمجية مثل UFW و firewalld بقواعدها rules ضمن ملف خاص يدعى iptables، ويمكن مطالعة المزيد عن هذا الموضوع بقراءة مقال كيفية إعداد جدار ناري باستخدام IPTables على Ubuntu 14.04، ومقال أساسيات IPTables - قواعد وأوامر شائعة للجدار الناري، إذ يساعد فهم iptables على التعامل مع حالات التعارض التي قد تنشأ من تسجيل بعض البرمجيات مثل Docker قواعدها الأمنية الخاصة بالتحكم بمنافذ الخادم ضمن الملف iptables إلى جوانب قواعد UFW المُسجلَّة فيه أيضًا. ملاحظة:توفر العديد من شركات الاستضافة خدمة جدار ناري خارجي يمكن تفعيله أمام الخادم السحابي لحمايته، بدلًا من الاعتماد فقط على الجدار الناري الموجود داخل نظام التشغيل، وهذا النوع من الجدران النارية يُدار من خلال لوحة التحكم الخاصة بمزود الخدمة، وتختلف أدوات التحكم فيه وطريقة إعداده من مزود لآخر. أهم ما يجب الانتباه إليه هو أن تكون الإعدادات الافتراضية للجدار الناري قائمة على مبدأ الحظر، أي أن يتم منع أي حركة مرور مجهولة أو غير مصرح بها بشكل تلقائي، فهذا الإجراء يحمي الخدمات الجديدة من الظهور على الإنترنت عن طريق الخطأ أو من أن تكون متاحة لأطراف غير مخولين. وعندما نقرر إطلاق خدمة جديدة، سيكون علينا حينها إعداد قواعد واضحة ومحددة في الجدار الناري تسمح بالوصول إليها وهذا يدفعنا بشكل طبيعي إلى التفكير المسبق في طريقة تشغيل الخدمة، وكيفية الوصول إليها، والتأكد من جميع الجوانب الأمنية قبل طرحها. شبكات السحابة الافتراضية الخاصة VPC Networks شبكات السحابة الافتراضية الخاصة Virtual Private Cloud -أو VPC اختصارًا- ههي شبكات مخصصة لربط موارد البنية التحتية في السحابة، وتوفر اتصالًا آمنًا ومعزولًا عن الإنترنت العام. كيف تعزز شبكات VPC أمان نظامنا عند حجز استضافة سحابية، يقوم بعض مزودي الخدمة بتخصيص واجهتين للشبكة تلقائيًا: واحدة عامة public network interface للتواصل مع الإنترنت، وأخرى خاصة private network interface للتواصل داخل الشبكة السحابية فقط. يمكننا تعطيل الواجهة العامة لبعض الخوادم والسماح لها بالتواصل فقط عبر الواجهة الخاصة. بذلك نحمي البيانات المتبادلة بين هذه الخوادم لأن الحركة لا تمر عبر الإنترنت العام، مما يقلل من خطر تعرضها للاختراق أو الكشف يساعدنا الاستخدام المدروس لعدد بوابات الإنترنت أو بوابات الدخول ingress gateways التي تعد نقاط الاتصال الوحيدة بين موارد شبكتنا السحابية الخاصة VPC والإنترنت العام على مراقبة حركة المرور العامة التي تتصل مع الموارد والتحكم بها بأفضل طريقة ممكنة، وتتوافق بعض أنظمة تنسيق الحاويات الحديثة مثل Kubernetes جيدًا مع مفهوم بوابات الدخول فلها إعداداتها الخاصة في هذا المجال، إذ تنشئ العديد من واجهات الشبكة الخاصة افتراضيًا، ويعود القرار لنا بما نريد إتاحته أو الكشف عنه منها. تطبيق شبكات VPC يتيح العديد من مزودي الخدمات السحابية المتخصصين بخدمات البنية التحتية إمكانية إنشاء مواردنا الخاصة وإضافتها إلى شبكة VPC حسب ما نريد لكن بالطبع على أنظمتهم الأساسية وداخل مراكز بياناتهم، أما بناء شبكتك الخاصة من VPC يدويًا من دون أي مزود خدمة فهو أمرٌ صعب وبتطلب إعدادات ومزايا متقدمة على الخادم ومعرفة عميقة بالشبكات، لذا يمكن استخدام أحد بدائل VPC الموجودة في المتناول وهو إعداد اتصال VPN بين الخوادم كما سنشرح في فقرتنا التالية. شبكات VPN والشبكات الخاصة الشبكة الافتراضية الخاصة virtual private network -أو VPN اختصارًا- هي وسيلة لإنشاء اتصالات آمنة بين الحواسيب البعيدة تكافئ الاتصالات ضمن شبكة محلية خاصة local private network، يساعدك ذلك على إعداد خدماتك كما لو أنها في شبكة خاصة وأيضًا على الاتصال مع خوادمنا البعيدة بطريقة آمنة. كيف تحسِّن شبكات VPN أمان النظام يُعَدُّ استخدام VPN طريقةً فعالة لبناء شبكة خاصة لا يراها إلَّا خوادمنا، وتتمتع الاتصالات ضمنها بالأمان والخصوصية التامة، يمكن تمرير حركة مرور بعض التطبيقات عبر الواجهة الافتراضية التي توفرها برمجيات VPN، وستكون عندها الخدمات الوحيدة المتاحة عبر الشبكة العامة هي تلك التي يُفترض بالعملاء أن يستخدموها عبر الإنترنت العام. تنفيذ شبكات VPN عمليًا يعتمد إعداد الشبكة الخاصة private network على التخطيط وضبط الإعدادات المناسبة لمشروعنا على التطبيقات والواجهات الشبكية والجدران النارية التي نستخدمها، وذلك في بداية العمل عند نشر الخوادم لأول مرة، بالمقابل يتطلب إعداد شبكات VPN ونشرها تثبيت أدوات إضافية على بنيتنا الحالية وبناء مسارات شبكة network routes إضافية، كما ينبغي أن يمتلك كل خادم في شبكة VPN الخاصة بنا البيانات والإعدادات التي تمَكِّنُه من تأسيس اتصال VPN مع مكونات الشبكة، وبعد إتمام كافة الإعدادات وتشغيل VPN بنجاح يمكن ضبط إعدادات تطبيقاتنا لتمرر كافة بياناتها عبر نفق VPN tunnel. ولمزيد من المعلومات ننصح بمطالعة المقالات الموجودة في قسم VPN. هناك العديد من الأدوات مفتوحة المصدر التي تساعدنا على بناء شبكات VPN مثل وايرجارد Wireguard وغيرها، وتتبع شبكات VPN عمومًا المبدأ نفسه الذي ناقشناه في فقرة VPC والخاص بتقييد الوصول إلى الخوادم السحابية بعدد محدود من المداخل ingress وذلك عبر تنفيذها سلسلة من واجهات الشبكة الخاصة خلف عدد قليل من نقاط الدخول entry points، ويمكننا تنفيذ التقنيتين معًا أي VPN ضمن بنية تحتية أساسها VPC من بناء شبكات VPN أكثر تخصيصًا. تدقيق الخدمات ومراجعتها يُعدّ تحليل الأنظمة المستخدمة وفهم ثغراتها واحتمالات الهجمات التي قد تتعرض لها من أهم تدابير الحماية التي تساعدك على تأمين الخودام وبقية مكونات النظام بأفضل طريقة ممكنة. ويُعدّ تدقيق الخدمات Service auditing ومراجعتها من أبرز أساليب هذا التحليل، فهو يوفر معلومات عن الخدمات العاملة على نظام معين، وأرقام المنافذ التي تستخدمها، والبروتوكولات التي تتخاطب بها وما إلى ذلك، وتساعدنا هذه المعلومات في ضبط إعدادات الخدمات الموجهة للعامة، وإعدادات الجدران النارية، وأيضًا في المراقبة، والإنذار بحدوث أي طارئ. كيف يساهم تدقيق الخدمات ومراجعتها بتعزيز أمان الأنظمة؟ تمثل كل خدمة داخلية أو عامة تعمل على نظام حاسوبي ثغرة أمنية محتملة ونقطة ضعف يمكن أن يستغلها المهاجمون، وبالتالي كلما ازداد عدد الخدمات الموجودة على خادمنا كلما ازدادت احتمالات اختراقه وما يتبعها من تأثيراتٍ سلبية على برمجياتنا. عند توفر فكرة أساسية عن الخدمات الشبكية العاملة على جهازنا يمكن البدء بتحليلها، وطرح الأسئلة التالية عن كل خدمة نود تدقيقها: هل من الضروري تشغيل هذه الخدمة هل تعمل هذه الخدمة على واجهات الشبكة الصحيحة أم على واجهات لا ينبغي لها العمل عليها هل ينبغي ربط هذه الخدمة بواجهة شبكة عامة أم خاصة هل قواعد الجدار الناري مضبوطة بطريقة صحيحة فتمرر حركة البيانات المسموحة فقط إلى هذه الخدمة وتمنع غير المسموحة هل النظام مجهز بآلية تنبيه تعلمنا بالتحذيرات الأمنية المرتبطة بنقاط ضعف كل خدمة من هذه الخدمات ننصح بإجراء هذا النوع من تدقيق الخدمات عند بناء أي خادم جديد، وتكرراه دوريًا كل بضعة أشهر مثلًا ليساعدنا على اكتشاف أي تغيير طرأ على الخدمات بقصد أو بغير قصد لنتخذ الإجراءات المناسبة حياله. تنفيذ تدقيق الخدمات ومراجعتها نستخدم الأمر ss لتدقيق خدمات الشبكة العاملة على نظامنا، إذ يعرض هذا الأمر كافة منافذ TCP و UDP المستخدمة على الخادم. لنلقِ نظرة على المثال التالي المتضمن استخدام هذا الأمر مع الخيارات الخاصة بإظهار اسم البرنامج، ورقم العملية PID، والعناوين الشبكية المستخدمة للاستماع أو لتلقي اتصالات TCP و UDP: $ sudo ss -plunt وهذه دلالة كل واحد من الخيارات p و l و u و n و t المستخدمة أعلاه: الخيار p مسؤول عن إظهار رقم العملية PID المقابل لكل مِقبس socket يعرض الخيار l المقابس الفعالة التي تستمع listening حاليًّا للاتصالات يعمل u على تضمين مقابس UDP في المعلومات المعروضة عن الخدمات بالإضافة إلى مقابس TCP يعرض n بيانات رقمية عن حركة مرور البيانات يعمل t على تضمين مقابس TCP في المعلومات المعروضة عن الخدمات بالإضافة إلى مقابس UDP وستحصل بعد تنفيذ الأمر على خرج يشبه التالي: Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=812,fd=3)) tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=69226,fd=6),("nginx",pid=69225,fd=6)) tcp LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=812,fd=4)) tcp LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=69226,fd=7),("nginx",pid=69225,fd=7)) الأعمدة الرئيسية في الخرج السابق هي: Netid و Local Address:Port و Process وهي الأجدر بالاهتمام، فإذا كانت قيمة العمود Local Address:Port هي 0.0.0.0 فإن الخدمة ستستقبل الاتصالات الواردة إليها على جميع واجهات الشبكة IPv4، وإذا كانت قيمته [::] فستتلقى الخدمة الاتصالات على جميع واجهات الشبكة من نوع IPv6، إذا طبقنا ذلك على المثال أعلاه سنجد أن كل من SSH و Nginx يستقبلان الاتصالات على جميع الواجهات العامة وبكلا النوعين IPv4 و IPv6. إذًا يمكن الآن أن نقرر فيما إذا كنا نرغب بإبقاء SSH و NGINX تستقبلان الاتصالات على كلا الواجهتين أم تغيير ذلك إلى واجهة واحدة، وبالمثل لبقية الخدمات. هذه هي الفائدة الرئيسية من تدقيق الخدمات بالإضافة إلى قدرتنا على كشف الخدمات التي تعمل على واجهات غير مستخدمة وضرورة تعطيلها. التحديثات التلقائية غير المراقبة يُعد التحديث المنتظم للخوادم وتطبيق الإصلاحات الأمنية بشكل دوري أمرًا أساسيًا للحفاظ على مستوى جيد من الأمان، حيث إن الخوادم غير المُحدَّثة أو الإصدارات القديمة من البرامج قد تحتوي على ثغرات أمنية معروفة للمهاجمين، مما يسهل عليهم استغلالها لاختراق الأنظمة. من خلال تفعيل التحديثات التلقائية أو التحديثات غير المراقبة Unattended Updates التي لا تتطلب تدخلاً بشريًا، يمكن ضمان تحديث معظم حزم النظام بشكل تلقائي وفوري، مما يساهم في معالجة الثغرات الأمنية وحماية النظام من مخاطرها. كيف تُحسّن التحديثات التلقائية أمان النظام تُحسن التحديثات التلقائية أمان النظام من خلال تقليل الفترات التي يكون فيها الخادم عرضة للاختراق بسبب الثغرات الأمنية المعروفة. فبفضل التحديثات التلقائية، يجري تثبيت الإصلاحات الأمنية التي تصدرها الشركات المطورة بشكل فوري، مما يحد من التأخير الذي قد يحدث عند تعطيل التحديثات. فحتى لو كان التأخير بسيطًا، فإنه يزيد من الفترة التي يكون فيها النظام عرضة للاستغلال. فمع التحديثات اليومية غير المُراقَبة، نضمن عدم تفويت أي تحديث أو إصلاح وتثبيته فور صدوره، مما يحسن أمان الخادم ويحميه من المخاطر الأمنية. تنفيذ التحديثات غير التلقائية على الخادم ننصح بقراءة مقال التحديث التلقائي لخادم أوبنتو فهو يعرفكم بأفضل استراتيجيات التحديثات التلقائية على نظام أوبنتو، سواء تحديثات الحزم أو نواة النظام kernel. البنية التحتية للمفتاح العام والتشفير بواسطة SSL أو TLS إن البنية التحتية للمفتاح العام Public key infrastructure -أو PKI اختصارًا- هي نظام مخصص لإنشاء وإدارة الشهادات certificates والتحقق من صحتها بهدف تحديد هويات الأفراد وتشفير الاتصالات بينهم. تُستخدم شهادات SSL أو TLS في البداية لإجراء الاستيثاق authentication بين مكونات النظام، ثم تُستخدم بعد ذلك لإنشاء اتصالات مشفرة بين هذه المكونات لضمان الأمان وحماية البيانات المتبادلة. دور PKI في تحسين أمان الأنظمة يساعد إنشاء سُلطَة تصديق الشهادات Certificate Authority -أو AC اختصارًا- في تحسين أمان النظام عن طريق السماح لكل خادم بالتحقق من هويات الخوادم الأخرى وتشفير البيانات المتبادلة معها. هذا يمنع هجمات الرجل في المنتصف man-in-the-middle حيث يحاول المهاجم انتحال شخصية خادم آخر لاعتراض البيانات. يُضبَط كل خادم ليثق في سُلطَة التصديق المركزية، وبالتالي يثق في الشهادات التي تصدرها هذه السُلطَة، مما يزيد الأمان ويحمي البيانات. إعداد بنية تحتية للمفتاح العام PKI في الواقع يتطلب إعداد سلطة لتصديق الشهادات وبناء البنية التحتية للمفتاح العام PKI جهدًا كبيرًا، كما أن إدارة الشهادات مثل إصدارها أو إبطالها قد تضيف عبئًا إداريًا إضافيًا، هذا الجهد لن يكون مفيدًا جدًا للمستخدم العادي، ولا يستحق كل هذه التكاليف، إلا إذا كان النظام كبيرًا ويزداد تعقيده مع الوقت، مما يجعل من الضروري وجود بنية خاصة لتأمينه. أما بالنسبة لمعظم المستخدمين، فإن استخدام الشبكات الخاصة الافتراضية VPN لتأمين الاتصال بين أجزاء النظام يُعد خيارًا مناسبًا ويوفر حماية كافية، إلى أن يكبر النظام ويصبح بحاجة فعلية لبنية مفاتيح عامة متقدمة. الخلاصة نستخلص من هذا المقال فكرتين: الأولى هي أن التدابير الأمنية الأساسية التي عرضناها وغيرها ينبغي أن تكون لبنة أساسية في أي نظام نبنيه مهما كان حجمه وتعقيده، ومن الضروي أن نفكر بها ونجهزها في بداية المشروع قبل نشر التطبيقات وتقديم الخدمات لإعداد بيئة آمنة قدر الإمكان. والثانية هي أنه لا يوجد أمان دائم في أي نظام تقني فإجراءات الحماية هي عمليات مستمرة ومتكررة وتخضع دائماً للمراجعة، وخاصةً بعد أي تغيير يطرأ على بنية النظام، لذا علينا أن نسأل أنفسنا دومًا عن التأثير الأمني الذي قد يقع عند إجراء أي تغيير مهما كان بسيطًا، ونسأل نفس السؤال قبل إطلاق أي خدمة أو ميزة جديدة. ترجمة -وبتصرف- لمقال Recommended Security Measures to Protect Your Servers لصاحبيه Justin Ellingwood و Alex Garnett. اقرأ أيضًا الهجمات الأمنية Security Attacks في الشبكات الحاسوبية كيفية تأمين الخوادم السحابية ضد هجمات حقن SQL ما هو الأمن السيبراني وما أهميته؟ مقدمة في تأمين خادم لينكس
  6. نناقش اليوم عدة طرق لتحديث خوادم أوبنتو تلقائيًا والاستفادة من ذلك في حماية الخوادم من الأخطاء والثغرات الأمنية وضمان عملها بأمان وموثوقية، وسنوضح استخدام بعض الأدوات والإعدادات التي تسهّل علينا إدارة تحديثات الخوادم وتصحيحها وإعادة تشغيلها، فإجراء تلك الأمور يدويًا أمر مرهق لمدير النظام وقد يتسبب بوقوع أخطاء. يشمل مقالنا ثلاثة أقسام: أفضل ممارسات إدارة التطبيقات لضمان إعادة تشغيلها بشكل صحيح بعد التحديث إعدادات الترقية التلقائية للمكتبات والحزم العاملة على الخادم التحديث والتصحيح المباشر لنواة النظام متطلبات العمل نحتاج خادم يعمل بنظام التشغيل أوبونتو، مع مستخدم عادي غير المستخدم الجذر root لكنه يتمتع بصلاحيات عالية sudo لتشغيل الأوامر الإدارية عند الحاجة، ويمكن تجهيز المطلوب باتباع خطوات مقال التهيئة الأولية لخادم أوبونتو. أفضل ممارسات إدارة التطبيقات عند ترقية الخادم، ستراودنا مخاوف أساسية تتعلق باستمرارية عمل التطبيقات بعد التحديث وإعادة التشغيل مثل كيف ستعمل تطبيقاتنا بعد ترقية الخادم؟ وهل سيعود الخادم لسلوكه الطبيعي بعد إعادة التشغيل اللاحقة للترقية؟ تشكل هذه الأسئلة هاجسًا لأي مدير النظام يسعى لتحديث خادمه وقد تجعله يتخلى عن قرار الترقية. كما سنحتاج للإجابة على هذه التساؤلات عند ضبط إعدادات الترقية التلقائية automatic upgrades على الخوادم ونتأكد من إعادة تشغيل جميع التطبيقات العاملة على الخادم بصورة سليمة بعد أي إعادة تشغيل أو توقف مفاجئ، علمًا أن أدوات إدارة حزم لينكس مصممة في الأساس لتعمل في الخلفية دون عرقلة أو أعباء صيانة إضافية على الخادم. نظريًّا ينبغي أن تُدار جميع التطبيقات العاملة على الخادم باستخدام نظام التمهيد init system ما أمكن ذلك، و systemd هي أداة التمهيد الرئيسية في أوبنتو وفي معظم توزيعات لينكس الأخرى حيث تتيح التفاعل مع الخدمات العاملة على الخادم وإعادة تشغيلها تلقائيًا حسب الحاجة باستخدام الأمر systemctl، بناءً على ذلك فإن أي برنامج مثبت على الخادم ويعمل في الخلفية سيأتي تلقائيًا مع خدمة systemd لإدارته. بالإضافة إلى ذلك، سينشأ ملف إعدادات خاص يسمى ملف إعدادت الوحدة unit file يحتوي على تعليمات تحدد كيفية تشغيل الخدمة وإيقافها وإدارتها بواسطة systemd. أما إذا كنا نشغل تطبيقًا خاصًا طورناه بنفسنا أو تطبيقًا ثبَّناه من مستودعات Git لا يتضمن ملف تمهيد للتكامل مع systemd، وسيصعب كتابة الملف يدويًّا ولن تكون نتائجه مضمونة، لذا يلجأ مدراء الأنظمة لاستخدام أدوات خاصة بإدارة التطبيقات مثل مدير العمليات supervisor لتوفير الوقت والجهد والاستغناء عن كتابة ملفات التمهيد يدويًا، كما يمكننا أيضًا استخدام جدولة المهام بواسطة cron مع الصيغة reboot@ لأتمتة العملية. ويمكن إعادة التشغيل باستخدام الأمر Sudo Shutdown now -r الذي يوقف جميع العمليات العاملة ويعيد تشغيل النظام مباشرةً، كما يمكننا جدولة إعادة التشغيل لتجري في وقت لاحق في ساعة ودقيقة معينة hh:mm بدل إعادة التشغيل الفوري، علمًا أن جميع الخدمات ونقاط الوصول endpoints الضرورية في بيئة الإنتاج ينبغي أن تعود للعمل بعد أي انقطاع طارئ. بعد أن عرضنا الطرق التي يمكن استخدامها لضمان إعادة تشغيل سليمة لجميع الخدمات أثناء عمليات الصيانة، لننتقل لشرح طريقة جدولة التحديثات التلقائية. إعدادات الترقية التلقائية يبدأ مدير حزم أوبنتو apt عمله بتحديث حزم النظام باستخدام apt update ثم ترقيتها بواسطة apt Upgrade التي ستعمل على ترقية جميع الحزم دون استثناء لأننا لم نحدد حزمة بعينها، تستخدم صيغة الأوامر هذه في الحالة المثالية لكنها ستختلف قليلًا وتتطلب إعدادات أكثر إذا كنا نستخدم حزمًا من مستودعات طرف ثالث غير مستودعات أوبنتو ونخشى من تعارضها مع حزم مستودعات أوبنتو، أو إذا كنا نحتفظ عمدًا بإصدارات محددة من بعض الحزم ولا نود ترقيتها لأنها تتوافق مع تطبيقاتنا مثلًا. يوفر أوبنتو أداة فريدة لتثبيت التصحيحات الأمنية patches والترقيات الأخرى غير المُراقبة التي لا تحتاج إلى تدخل بشري لإنجازها، تسمى unattended-upgrades وهي مثبتة تلقائيًا في معظم خوادم أوبنتو، ويمكن تثبيتها يدويًا باستخدام أوامر apt التالية: $ sudo apt update $ sudo apt install unattended-upgrades يمكن التأكد من عمل الخدمة unattended-upgrades باستخدام systemctl كما يلي: $ sudo systemctl status unattended-upgrades.service إذا كانت الخدمة تعمل، سنحصل على خرج يشبه التالي: unattended-upgrades.service - Unattended Upgrades Shutdown Loaded: loaded (/lib/systemd/system/unattended-upgrades.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2022-02-14 17:51:49 UTC; 3h 4min ago Docs: man:unattended-upgrade(8) Main PID: 829 (unattended-upgr) Tasks: 2 (limit: 1137) Memory: 10.6M CGroup: /system.slice/unattended-upgrades.service ستعمل الأداة unattended-upgrades بإعداداتها الافتراضية على تثبيت كافة الإصلاحات والتحديثات المتوفرة للحزم التابعة لمستودعات أوبنتو، أما إذا كنا نستخدم إصدارات أقدم من بعض الحزم وأردنا تجنب التعديلات على المستودع الأولي upstream changes أو كان خادمنا يستخدم مستودعات طرف ثالث تتبع لجهات خارجية إلى جانب مستودعات أوبنتو فسنحتاج عندها لتعديل إعدادات unattended-upgrades الافتراضية وضبطها بطريقة مختلفة. تُخَزَّن إعدادات هذه الأداة في الملف ‎/etc/apt/apt.conf.d/50unattended-upgrades ويمكننا فتحه وتحريره بواسطة أي محرر نصوص مثل نانو nano أو غيره كما يلي: $ sudo nano /etc/apt/apt.conf.d/50unattended-upgrades بمجرد فتح الملف، سنلاحظ أنه سهل القراءة ومشروح جيدًا، ويتضمن مجموعة من التعليقات التي تشرح التعليمات البرمجية ووظائفها، تتضمن كتلة التعليمات الأولى في الملف أسماء الحزم التي ستُحَدِّثها الأداة تلقائيًا ولها القالب العام لأسماء مستودعات حزم أوبنتو، وهي افتراضيًا ملفات المستودع المركزي core repository وملفات مستودع الحماية security-، أما مستودعات updates- و proposition- و backports- فتحديثها مُعطّل افتراضيًا ويُترك للمستخدم حرية تفعيلها من عدمه، لأنها قد تتضمن تعديلات جذرية على حزم النظام لذا نجد أن أسطرها تبدأ برمز التعليق //، ويمكن إزالته إن رغبتنا بتفعيل التحديثات التلقائية لها. إذا تابعنا تصفح الملف، سنجد عددًا من الأسطر التي يمكن التحكم بخياراتها باستخدام true أو false، على سبيل المثال يوجد خيار خاص بتفعيل إعادة التشغيل التلقائية بعد تثبيت الحزم التي تتطلب إعادة تشغيل كي تتفعل وتدخل حيز التنفيذ، يمكن تفعيل هذا الخيار بإزالة رمز التعليق // وتبديل القيمة false إلى true، لكن اتخاذ قرار كهذا سيعرضنا لانقطاعات غير متوقعة في الخدمة نتيجة إعادة التشغيل وينبغي أن تكون طبيعة تطبيقاتنا قادرة على تحمل التبعات الناتجة عن ذلك. // Automatically reboot *WITHOUT CONFIRMATION* if // the file /var/run/reboot-required is found after the upgrade //Unattended-Upgrade::Automatic-Reboot "false"; بعد الانتهاء من إجراء التعديلات، نحفظ التغييرات على الملف ونغلقه بالخطوات التي تناسب محرر النصوص المستخدم ففي nano مثلًا نضغط على Ctrl+X ثم Y وبعدها Enter. والآن نعيد تشغيل الخدمة unattended-upgrades وفق الأمر التالي لتصبح التعديلات سارية المفعول: $ sudo systemctl restart unattended-upgrades.service بهذا نكون قد انتهينا من شرح طريقة ضبط التحديثات الأمنية التلقائية لحزم النظام لتعمل تلقائيًا دون تدخل منا، سنناقش في الفقرة التالية تحديث نواة النظام والتعامل مع الحالات التي تقتضي إعادة التشغيل. التحديث والتصحيح المباشر لنواة النظام نواة نظام لينكس هي الجزء الأساسي من المظام حيث تحتوي على تعريفات معظم الأجهزة كالمعالج والذاكرة والطابعة وغيرها، وهي المسؤولة عن التفاعل منخفض المستوى بين العتاد الصلب والبرامج، وتصدر تحديثاتها بتواتر أقل من تحديثات الحزم الأخرى، وعادةً ما ترتبط بظهور ثغرة أمنية خطيرة ينبغي معالجتها، أو ظهور ميزة جديدة، وقد تكون ضرورية أيضًا في الحالات التي تصبح فيها النواة قديمة جدًا لدرجة نخشى معها وجود ثغرات أمنية وأخطاء متراكمة لا علم لنا بها. لا توجد منهجية جاهزة لجدولة تحديثات نواة نظام لينكس بصورة تلقائية فهذا الأمر يختلف من حالة لأخرى، فتحديثات النواة تتطلب إعادة تشغيل كاملة للنظام، ومنطقيًا لا يمكن جدولة أمر كهذا دون أخذ بيئة التشغيل الخاصة بكل خادم والخدمات التي يقدمها بالحسبان، فبعض الخوادم ينبغي أن تعمل على مدار اليوم والساعة دون انقطاع، وبعضها قد يأخذ وقتًا طويلًا لا يمكن التكهن به في إعادة التشغيل، أو قد يتطلب تدخلًا يدويًا لإتمام العملية أو غير ذلك، لذا لا يمكن جدولة التحديث ليجري تلقائيًا دون علم المستخدم وموافقته. أما إذا كنا لا نمانع بإجرائها تلقائيًا مع ما يترتب عليها من فترات انقطاع في الخدمة، فيمكننا ضبط ذلك في إعدادات التحديثات غير المُراقبة unattended updates بواسطة apt ليجري تحديث نواة النظام وتثبيت إصدارتها الجديدة مع بقية الحزم، وسيستخدم خادمنا النواة الجديدة المُحَدَّثة تلقائيًا بعد إعادة التشغيل، لكن عادةً ما يلجأ مديرو النظام في بيئات التشغيل الحقيقية لأساليب تضمن استمرارية تقديم الخدمة للعملاء مثل استخدام موازن حمل يوجه حركة مرور البيانات إلى الخوادم البديلة أثناء إعادة تشغيل الخادم الذي يُحَدِّث نواته، وتكرار العملية مع بقية الخوادم بالترتيب ليكتمل تحديث المجموعة كلها دون أن يشعر المستخدم بالانقطاع. تفعيل التصحيح المباشر Livepatch لإبقاء الخادم يعمل عند تحديث النواة يوفر نظام لينكس ميزة مفيدة تدعى التصحيح المباشر live patching تسمح لنا بتحديث النواة kernel دون إعادة التشغيل، ويُشرف على التصحيحات المباشرة -وفق هذه الميزة- أداتان أساسيتان هما: Canonical الخاصة بأنظمة أوبنتو حصرًا KernelCare التي تدعم معظم توزيعات لينكس الشهيرة إلى جانب أوبنتو علمًا أننا نحتاج لتسجيل حساب على موقع كلتا الأداتين لاستخدامهما، وتوفر Canonical إمكانية التسجيل والاستخدام المجاني للأفراد. يمكن تسجيل الدخول إلى Canonical والحصول على مفتاح خاص بالتصحيح المباشرة من هذا الرابط، ويمكننا بعدها تثبيت الحزمة canonical-livepatch بواسطة snap -وهو مدير حزم آخر يعمل إلى جانب apt في أنظمة أوبنتو- كما يلي: $ sudo snap install canonical-livepatch ثم نكتب الأمر التالي لتفعيل الحزمة canonical-livepatch، ولا ننسى استبدال your-key بمفتاحنا الخاص الذي حصلنا عليه من موقع Canonical: $ sudo canonical-livepatch enable your-key بعد التنفيذ ستظهر رسالة واضحة تعلمنا بنجاح تثبيت الخدمة Successfully enabled device وبذلك ستصبح canonical-livepatch خدمة عاملة على نظامنا، وستعمل في الخلفية ويمكن التأكد من ذلك بالاستعلام عن حالتها بواسطة canonical-livepatch status كما يلي: $ sudo canonical-livepatch status سنحصل على خرج يشبه التالي: last check: 50 seconds ago kernel: 5.15.0-25-generic server check-in: succeeded patch state: ✓ all applicable livepatch modules inserted patch version: 84.1 tier: updates (Free usage; This machine beta tests new patches.) machine id: 2565a9e7fc9f4405a167e4caf9b9dcf3 بمساعدة هذه الأداة نكون قد ضبطنا التحديثات التلقائية لنواة نظامنا كي تحدث من دون تدخل يدوي، ودون الحاجة لإعادة تشغيل النظام لنحافظ بذلك على بيئة تشغيل آمنة ومُحَدَّثة لتطبيقاتنا. الخلاصة تعرفنا في هذا المقال على جانب أساسي من مهام مهندسي DevOps وهو تحديث الخوادم والتعامل مع التحديثات التي تتطلب إعادة تشغيل، وتعلمنا الفرق بين تحديث حزم أوبنتو وتحديث نواة النظام، ويمكن التعمق أكثر في إدارة أوبنتو ومهام DevOps الأخرى بتحميل كتاب دليل إدارة خوادم أوبنتو مجانًا من أكاديمية حسوب، ومطالعة مقالات قسم DevOps أيضًا على الأكاديمية. ترجمة -وبتصرف- لمقال How to Keep Ubuntu 22.04 Servers Updated لصاحبه Alex Garnett. اقرأ أيضًا أساسيات إدارة الملفّات والتنقّل في لينكس الدليل الكامل لاستخدام أوامر apt في نظام لينكس تثبيت لينكس مع نظام ويندوز كيفية تثبيت التطبيقات في لينكس
  7. تُعدّ أتمتة المهام المتكررة هدفًا ضمنيًا لكل مستخدم يتعلم أساسيات باش، فهي في الغالب الغاية الأساسية من كل سكربت نكتبه، وسنعرض في هذا المقال مجموعة سكربتات مفيدة يمكنك الاستناد عليها لتتعلم أتمتة أي مهمة تريدها على خوادم لينكس، سنستخدم هنا جميع المهارات التي تعلمناها خلال السلسلة بدايةً من المصفوفات والجمل الشرطية وكذلك الحلقات والمفاهيم الأخرى. إنشاء سكربت باش لإدارة المستخدمين تُعدّ عملية إنشاء مستخدم على خوادم متعددة مهمة تقليدية لأي مدير نظام، لكنها مع التكرار تصبح مملة وشاقة، سننشئ معًا سكربت باش يؤتمت هذه العملية ويُنشئ المستخدمين تلقائيًا. سنُجهّز في البداية ملفًا نصيًّا يتضمن الأسماء hostnames أو عناوين IP للخوادم المطلوب إنشاء المستخدم عليها. يبين الملف servers.txt التالي على سبيل المثال أسماء خمسة خوادم: kabary@handbook:~$ cat servers.txt server1 server2 server3 server4 server5 لقد استخدمنا أسماء الخوادم هنا بدلًا من عناوين IP الخاصة بها لأن هذه العناوين مخزنة لدينا في الملف "etc/hosts/" الذي يتضمن الاسم hostname وعنوان IP المقابل له، وتستطيع أيضًا استخدام ملف إعدادات SSH. والآن لنفحص سكربت إنشاء المستخدمين adduser.sh التالي: #!/bin/bash servers=$(cat servers.txt) echo -n "Enter the username: " read name echo -n "Enter the user id: " read uid for i in $servers; do echo $i ssh $i "sudo useradd -m -u $uid $name" if [ $? -eq 0 ]; then echo "User $name added on $i" else echo "Error on $i" fi done عند تنفيذ السكربت سيطلب منا إدخال اسم المستخدم والمُعرِّف الخاص به، ثم سيتصل السكربت بكل خادم من الخوادم المحددة في الملف servers.txt السابق عبر اتصال SSH ويُنشئ عليه المستخدم الذي طلبناه. ننصح بمطالعة المزيد عن تقنية SSH للاتصال بالخوادم البعيدة عبر مشاهدة الفيديو التالي: لنلاحظ الآن طريقة عمل السكربت بعد التنفيذ: بهذا نكون قد نجحنا في إنشاء مستخدم يدعى ansible على الخوادم الخمسة المطلوبة. يوجد نقطتان مهمتان للغاية يتوجب علينا فهمها عند التعامل مع سكربت من هذا النوع: ضرورة استخدم مفاتيح مرور pass phrases فارغة لجلسات ssh، أو استخدم الوكيل ssh-agent لتمرير هويتنا إلى الخادم البعيد حتى لا نضطر لإدخال كلمة المرور في أثناء عمل السكربت الحرص على امتلاك حساب مستخدم فعال وعالي الصلاحيات يتمتع بأذونات الوصول إلى جميع الخوادم المطلوبة بدون متطلبات تتعلق بكلمة المرور تخيل أنك تحتاج لتعرف مستخدمين جدد على مائة خادم مثلًا، فكم ستكون العملية طويلة وشاقة، هنا تكمن أهمية الأتمتة إذ توفر عليك ساعات من العمل المضني. أتمتة عمليات النسخ الاحتياطي النسخ الاحتياطي مهمة أساسية لا غنى عنها في كل منظومة، وكثيرًا ما نستخدم سكربتات باش لأتمتة المهام، يبين السكربت backup.sh التالي مثالًا عليها: #!/bin/bash backup_dirs=("/etc" "/home" "/boot") dest_dir="/backup" dest_server="server1" backup_date=$(date +%b-%d-%y) echo "Starting backup of: ${backup_dirs[@]}" for i in "${backup_dirs[@]}"; do sudo tar -Pczf /tmp/$i-$backup_date.tar.gz $i if [ $? -eq 0 ]; then echo "$i backup succeeded." else echo "$i backup failed." fi scp /tmp/$i-$backup_date.tar.gz $dest_server:$dest_dir if [ $? -eq 0 ]; then echo "$i transfer succeeded." else echo "$i transfer failed." fi done sudo rm /tmp/*.gzecho "Backup is done." لنشرح السكربت أعلاه: أنشأنا في البداية مصفوفة تدعى backup_dirs سنكتب فيها أسماء المجلدات التي نريد أخذ نسخ احتياطية عنها، ثم عرّفنا ثلاثة متغيرات: dest_dir: لتحديد مجلد الوجهة الذي ستُحفظ فيه النسخ الاحتياطية dest_server: لتعيين الخادم الهدف backup_time: لتحديد تاريخ بدء عملية النسخ الاحتياطي وبعدها أنشأنا حلقة for ستمر على المجلدات الموجودة في المصفوفة "backup_dirs" واحدًا واحدًا وتضغط كل مجلد منها باستخدام التعليمة tar وتحتفظ بالنسخة المضغوطة في المجلد المؤقت tmp/، ثم تنسخها إلى الخادم الهدف بواسطة التعليمة scp، وبعد اكتمال النسخ ستُحذف الملفات المضغوطة من المجلد tmp/. يبين الخرج التالي أمثلة على طريقة عمل السكربت: kabary@handbook:~$ ./backup.sh Starting backup of: /etc /home /boot /etc backup succeeded. etc-Aug-30-20.tar.gz 100% 1288KB 460.1KB/s 00:02 /etc transfer succeeded. /home backup succeeded. home-Aug-30-20.tar.gz 100% 2543KB 547.0KB/s 00:04 /home transfer succeeded. /boot backup succeeded. boot-Aug-30-20.tar.gz 100% 105MB 520.2KB/s 03:26 /boot transfer succeeded. Backup is done. يمكننا جدولة السكربت باستخدام مهام cron job لتبدأ أعمال النسخ الاحتياطي عند منتصف الليل مثلًا، وذلك وفق التالي: kabary@handbook:~$ crontab -e 0 0 * * * /home/kabary/scripts/backup.sh يمكن معرفة المزيد عن جدولة المهام بواسطة cron بالاطلاع على الفيديو التالي على قناة أكاديمية حسوب: مراقبة المساحات التخزينية المتوفرة على القرص الصلب المساحات التخزينية على نظام الملفات في تناقص دائم وهذا أمرٌ مفروغٌ منه، وكل ما نستطيع القيام به تجاه هذا الأمر هو المراقبة والتصرف بالوقت المناسب حتى يؤدي التناقص المستمر إلى نفاذ المساحة وتعطل النظام، يساعدنا الأمر df على عرض المساحات المتوفرة على أي نظام ملفات، وهذا مثال على استخدامه: kabary@handbook:~$ df -h / /apps /database Filesystem Size Used Avail Use% Mounted on /dev/sda5 20G 7.9G 11G 44% / /dev/mapper/vg1-applv 4.9G 2.4G 2.3G 52% /apps /dev/mapper/vg1-dblv 4.9G 4.5G 180M 97% /database كما نلاحظ هنا فالمساحة المخصصة لقاعدة البيانات database/ على نظام الملفات هذا تكاد تنفذ فنسبة استخدامها 97%، يمكنك عرض نسبة الاستخدام فقط بدون التفاصيل الأخرى بواسطة الأمر awk. دعونا لنستعمل الآن هذين الأمرين في السكربت disk_space.sh كما يلي: #!/bin/bash filesystems=("/" "/apps" "/database") for i in ${filesystems[@]}; do usage=$(df -h $i | tail -n 1 | awk '{print $5}' | cut -d % -f1) if [ $usage -ge 90 ]; then alert="Running out of space on $i, Usage is: $usage%" echo "Sending out a disk space alert email." echo $alert | mail -s "$i is $usage% full" your_email fi done يبدأ السكربت بإنشاء مصفوفة تدعى filesystems نكتب فيها أسماء مسارات أنظمة الملفات التي نود مراقبتها، ويوجد بعدها حلقة تكرار تُنَفذ عند كل عنصر من عناصر المصفوفة، وتستعلم عن نسبة استخدام المساحة التخزينية المخصصة له فإذا كانت أكبر من 90% سيُرسل لنا السكربت تنبيهًا عبر البريد الإلكتروني يخبرنا بأن المساحة على وشك النفاذ. ملاحظة: لاستخدام السكربت السابق يتوجب استبدال your_email بعنوان بريد إلكتروني حقيقي. سنحصل على هذا الخرج بعد تنفيذ السكربت: kabary@handbook:~$ ./disk_space.sh Sending out a disk space alert email. وستكون رسالة البريد الإلكتروني مشابهة للتالي: لنفترض الآن أننا نرغب بجدولة السكربت disk_space.sh ليعمل كل ست ساعات مرة باستخدام مهام cron، فسنكتب التالي: kabary@handbook:~$ crontab -e 0 */6 * * * /home/kabary/scripts/disk_space.sh الخاتمة وصلنا لنهاية مقالنا الذي عرضنا لكم فيه أمثلة بسيطة توضح أتمتة المهام بواسطة سكربتات باش، إن الإمكانات الفعلية لباش أكبر بكثير، لذا ندعوكم لتجربة تطوير السكربتات الموجودة هنا وتنفيذ أفكار أخرى مختلفة لتقوية مهاراتكم في باش واكتساب القدرة على أتمتة أي عمل تحتاجه على خوادم لينكس، كانت هذه نصيحتنا في ختام سلسلتنا التعليمية عن باش، نذكرك أخيرًا برابط سلسلة تعلم البرمجة باستخدام باش . ترجمة -وبتصرف- للمقال Automation With Bash. اقرأ أيضًا المقال السابق: استخدام الدوال في باش Bash استخدام المعاملات الحسابية في سكربتات باش Bash عمليات السلاسل النصية في باش Bash الجمل الشرطية في باش Bash الحلقات في باش Bash
  8. تساعد الدوال البرمجية على تنظيم سكربتات باش وجعلها أسهل في القراءة، وخاصة السكربتات كبيرة الحجم، إذ يمكننا استدعاء الدالة لأداء المهمة نفسها في عدة مواضع داخل السكربت دون الحاجة لتكرار كتابة التعليمات البرمجية الخاصة بهذه المهمة أكثر من مرة. سنتعلم في هذا المقال كيفية إنشاء الدوال البرمجية Functions في سكربتات باش وتمرير الوسطاء إليها وإرجاع النتائج منها، كما سنتعرف على الفرق بين المتغيرات المحلية والمتغيرات العامة، وعلى ماهية الدوال العودية Recursive Functions وكيفية تحقيقها في باش. إنشاء الدوال في باش توجد صيغتان للتصريح عن الدوال البرمجية في باش، وتُعدّ الصيغة التالية هي الأكثر استخدامًا: function_name () { commands } أما الصيغة الثانية الأقل شهرة، فهي تبدأ بالكلمة المفتاحية function يليها اسم الدالة كما يلي: function function_name { commands } ينبغي الانتباه إلى الأساسيات التالية عن التعامل مع الدوال: لا تعمل الدالة أبدًا ما لم نقم باستدعائها لا يمكننا استدعاء الدالة قبل تعريفها، لذا نجد أن تعريف الدالة في تسلسل تعليمات السكربت يأتي قبل أي استدعاء لها نستدعي الدالة بكتابة اسمها فقط نمرر الوسيط إن وجد مباشرة بعد اسم الدالة لنلقِ نظرة على السكربت fun.sh التالي: #!/bin/bash hello () { echo "Hello World" } hello hello hello عرّفنا في بداية السكربت دالة تدعى hello وظيفتها عرض العبارة Hello World على الطرفية Terminal، ثم استدعينا الدالة ثلاث مرات بكتابة اسمها، لذا عند تنفيذ السكربت ستظهر العبارة Hello World على الشاشة ثلاث مرات كالتالي: kabary@handbook:~$ ./fun.sh Hello World Hello World Hello World إرجاع القيم من الدالة في باش لا تُرجع لنا دوال باش قيمًا عند استدعائها على عكس السائد في معظم لغات البرمجة، فعند انتهاء التنفيذ الدالة تُرجع حالة الخروج من آخر أمر مُنفذ وتُخَزَّن الحالة في المتغير الخاص ‎‏‎‏‏‎‏?$، فإذا كان التنفيذ ناجحًا سيأخذ المتغير ‎‏‏‎‏?$ القيمة صفر، وإذا فشل التنفيذه فسيأخذ قيمة عدد صحيح موجب آخر يقع ضمن المجال [1-255] حسب سبب الفشل. لكن يمكننا استخدام التعليمة return لتغيير حالة الخروج من الدالة كما يوضح السكربت error.sh التالي: #! /bin/bash error () { blabla return 0 } error echo "The return status of the error function is: $?" إذا شغلنا السكربت السابق سنحصل على الخرج التالي: kabary@handbook:~$ ./error.sh ./error.sh: line 4: blabla: command not found The return status of the error function is: 0 الكلمة blabla التي كتبناها في جسم الدالة () error ما هي إلّا كلمة عشوائية لا تمثل أي أمر برمجي، لذا فالتعليمية return 0 هي السبب في حصولنا على حالة خروج صفرية من الدالة () error أي حالة خروج ناجحة، وبدونها ما كانت الدالة ستعطينا هذه النتيجة لأن blabla حتمًا سترجع رسالة خطأ مفادها لم يتم العثور على الأمر. رغم أن دوال باش لا تعيد قيمًا، فقد ساعدتنا طريقة الكتابة السابقة على تغيير حالة الخروج من الدالة ونجاح عملية تنفيذها، وبدونها لن تتمكن الدالة من إرجاع قيمة للبرنامج المستدعي أي لن نحصل على نتيجة تنفيذ الدالة لأن أي رمز خروج غير الصفر يشير إلى وجود خطأ. ملاحظة: لنتذكر دائمًا أن return تعني إنهاء تنفيذ الدالة والخروج منها. تمرير الوسطاء إلى دالة باش يشبه تمرير الوسطاء إلى دوال باش كثيرًا تمرير الوسطاء إلى سكربتات باش، فكل ما يتطلبه الأمر كتابة الوسطاء أو سردها إلى جانب اسم الدالة عند استدعائها. يوضح السكربت iseven.sh التالي طريقة القيام بذلك: #!/bin/bash iseven () { if [ $(($1 % 2)) -eq 0 ]; then echo "$1 is even." else echo "$1 is odd." fi } iseven 3 iseven 4 iseven 20 iseven 111 تُميّز الدالة () iseven في الكود أعلاه بين الأعداد الزوجية والأعداد الفردية، وقد استدعيناها أربع مرات في السكربت وفي كل استدعاء مررنا لها عددًا مختلفًا، وطالما أننا كتبنا الوسيط مباشرة بعد الدالة فهو الوسيط الأول وسيُشير له المتغير $1. لنختبر الآن طريقة عمل السكربت: kabary@handbook:~$ ./iseven.sh 3 is odd. 4 is even. 20 is even. 111 is odd. وسطاء الدالة مغايرين لوسطاء السكربت، وكل منهم يعمل في مستوى خاص مختلف عن الآخر، سيبين السكربت التالي funarg.sh الفرق: #!/bin/bash fun () { echo "$1 is the first argument to fun()" echo "$2 is the second argument to fun()" } echo "$1 is the first argument to the script." echo "$2 is the second argument to the script." fun Yes 7 والآن لنشغل السكربت مع تمرير وسيطين له، ونلاحظ النتيجة: kabary@handbook:~$ ./funarg.sh Cool Stuff Cool is the first argument to the script. Stuff is the second argument to the script. Yes is the first argument to fun()7 is the second argument to fun() لقد استخدمنا المتغيرين $1 و $2 لوظيفتين، للتعبير عن الوسيطين الأول والثاني مرة للدالة ومرة للسكربت، وعند استدعائهما من داخل الدالة كان لهما معنى مختلف عن وسطاء السكربت. المتغيرات المحلية والمتغيرات العامة داخل سكربتات باش تكون متغيرات باش عامة Global Variables أو محلية Local Variables، يمكننا استخدام المتغيرات العامة على مستوى السكربت كاملًا، أما المتغيرات المحلية فلا تستخدم إلّا ضمن نطاق الدالة. يوضح السكربت domain.sh bash التالي الفرق بينهما: #!/bin/bash v1='A' v2='B' myfun() { local v1='C' v2='D' echo "Inside myfun(): v1: $v1, v2: $v2" } echo "Before calling myfun(): v1: $v1, v2: $v2" myfun echo "After calling myfun(): v1: $v1, v2: $v2" عرّفنا في بداية السكربت متغيرين عامين Global هما v1 و v2، ثم في داخل الدالة ()myfun عرّفنا متغير محلي Local يدعى v1 باستخدام الكلمة المفتاحية local، وعدّلنا قيمة المتغير العام v2، نلاحظ أننا نستطيع استخدام الاسم نفسه لمتغيرات محلية مختلفة في دوال مختلفة. إذا شغّلنا السكربت الآن سنحصل على النتيجة التالية: kabary@handbook:~$ ./scope.sh Before calling myfun(): v1: A, v2: B Inside myfun(): v1: C, v2: D After calling myfun(): v1: A, v2: D نستنتج من المثال السابق ما يلي: إذا كان لدينا متغير محلي ومتغير عام لهما الاسم نفسه، فإن المتغير المحلي يتمتع بالأولوية داخل الدالة يمكننا تعديل قيمة متغير عام من داخل الدالة الدوال العودية Recursive Functions الدالة العودية recursive function هي دالة تستدعي نفسها مرات عدة حتى الوصول للشرط المطلوب، وهي تفيدنا في التعامل مع المسائل البرمجية التي يمكن تقسيمها إلى مسائل أصغر مشابهة لها. تُعدّ دالة حساب العاملي factorial function مثال تقليدي على التعادوية، يوضحه السكربت factorial.sh التالي: #!/bin/bash factorial () { if [ $1 -le 1 ]; then echo 1 else last=$(factorial $(( $1 -1))) echo $(( $1 * last )) fi } echo -n "4! is: " factorial 4 echo -n "5! is: " factorial 5 echo -n "6! is: " factorial 6 تبدأ كل دالة عودية بتعريف حالة أساسية أو حدّية base case وعند الوصول إليها تنتهي الاستدعاءات الذاتية للدالة أي تنتهي العودية، والحالة الحدّية للدالة ()factorial في مثالنا السابق الواحد حيث أن عاملي العدد 1 هو العدد 1: if [ $1 -le 1 ]; then echo 1 لنوضح الآن الحالة العودية في دالة حساب العاملي: إن حساب العاملي لأي عدد صحيح موجب مثل n يساوي قيمة العدد n مضروبًا بحساب العاملي للعدد الأصغر منه n-1 وهكذا وفق المعادلة التالية: factorial(n) = n * factorial(n-1) وقد استخدمنا هذه المعادلة في كتابة الحالة العودية recursive case للدالة السابقة وفق التالي: last=$(factorial $(( $1 -1))) echo $(( $1 * last )) لنشغّل السكربت ونتأكد من صحة النتائج: kabary@handbook:~$ ./factorial.sh 4! is: 24 5! is: 120 6! is: 720 يمكن تجربة أفكار أخرى لإتقان مفهوم الدوال العودية، لنحاول مثلًا حساب قيمة سلسلة فيبوناتشي لعدد معين، علينا أن نحدد في البداية الحالة الأساسية ثم الحالة العودية وبعدها نكتب السكربت. الخاتمة وصلنا إلى ختام مقالنا الذي شرحنا فيه أهمية الدوال البرمجية التقليدية والدوال التعاودية وحالات استخدامها في سكربتات باش، نأمل أنه كان مفيدًا، تابع مقالنا التالي والأخير حيث سنطبق فيه كل المبادئ التي تعلمناها على في كافة مقالات سلسلة تعلم باش. ترجمة -وبتصرف- للمقال Using Functions in Bash. اقرأ أيضًا المقال السابق: الحلقات في باش Bash أنشئ برنامجك النصي الأول على صدفة باش Bash استخدام الدوال في سكربات الصدفة Shell Scripts عمليات السلاسل النصية في باش Bash
  9. الحلقات Loops ضرورية في أي لغة برمجة، ولها حالات استخدام متعددة في سكربتات باش سنتعلمها معًا في هذا المقال، وسنتعرف على حلقات For و While و Until، وعلى كيفية التحكم بالحلقات باستخدام تعليمات break و continue، بالإضافة لطريقة كتابة الحلقات اللانهائية، وكيف أنها قد تشير في بعض الأحيان لوجود أخطاء في كتابة السكربت. حلقات For في باش حلقة for هي واحدة من ثلاثة أنواع من الحلقات التكرارية التي يمكننا استخدامها في سكربتات باش، وهي تكتب بطريقتين: حلقة for المكتوبة بأسلوب لغة C حلقة for المعتمدة على قائمة أو مجال محدد من العناصر كتابة حلقة for بأسلوب لغة C في سكربتات باش نكتب هذا النوع من حلقات for بالطريقة نفسها التي نكتبها في لغات البرمجة C و ++C وفق الصيغة العامة التالية: for ((initialize ; condition ; increment)); do [COMMANDS] done إذا طبقنا الصيغة العامة السابقة على المثال أدناه، فإن تنفيذ الحلقة الموجودة فيه سيعرض عبارة "Hello Friend" عشر مرات على شاشة الطرفية: for ((i = 0 ; i < 10 ; i++)); do echo "Hello Friend" done لنفسر الشيفرة السابقة: القيمة الابتدائية لمتغير الحلقة هي 0، وشرط التكرار أن لا تتجاوز قيمة المتغير العدد 10، ومقدار الزيادة هو 1، وبالتالي في كل مرة يتحقق فيها شرط الحلقة سيُنفذ الأمر البرمجي وهو في حالتنا عرض العبارة Hello Friend على الشاشة وتزداد قيمة متغير الحلقة i بمقدار واحد، ثم يُفحص الشرط من جديد وهكذا حتى الوصول للحد الأعلى أي 10. وهذه نتيجة تنفيذ الحلقة: kabary@handbook:~$ bash hello.sh Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend كتابة حلقة for المعتمدة على القوائم في سكربتات باش تفيدنا حلقة for المكتوبة بهذه الطريقة في الحالات التي نتعامل فيها مع قائمة معروفة العناصر، مثل: مجموعة ملفات أو مجموعة سلاسل نصية، أو مجال محدد من الأعداد، أو مصفوفة، أو ربما مخرجات ثابتة لأمر معين أو غير ذلك، ولها الصيغة العامة التالية: for item in [LIST]; do [COMMANDS] done يبين المثال أدناه الحلقة نفسها التي تعاملنا معها في الفقرة السابقة ولكنها مكتوبة هذه المرة بطريقة القائمة: for i in {1..10}; do echo "Hello Friend" done وهذا مثال آخر حيث يعرض السكربت var.sh التالي جميع الملفات الموجودة في المجلد var/: #!/bin/bash for i in /var/*; do echo $i done وسنحصل في الخرج على نتيجة تشبه ما يلي: kabary@handbook:~$ ./var.sh /var/backups /var/cache /var/crash /var/lib /var/local /var/lock /var/log /var/mail /var/metrics /var/opt /var/run /var/snap /var/spool /var/tmp حلقات While في باش هذه هي الصيغة العامة لحلقة while في باش: while [ condition ]; do [COMMANDS] done لنطبق مثالًا عمليًّا: يعرض السكربت التالي المضاعفات العشرة الأولى للعدد 3: #!/bin/bash num=1 while [ $num -le 10 ]; do echo $(($num * 3)) num=$(($num+1)) done وسيكون الخرج كما يلي: kabary@handbook:~$ ./3x10.sh 3 6 9 12 15 18 21 24 27 30 لنُحلّل السكربت أعلاه، في البداية أعطينا المتغير num القيمة الابتدائية 1، وحددنا شرط تكرار الحلقة بأن لا تتجاوز قيمة المتغير العدد 10، وبناءً على ذلك سيُنَفَذ الأمران الموجودان في جسم الحلقة وهما أولًا إظهار ناتج ضرب قيمة المتغير num بالعدد 3 على الشاشة، وثانيًا زيادة قيمة num بمقدار 1، ويُعاد تنفيذهما مرارًا وتكرارًا طالما أن شرط الحلقة محقق. حلقات Until في باش النوع الثالث من الحلقات التكرارية باش هو until، وليس الحلقة do-while التي اعتاد عليها مبرمجو لغة C و ++C، وكما نلاحظ أدناه فالصيغة العامة لحلقة until مطابقة تمامًا لصيغة كتابة حلقة while: until [ condition ]; do [COMMANDS] Done ويكمن الفرق بينهما في طريقة تعامل كل حلقة مع نتيجة اختبار الشرط فيما إذا كان محققًا أو غير محقق، ففي الحلقة while يستمر التنفيذ طالما أن شرط الحلقة محقق، وعلى العكس تمامًا في الحلقة until إذ يستمر التنفيذ طالما أن الشرط غير محقق. لنكتب الآن السكربت السابق باستخدام الحلقة until بدلًا من while، دقق جيدًا في الشرط فهو جوهر الاختلاف بين السكربتين: #!/bin/bash num=1 until [ $num -gt 10 ]; do echo $(($num * 3)) num=$(($num+1)) done الشرط هنا هو أن تكون قيمة المتغير num أكبر من 10، بينما كان الشرط في سكربت while هو أن تكون قيمة المتغير num أصغر من 10وهما كما تلاحظ عكس بعضهما تمامًا. حالات شائعة لاستخدام الحلقات في باش بعد أن تعرفنا على أساسيات الحلقات، سنعرض بعضًا من أشهر استخداماتها في باش: إظهار عناصر مصفوفة بواسطة سكربت باش تُعدّ الحلقة for خيارًا مناسبًا لعرض عناصر أي مصفوفة في باش، يمكن الرجوع لمقالنا استخدام المصفوفات في باش Bash لمعرفة كيفية إنشاء مصفوفات باش والتعامل معها. ألقِ نظرة على السكربت prime.sh التالي، إنه يتضمن مصفوفة تسمى prime وحلقة for تمر على عناصر المصفوفة واحدًا تلو الآخر وتُظهرهم على الشاشة بواسطة الأمر echo: #!/bin/bash prime=(2 3 5 7 11 13 17 19 23 29) for i in "${prime[@]}"; do echo $i done وسيكون الخرج كما يلي: kabary@handbook:~$ ./prime.sh 2 3 5 7 11 13 17 19 23 29 استخدام break و continue في حلقات باش تساعدنا تعليمات break و continue على إنهاء تنفيذ الحلقة قبل الأوان أو تخطي بعضًا من تكراراتها لأغراض تخدم برنامجنا. توقف التعليمة break تنفيذ الحلقة وتنقل التنفيذ إلى التعليمة التالية في البرنامج، فعلى سبيل المثال ستعرض الحلقة التالية الأعداد من واحد إلى ثلاثة فقط على شاشة الطرفية: for ((i=1;i<=10;i++)); do echo $i if [ $i -eq 3 ]; then break fi done وتساعدنا التعليمة continue على تجاوز بعض التكرارات، فكلما وصل تسلسل التنفيذ إلى continue تُهمَل الأوامر التي تليها والتي تقابل قيمًا معينة للمتغير نود تجاوزها، ويُكرر تنفيذ الحلقة بعد زيادة قيمة المتغير. يبين السكربت odd.sh التالي كيفية استخدام continue لتخطي الأعداد الزوجية وإظهار الأعداد الفردية فقط من بين الأعداد من صفر إلى عشرة: #!/bin/bash for ((i=0;i<=10;i++)); do if [ $(($i % 2)) -ne 1 ]; then continue fi echo $i done وستحصل على هذا الخرج: kabary@handbook:~$ ./odd.sh 1 3 5 7 9 الحلقات اللانهائية في باش الحلقات اللانهائية Infinite Loops هي حلقات دائمة التكرار بدون نهاية لأن شرط تكرارها محقق دائمًا. قد يبني المستخدم حلقات لانهائية عن قصد لأهداف معينة، لكن معظم الحلقات النهائية في واقع الأمر تظهر نتيجة أخطاء بشرية غير مقصودة. الهدف من إنشاء الحلقة التالية مثلًا هو إظهار الأرقام من واحد إلى عشرة على شاشة الطرفية لكن بترتيب تنازلي، إذا دققنا فيها سنكتشف خطأ يوقع المستخدم ضحية تكرار لا نهائي للحلقة: for ((i=10;i>0;i++)); do echo $i done إذا دققنا في الكود أعلاه سنلاحظ أن مُنشئ الحلقة هنا يزيد قيمة المتغير i بمقدار واحد بعد كل تنفيذ بدلًا من إنقاصها بمقدار واحد ليصل في النهاية إلى القيمة صفر وهي شرط انتهاء الحلقة، فحتى تعمل الحلقة بالصورة المطلوبة علينا استبدال ++i بالتعليمة --i لتصبح كما يلي: for ((i=10;i>0;i--)); do echo $i done قد تفرض علينا متطلبات العمل إنشاء حلقات لا نهائية لانتظار تحقق شروط معينة على النظام مثلًا أو لأسباب أخرى، وبصرف النظر عن السبب إذا احتجنا لإنشاء هذا النوع من الحلقات فيمكننا ذلك باستخدام إحدى الصيغ التالية. صيغة بناء حلقة لا نهائية باستخدام حلقة for: for ((;;)); do [COMMANDS] done صيغة بناء حلقة لا نهائية باستخدام حلقة while: while [ true ]; do [COMMANDS] done الخلاصة انتهى مقالنا الخاص بالحلقات التكرارية في سكربتات باش، عرضنا فيه الحلقات الأساسية الثلاث مع بعض الأمثلة العملية ووضحنا حالات الاستخدام الخاصة بكل حلقة، ندعوك لمتابعة المقال التالي حول الدوال في باش وتطبيق المزيد من المفاهيم المتقدمة. ترجمة -وبتصرف- للمقال Loops in Bash. اقرأ أيضًا المقال السابق: الجمل الشرطية في باش Bash كيف تستخدم بنى التحكم في سكربتات الصدفة Shell Scripts - الجزء 1 كيف تستخدم بنى التحكم في سكربتات الصدفة Shell Scripts - الجزء 2 كيف تستخدم بنى التحكم في سكربتات الصدفة Shell Scripts - الجزء 3
  10. سنتعلم في هذا المقال كيفية استخدام الجمل والعبارات الشرطية الشائعة مثل if و else و case لكتابة سكربتات باش فعالة، ونتعرف على طريقة التعامل مع سيناريوهات متعددة واتخاذ القرارات المناسبة من خلالها. استخدام الجملة الشرطية if في باش يعد الشرط if العنصر الأساسي في أي بنية برمجية لاتخاذ القرار، ويمكن كتابة الشرط في باش وفق الصيغة العامة التالية: if [ condition ]; then your code fi نلاحظ أن كل عبارات if الشرطية تبدأ في باش بالأمر if وتنتهي بالأمر fi أي if معكوسة. ويجب الانتباه للمسافات الفارغة عند كتابة الجمل الشرطية فهي ذات أهمية، وهذه مواضعها: توجد مسافة فارغة قبل الشرط condition المكتوب بين قوسين وبعده، وإذا أهملناها سنحصل على خطأ توجد مسافة فارغة قبل وبعد العوامل الشرطية مثل = أو == أو => وعدم الالتزام بها سيعطينا رسالة خطأ مفادها unary operator expected أي يتوقع تشغيل عامل فردي. لنجرب الآن كتابة سكربت بسيط باسم root.sh، يعرض على الشاشة عبارة "You are root" في حالة واحدة فقط إذا كان المستخدم الذي ينفذ السكربت هو الجذر root: #!/bin/bash if [ $(whoami) = 'root' ]; then echo "You are root" fi اعتمدنا في هذا السكربت على الأمر whoami الذي يعطينا اسم المستخدم مُنفّذ السكربت، وكتابته ضمن قوسين هلاليين مع رمز الدولار بالصيغة التالية (command)$ فتعني حصولنا على خرج الأمر بهيئة متغير، تدعى هذه العملية تعويض الأوامر command substitutions وقد تعرفنا عليها في مقال المتغيرات في سكربتات الصدفة باش Bash. إذًا سيتحقق الشرط الوارد في جملة if ويطبع العبارة المطلوبة في حالة واحدة فقط وهي عندما ينفذ السكربت المستخدم الجذر root، وتظهر الصورة التالية نتيجة التنفيذ: استخدام الجملة الشرطية if-else في باش يمكننا تضمين أي تعليمة برمجية نريد تنفيذها عندما لا يتحقق الشرط if في الجملة الشرطية else فإذا عدنا للسكربت السابق root.sh مثلًا، وعدّلنا محتواه حتى يعطينا خرجًا معينًا عند عدم تحقق الشرط، فالسكربت في وضعه الحالي لا يظهر لنا أي نتيجة عند التنفيذ من مستخدم آخر غير الجذر وعندما نعدله على النحو التالي: #!/bin/bash if [ $(whoami) = 'root' ]; then echo "You are root" else echo "You are not root" fi ونحاول تشغيل السكربت من حساب المستخدم العادي، سنحصل على تنبيه يخبرنا أن المستخدم ليس المستخدم الجذر كما يلي: kabary@handbook:~$ ./root.sh You are not root استخدام الجملة الشرطية else-if في باش تساعدنا الجملة الشرطية else-if على اختبار أكثر من شرط في الوقت نفسه، وتكتب اختصارًا elif. لنلاحظ على سبيل المثال السكربت age.sh التالي الذي يأخذ معامل العمر AGE كوسيط ويعطينا نتيجة حسب الشروط المحددة في if و elif: #!/bin/bash AGE=$1 if [ $AGE -lt 13 ]; then echo "You are a kid." elif [ $AGE -lt 20 ]; then echo "You are a teenager." elif [ $AGE -lt 65 ]; then echo "You are an adult." else echo "You are an elder." fi يمكنك العودة لمقال تمرير الوسطاء إلى سكربت باش Bash لمطالعة مزيد من المعلومات عن تمرير الوسطاء لسكربتات باش. لننفذ السكربت الآن مع قيم مختلفة لمتغير العمر AGE ونلاحظ النتائج كما يلي: kabary@handbook:~$ ./age.sh 11 You are a kid. kabary@handbook:~$ ./age.sh 18 You are a teenager. kabary@handbook:~$ ./age.sh 44 You are an adult. kabary@handbook:~$ ./age.sh 70 You are an elder. استخدمنا الشرط lt وهو اختصار للعبارة الإنجليزية less than أي أصغر من لاختبار قيمة المتغير AGE$. وتجدر الإشارة لكوننا نستطيع استخدام الجملة الشرطية elif بقدر ما نريد في بنية else-if، أما else فلا تُكتَب فيها إلا مرة واحدة فقط، وأن كافة جمل if الشرطية ينبغي أن تُغلق باستخدام fi. استخدام جمل if الشرطية المتداخلة في باش يُقصد بالجمل الشرطية المتداخلة استخدام جمل if الشرطية داخل جمل if أخرى، لننظر على السكربت التالي weather.sh لتوضيح الأمر: #!/bin/bash TEMP=$1 if [ $TEMP -gt 5 ]; then if [ $TEMP -lt 15 ]; then echo "The weather is cold." elif [ $TEMP -lt 25 ]; then echo "The weather is nice." else echo "The weather is hot." fi else echo "It's freezing outside ..." fi يُظهِر هذا السكربت عبارات تصف الطقس بحسب درجة الحرارة المعطاة له ضمن الوسيط TEMP، فإذا كانت درجة الحرارة أكبر من 5 درجات فستُفَعَل عندها الجملة الشرطية الداخلية if-elif، دعنا نعطي السكربت بعض القيم المتفاوتة ونلاحظ النتائج: kabary@handbook:~$ ./weather.sh 0 It's freezing outside ... kabary@handbook:~$ ./weather.sh 8 The weather is cold. kabary@handbook:~$ ./weather.sh 16 The weather is nice. kabary@handbook:~$ ./weather.sh 30 The weather is hot. استخدام الجملة الشرطية case في باش الجملة الشرطية case هي بديل مناسب يغني في بعض الحالات التعقيد المصاحب لكثرة استخدام if ضمن السكربت، فالجملة case أسهل في القراءة والإعداد، وهذه صيغتها العامة: case "variable" in "pattern1" ) Command … ;; "pattern2" ) Command … ;; "pattern2" ) Command … ;; esac لننتبه للتالي عند كتابة case: توجد مسافة فارغة وقوس هلالي ) بعد كل حالة أو نمط من أنماط الجملة الشرطية case تنتهي جميع الأوامر بمسافة فارغة وفاصلة منقوطة مزدوجة ;; والمسافة الفارغة هنا إلزامية تنتهي جملة case دائمًا بالأمر esac وهو عبارة عن كلمة case معكوسة أكثر حالات استخدام case عند مطابقة المتغير مع أنماط أو خيارات محددة وواضحة كما في السكربت char.sh التالي: CHAR=$1 case $CHAR in [a-z]) echo "Small Alphabet." ;; [A-Z]) echo "Big Alphabet." ;; [0-9]) echo "Number." ;; *) echo "Special Character." esac يقبل هذا السكربت وسيطًا واحدًا هو CHAR، وتطابق الجملة case قيمته مع مجموعة أنماط، لتقرر فيما إذا كان المتغير حرفًا أبجديًا كبيرًا أو صغيرًا، أو عددًا أو محرفًا خاصًا. لنلاحظ نتائج تنفيذ السكربت مع قيم مختلفة للمتغير: kabary@handbook:~$ ./char.sh a Small Alphabet. kabary@handbook:~$ ./char.sh Z Big Alphabet. kabary@handbook:~$ ./char.sh 7 Number. kabary@handbook:~$ ./char.sh $ Special Character. يُمثل رمز النجمة * هنا الحالة الافتراضية للجملة case وهو يقابل else في جملة if الشرطية. شروط الاختبار في باش تعتمد شروط الاختبار المستخدمة في الجمل الشرطية السابقة في نهاية الأمر على معاملات منطقية، وتختلف طبيعتها حسب نوع البيانات المتعامل معها سواء كانت أعدادًا أو سلاسل نصية أو ملفات، ويبين الجدول التالي أشهر هذه الشروط: الشرط المكافئ a -lt $b$ قيمة a أصغر من قيمة b a -gt $b$ قيمة a أكبر من قيمة b a -le $b$ قيمة a أصغر أو تساوي قيمة b a -ge $b$ قيمة a أكبر أو تساوي قيمة b a -eq $b$ قيمة a تساوي قيمة b a -ne $b$ قيمة a لا تساوي قيمة b e $FILE- المتغير FILE$ موجود d $FILE- المتغير FILE$ موجود وهو مجلد f $FILE- المتغير FILE$ موجود وهو ملف عادي L $FILE- المتغير FILE$ موجود وهو رابط مرن soft link ‎$STRING1 = $STRING2‎ قيمة السلسلة النصية STRING1 تساوي قيمة السلسلة النصية STRING2 ‎$STRING1 != $STRING2 قيمة السلسلة النصية STRING1 لا تساوي قيمة السلسلة النصية STRING2 ‎-z $STRING1 قيمة السلسلة النصية STRING1 فارغة لا يتوجب حفظ هذه الشروط عن ظهر قلب فهي متوفرة في دليل مساعد يشرح طريقة كتابتها عندما نحتاجها، ويمكننا الوصول إليه بالتعليمة التالية: kabary@handbook:~$ man test لنطبق مثالًا عمليًّا على شروط باش، سننشئ السكربت filetype.sh الذي يبين طبيعة العنصر المعطى له إذا كان ملفًا أو مجلدًا أو رابطًا مرنًا soft link: #!/bin/bash if [ $# -ne 1 ]; then echo "Error: Invalid number of arguments" exit 1 fi file=$1 if [ -f $file ]; then echo "$file is a regular file." elif [ -L $file ]; then echo "$file is a soft link." elif [ -d $file ]; then echo "$file is a directory." else echo "$file does not exist" fi تفحص الجملة الشرطية الموجودة في بداية السكربت عدد الوسطاء الممررة له، وإذا تبين عدم وجود أي وسيط أو كان عدد الوسطاء أكبر من 1 فستُنهي هذه الجملة تنفيذ السكربت دون الانتقال للخطوة التالية، وتعطي المستخدم رسالة تحذيرية تطالبه بالتأكد من صحة الوسطاء المقدمة. لننفذ السكربت على عناصر مختلفة ونتفقد النتائج: kabary@handbook:~$ ./filetype.sh weather.sh weather.sh is a regular file. kabary@handbook:~$ ./filetype.sh /bin /bin is a soft link. kabary@handbook:~$ ./filetype.sh /var /var is a directory. kabary@handbook:~$ ./filetype.sh Error: Invalid number of arguments طريقة كتابة جملة if-else في سطر واحد يمكنك استخدام الجمل الشرطية if-else في ورؤية نتائجها في الصدفة shell مباشرةً دون الحاجة لكتابتها في سكربت خاص وتنفيذه عند اللزوم، وهذا مثال: if [ $(whoami) = 'root' ]; then echo "You are root" else echo "You are not root" fi بوسعنا كتابة هذا المثال في سطر واحد لتسهيل التعامل معه، ويجري ذلك بإضافة فاصلة منقوطة بين كل أمر وآخر كما يلي: if [ $(whoami) = 'root' ]; then echo "root"; else echo "not root"; fi نستطيع الآن تنفيذه في الصدفة مباشرةً، لننسخه ونلصقه في الطرفية terminal ونلاحظ النتيجة. الخلاصة حاولنا في هذه المقالة تكوين فكرةً عامة عن استخدام الجمل الشرطية في سكربتات باش من خلال أمثلة عملية متنوعة، ولفهم الموضوع بشكل أفضل ننصح بالتفكير بحالات اتخاذ قرار جديدة ومحاولة كتابة سكربتات مناسبة لها، فهذا من شأنه تعزيز فهم الموضوع والتعامل معه بكفاءة. ترجمة -وبتصرف- للمقال Decision Making With If Else and Case Statements. اقرأ أيضًا المقال السابق: عمليات السلاسل النصية في باش Bash البُنى الشرطية في Bash الاستخدامات المتقدمة لعبارة if الشرطية في Bash استخدام البنية case في باش أساسيات كتابة برامج Bash
  11. كثيرًا ما يتردد على مسامعنا مصطلح تعلم الآلة Machine Learning في الآونة الأخيرة، ويزداد الاهتمام به يومًا بعد يوم، فما هو تعلم الآلة بالضبط؟ وما هي أفضل أطر العمل البرمجية والأدوات التي تساعدنا على استثماره وتحقيق أقصى استفادة منه في مشاريعنا؟ هذا ما سنتعرف عليه في مقال اليوم. ما هو تعلم الآلة هناك تعريفات متنوعة لتعلم الآلة تصب جميعها في الفكرة الأساسية التالية: تعلم الآلة Machine Learning أو ML اختصارًا، هو ببساطة جعل الحاسوب يتعلم الأشياء من تلقاء نفسه، وهذا هو الرابط بين تعلم الآلة والذكاء الاصطناعي AI، فالذكاء الاصطناعي يعني جعل الآلة تفكر وتتعلم كما يفعل الإنسان. إذًا كيف ننجح في جعل الحاسوب يتعلم من تلقاء نفسه؟ أسهل طريقة لتحقيق ذلك هي باستخدام أطر عمل الذكاء الاصطناعي AI Frameworks فهي الطريقة المثلى للنجاح، وقبل أن نتعرف على هذه الأطر دعنا نستذكر في البداية معنى إطار العمل في البرمجة. إطار العمل framework في جوهره هو طريقة لتنفيذ العمل، فمن خلالها يمكن تنظيم العمل البرمجي وتهيئة البيئة المناسبة لتحسين عملية التطوير وتسريعها، ولعل الكفاءة والفعالية هي أبرز فوائد استخدام إطار العمل. سنعرض في هذا المقال 11 إطار عمل شهير من أفضل أطر عمل الذكاء الاصطناعي التي يمكننا استخدامها في مشاريع تعلم الآلة، وهي: تنسرفلو TensorFlow تورش Torch ثينو Theano كافيه Caffe كيراس Keras إطار عمل مايكروسوفت CNTK ساي كيت ليرن Scikit-learn أزور Azure ML Studio أكورد دوت نت Accord.NET سبارك Spark MLlib إطار عمل أمازون لتعلم الآلة Amazon Machine Learning دعونا نوضح كل إطار من هذه الأطر وأبرز مميزاته واستخداماته بمزيد من التفصيل 1. تنسرفلو TensorFlow يتمتع إطار العمل تنسرفلو TensorFlow بالعديد من المميزات التي تجعله خيارًا مناسبًا للمطورين الباحثين عن أداة فعالة لمشاريع الذكاء الاصطناعي، ولعل أبرز مميزاته أنه من تطوير شركة جوجل العالمية التي تمنحه دعمًا واسع النطاق وتوفر له تحديثات منتظمة ومستمرة لجعله يواكب آخر مستجدات التعلم الآلي. ولا يخفى على أحد الدعم رفيع المستوى الذي تقدمه شركة بعراقة جوجل لمنتجاتها، فضلًا عن مجتمع المطورين الضخم الذي يستخدم تنسرفلو TensorFlow حول العالم والذي من شأنه توفير عون كبير من خلال مساهماته وإجاباته عن كل التساؤلات، وإضافة لميزات الدعم القوي، يتميز إطار تنسرفلو بالمرونة فهو نظام مقسم لأجزاء أو وحدات modular system، بمعنى يمكننا استخدام كل جزء من هذه الأجزاء بمفرده أو استخدام الأجزاء معًا حسب متطلبات مشروعنا، كما أنه يتمتع بقابلية النقل Portability وهذه ميزة مهمة يفضلها معظم مستخدميه، حتى أن بإمكاننا استعماله على الهاتف الجوال في حال لم يتوفر لنا الوصول إلى حاسوب مكتبي أو محمول لتثبيته. ولمطالعة المزيد من المعلومات حول هذا الإطار واستخدامه عمليًا، ننصح بقراءة مقال بناء شبكة عصبية للتعرف على الأرقام المكتوبة بخط اليد باستخدام مكتبة TensorFlow. 2. تورش Torch أطلق إطار عمل تورش Torch لأول مرة في العام 2002، وهو أقرب إلى المكتبة منه إلى إطار العمل التقليدي، حيث يتكون من مجموعة خوارزميات تستخدم في مجال تعلُّم الآلة، ومن أبرز المميزات التي يوفرها Torch لمستخدميه نذكر: المصفوفات متعددة الأبعاد N-dimensional arrays واجهة خاصة للغة البرمجة C عمليات الجبر الخطي الروتينية دعم سريع وفعال لوحدة معالجة الرسومياتGPU السرعة والمرونة في تطوير المشاريع إلى جانب مجتمع المطورين الكبير الذي يستخدمه والذي من شأنه توفير الدعم المطلوب عند الحاجة، ويُحسَب لمجتمع Torch نشاطه الواسع على GitHub والسعي الدائم لتطوير هذه المكتبة. ملاحظة: تورش هي مكتبة تعلم آلي مفتوحة المصدر تُستخدم لإنشاء الشبكات العصبية العميقة وهي مكتوبة بلغة البرمجة Lua، أما بايتورش PyTorch فهو إطار عمل مفتوح المصدر لتعلم الآلة يعتمد على لغة البرمجة بايثون ومكتبة تورش Torch وننصح بقراءة مقال تعرف على إطار عمل باي تورش PyTorch وأهميته لتطبيقات الذكاء الاصطناعي للتعرف أكثر على هذا الإطار. 3. ثينو Theano تحظى الأداة ثينو Theano بشعبية كبيرة بين أُطر عمل الذكاء الاصطناعي، رغم أنه ليس حديثًا، لكنه يستند إلى لغة بايثون واسعة الانتشار وسهلة التعلُّم والمناسبة تمامًا لمشاريع تعلم الآلة والذكاء الاصطناعي وهذا يعطي ثينو قيمة مضافة. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن يمكن أن نقول أن أداة Theano بمثابة معيار في مجال تعلُّم الآلة والذكاء الاصطناعي فهي قديمة نسبيًا وقد اعتمدت عليها الكثير من الأدوات الأخرى سواء على صعيد البنية أو الوظيفة، وأكثر ما يميزها الاختبار الشامل الذي تنفذه على الشيفرات البرمجية قبل إطلاقها وهي في الواقع رائدة في هذا المجال. كما أنها تسهل التعامل مع التعبيرات الرياضية، وتدعم العمل مع التمايز الرمزي symbolic differentiation ووحدة معالجة الرسوميات GPU التي تسرّع إنجاز العمليات الحسابية بشكل كبير. 4. كافيه Caffe صدر إطار العمل كافيه Caffe في العام 2017 وهو أحد أطر العمل الحديثة، ومكتوب بلغة ++C وهذا يجعله خيارًا مناسبًا للكثير من المبرمجين ومهندسي البرمجيات الذين غالبًا ما يجيدون هذه اللغة والتي ما زالت تستخدم على نطاق واسع. ويُعَدّ Caffe أفضل أطر العمل المستخدمة في بناء الشبكات الالتفافية Convolutional networks وهي نوعٌ خاص من الشبكات العصبية لذا من الجيد اعتماده من البداية إذا كنا ننوي بناء هذا النوع من الشبكات في مشروعنا. ومن مميزاته أيضًا العمل بسلاسة مع وحدة مالجة الرسوميات GPU المناسبة، فقد لا يستغرق Caffe أكثر من يوم واحد لمعالجة عشر ملايين صورة، فإذا كانت السرعة من أولوياتنا فسيكون هذا الإطار خيارًا مثاليًا لنا. 5. كيراس Keras إطار العمل كيراس Keras بسيط وسهل التعلُّم، كما يتمتع Keras بصغر حجمه lightweight وهذا يجعله سريع الأداء لقلة الموارد الحاسوبية التي يحتاجها لإنجاز مهمة معينة مقارنة بغيره من أطر العمل، ويوفر كيراس بالإضافة إلى ما سبق واجهة للعمل مع بايثون، وقد تحدثنا عن أهمية هذه اللغة لمستخدمي الذكاء الاصطناعي، ويُعدّ خيارًا ممتازًا في بناء الشبكات العصبية التكرارية recurrent networks، والشبكات العصبية الالتفافية convolutional networks، على عكس Caffe الذي يتخصص فقط بالشبكات العصبية الالتفافية. وهذه قائمة بالمهام التي يتعامل معها كيراس بطريقة جيدة لتعرف في أي نوع من المشاريع يمكنك استخدامه: التصنيف Classification توليد النص Generating text تلخيص النص Summarizing a text الترجمة Translations التعرف على الكلام Speech recognition ويمكن اعتماده للعديد من المهام المعقدة الأخرى 6. Microsoft CNTK يُعَدّ CNTK من مايكروسوفت منافسًا قويًّا لأُطر عمل الذكاء الاصطناعي شائعة الاستخدام مثل TensorFlow وغيره، فهو يتعامل مع مختلف عمليات التعلُّم الآلي مثل: بناء الشبكات العصبية الالتفافية والتكرارية والشبكات ذات الذّاكرة الطويلة قصيرة المدى LSTMs. وقد جَهِدَت مايكروسوفت منذ إطلاقه لجعل CNTK مرنًا، وفعَّالًا، وعالي الأداء، وقد نجحت مايكروسوفت في مسعاها، فقد حصل CNTK على نتائج جيدة في معظم اختبارات الأداء المعيارية benchmark tests التي خضع لها، فهو يقدم أداءً جيدًا في كل عملية ينفذها ضمن وقت محدد، كما أنه يتعامل بمرونة وفعالية مع جميع أنواع المهام التي تُعطى له، مثل: التعرّف على الكلام، والتعرّف على الصور ومعالجتها، وتوليد النصوص، وتدريب أنظمة الإنتاج. كما أنه يعمل بكفاءة مع نظامي ويندوز و لينكس على حد سواء. 7. ساي كيت ليرن Scikit-learn أكثر ما يميز Scikit-learn أنه منصة مفتوحة المصدر تتمتع بمجتمع نشط للغاية يساندنا في أي مشكلة قد تواجهنا، فالإجابات متوفرة بكثرة في منتديات المنصة، وإن لم نجد نبحث عنه بين المواضيع المنشورة كل ما علينا هو طرح سؤال جديد وترقب الإجابات فالمجتمع متفاعل وسيجيبنا بسرعة، ويمكننا دائمًا الاستعانة بالتوثيقات التي توفرها Scikit-learn. وفضلًا عن الدعم المميز لهذا الإطار فهو سريع الأداء، ويؤمن لمستخدميه واجهة برمجة تطبيقات API واضحة ومنظمة وجاهزة للعمل مع مختلف الاحتياجات. ولمطالعة المزيد حول استخدام ساي كيت ليرن عمليًا ننصح بمقال بناء مصنف بالاعتماد على طرق تعلم الآلة بلغة البايثون باستخدام مكتبة Scikit-Learn. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن 8. Azure ML Studio تختلف Azure ML Studio عن غيرها من أطر عمل الذكاء الاصطناعي بكونها تمتلك نسختين، نسخة مدفوعة وأخرى مجانية، بالإضافة لكونها أداةً تفاعلية وسحابية، وهذا ما سنشرحه تاليًّا. تُنشئ Azure ML Studio التحليلات التنبؤية لنماذج تعلُّم الآلة بطريقة تفاعلية، فتتنبأ بالنتائج وفقًا للبيانات التي تقدمها لها، كما تتيح لنا بناء نماذجنا الخاصة بسهولة بواسطة السحب والإفلات ثم التنبؤ بالنتيجة استنادًا إلى المدخلات، وزيادة في دقة النتائج توفر Azure مكتبة مضمّنة built-in library تحتوي مجموعة واسعة من خوارزميات الذكاء الاصطناعي الكفيلة بتحسين تنبؤاتنا. وأكثر ما يميزها أنها سحابية، فلن نحتاج لتثبيت أي تطبيق على جهازنا، إذ كل ما يتطلبه الأمر حاسوب شخصي أو محمول متصل بالإنترنت. 9. أكورد دوت نت Accord.NET يُستخدم إطار العمل Accord.NET في معالجة الصور وتعلم الآلة، وهو مكتوب بلغة #C المستخدمة بكثرة في بناء البرامج والتطبيقات، ويتضمن مكتبات متنوعة للحوسبة العلمية بما فيها مكتبات تعلُّم الآلة، وهذه أبرز المميزات التي يوفرها: دعم آلات المتجهات Support for vector machines خوارزمية التجميع باستخدام نموذج غاوس المختلط Gaussian Mixture Models خوارزمية k-Means أشجار القرار Decision Trees النماذج البايزية البسيطة Naive Bayesian models لمعرفة المزيد عن هذه الخوارزميات وغيرها ننصح بقراءة مقال أدوات برمجة نماذج تعلم الآلة. ملاحظة: ذكرنا أن Accord.NET مميز في التعامل مع الصور، وبالتالي فهو مناسب للعمل مع كائنات اكتشاف الوجوه ومعالجتها إذ يحوّلها إلى تدفقات من الصور، وبالمثل أيضًا للمهام المتعلقة بالصوتيات audio فهو قادر على معالجة الإشارات الصوتية وتحويلها وفلترتها لتناسب برامج التعلم الآلي والتطبيقات الإحصائية. 10. سبارك Spark MLlib يعمل إطار العمل Spark MLlib كمكتبة لتعلّم الآلة على غرار معظم الأدوات التي عرضناها سابقًا، وهو يتضمن عدة خوارزميات ملائمة للمهام التالية: التصنيف classification لتحديد الصنف الذي ينتمي إليه عنصر ما بناءً على خصائصه التجميع clustering لتقسيم مجموعة من البيانات إلى مجموعات أو عناقيد بحيث تكون العناصر في كل مجموعة متشابهة فيما بينها وتختلف عن العناصر في المجموعات الأخرى الانحدار regression لفهم وتوقع العلاقات بين المتغيرات التصفية التعاونية collaborative filtering لتوقع تفضيلات المستخدم بناءً على معلومات جُمعت من عدة مستخدمين سابقين وغيرها. ومن أبرز مميزات Spark MLlib بساطته وتوافقه مع الأدوات والأطر الأخرى، إذ يتيح لنا هذا التوافق الاهتمام بجوانب معينة من مشروعنا وتنفيذها بالتطبيق المناسب لها طالما أن يتوافق مع إطار العمل، وهو ما يضمن تطويرًا أسرع وأكثر كفاءة. ويتمتع Spark MLlib بمحرك فعّال يتفوق على لغتي البرمجة بايثون و R اللتين لطالما استخدمهما علماء البيانات في التعامل مع مهام التعلم الآلي، إذ يعالج Spark MLlib المشكلات التي لا تستطيع بايثون و R معالجتها بطريقة تفاعلية وعلى نطاق أوسع. ملاحظة: يمكننا اختيار لغة البرمجة التي نريدها للعمل مع Spark MLlib من بين اللغات التالية: بايثون، و R، و Scala، وجافا. 11. Amazon Machine Learning يمكننا إضافة ميزات الذكاء الاصطناعي إلى مختلف أنواع التطبيقات بالاستعانة بأداة تعلّم الآلة من منصة أمازون AWS، ومن هذه الميزات تحليل الكلام، وروبوتات الدردشة chatbot، والرؤية الحاسوبية computer vision. ما يميز AWS إمكانية استخدامها مع أطر عمل الذكاء الاصطناعي الأخرى مثل TensorFlow و Caffe، وهو ما يمنحنا بيئة متقنة وفعّالة لتطبيقات تعلّم الآلة وإمكانية الاستفادة فيها من وظائف TensorFlow و Caffe جنبًا إلى جنب مع وظائف AWS. توفر AWS أداة مُضَمّنة تدعى Amazon Sagemaker تساعدنا على بناء نماذجنا الخاصة للتعلُّم الآلي ونشرها بسهولة وكفاءة، وتفيدنا بشكل كبير إذا كنا نخطط لاستخدام Amazon Machine Learning كإطار عمل في مشروعنا. الخلاصة بهذا نكون قد وصلنا لنهاية مقالنا الذي اكتشفنا فيه أهم أُطر عمل الذكاء الاصطناعي المتخصصة في تعلُّم الآلة، حيث قد عرضنا بعجالة سريعة أحد عشر إطار عمل من أفضل الأُطر المتوفرة حاليًا، ولكل منها مزاياه الخاصة والجوانب التي يتفوق فيها على غيره سواء من ناحية الأداء أو التحديثات أو التوافق مع لغات البرمجة أو التخصص في نوع معين من المعالجة مثل معالجة الصور أو غيرها. يمكننا الآن الانطلاق من هنا واختيار المنتج الذي يناسبنا والبحث عنه أكثر ثم البدء بتعلمه، ترجمة -وبتصرف- لمقال 11 Best AI Framework for Machine Learning لصاحبه Mark Bynum. اقرأ أيضًا تعرف على أهم كتب الذكاء الاصطناعي المجانية برمجة الذكاء الاصطناعي: بناء مستقبل الآلات الذكية أساسيات الذكاء الاصطناعي: دليل المبتدئين مكتبات وأطر عمل الذكاء الاصطناعي: القوة الكامنة خلف الأنظمة الذكية
  12. يدرك كل من تعامل مع متغيرات باش عدم وجود أي فرق بين تعريف متغير نوعه عدد صحيح int ومتغير نوعه سلسلة نصية string، فمهما كان نوع البيانات المتغير في باش هو متغير فقط. وقد تعرفنا في مقالنا السابق على معاملات العمليات الحسابية للتعامل مع الأعداد، وسنتعرف في هذا المقال على وظائف باش الخاصة بالتعامل مع السلاسل النصية، مثل حساب طول سلسلة نصية، وربط السلاسل النصية ببعضها، واستخراج السلاسل النصية الفرعية منها، واستبدال السلاسل النصية الفرعية وغير ذلك. معرفة طول السلسلة النصية السلسلة النصية string هي مجموعة أو مصفوفة من المحارف المتسلسلة فيما بينها. لنُعرّف سلسلة نصية جديدة أو متغير جديد في باش باسم distro مثلًا ونعطيه القيمة "Ubuntu" نكتب التالي: distro="Ubuntu" يمكن الحصول على طول هذه السلسلة أي عدد محارفها بكتابة الرمز # قبل اسمها ضمن تعليمة echo كما يلي: kabary@handbook:~/scripts$ echo ${#distro} 6 تنحصر وظيفة الأمر echo بإظهار النتيجة، أما الصيغة {string#} فهي التي احتسبت طول السلسلة الفعلي. دمج السلاسل النصية نعني بدمج السلاسل concatenation إلحاق سلسلة نصية أولى بنهاية سلسلة نصية أخرى للحصول على سلسلة أطول. لننشئ على سبيل المثال السلسلتين النصيتين "str1" و "str2" كما يلي: str1="hand" str2="book" لندمج الآن السلسلتين معًا ونخزن النتيجة بسلسلة ثالثة: str3=$str1$str2 توضح الصورة التالية النتيجة: إيجاد السلاسل النصية الفرعية تساعدنا هذه العملية على فهرسة index أو إيجاد موضع محرف معين أو تسلسل محارف معين ضمن سلسلة نصية، لتوضيح الفكرة لننشئ السلسلة النصية "str" كالتالي: str="Bash is Cool" يمكننا الآن معرفة موضع السلسلة الفرعية "Cool" ضمن الجملة السابقة باستخدام الأمر expr، وفق التالي: kabary@handbook:~/scripts$ word="Cool" kabary@handbook:~/scripts$ expr index "$str" "$word" 9 بعد تنفيذ الأمر حصلنا على العدد 9 في النتيجة، وهو يشير إلى ترتيب أول محرف من محارف السلسلة الفرعية "Cool" ضمن السلسلة الأصلية "Bash is Cool"، أي أنه مثل فهرس index لموضع السلسلة الفرعية. ملاحظة: لم نستخدم الجملة الشرطية if في الأمثلة التي أوردناها في هذا المقال لأننا سنخصص لها مقالًا لاحقًا. استخراج السلاسل النصية الفرعية يعني استخراج سلسلة فرعية من سلسلة أخرى استخراج حرف أو كلمة أو مجموعة كلمات من سلسلة نصية معينة. سنكتب الأمر التالي لإنشاء سلسلة نصية جديدة اسمها "foss" كما يلي: foss="Fedora is a free operating system" لنفترض الآن أننا نرغب باستخراج السلسلة الفرعية "fedora" من السلسلة السابقة، فكيف سننجز ذلك؟ ما نحتاجه هو فهرس السلسلة الفرعية أو موضع أول محرف فيها، بالإضافة إلى طولها الذي يُمثّل عدد المحارف التي نريد استخراجها. إذًا كي نستخرج السلسلة "fedora" سنبدأ من المحرف الذي ترتيبه 0، وسنستخرج بعده 6 محارف، وفق التالي: kabary@handbook:~/scripts$ echo ${foss:0:6} Fedora نلاحظ أن ترتيب المحارف في سلاسل باش النصية يبدأ من الصفر وليس من الواحد، وهذا ينسجم مع ترتيب أول عنصر في مصفوفات باش التي تبدأ دائمًا بالصفر. يمكننا أيضًا تحديد موضع البداية فقط وعدم ذكر عدد المحارف المطلوب استخراجها وعندها سيستخرج باش جميع المحارف من موضع البداية وحتى نهاية السلسلة. على سبيل المثال إذا أردنا استخراج السلسلة الفرعية "free operating system" من السلسلة "foss" السابقة، فسنحدد فقط موضع البداية وهو في حالتنا 12 كما يلي: kabary@handbook:~/scripts$ echo ${foss:12} free operating system استبدال السلاسل النصية الفرعية تتيح لنا هذه العملية استبدال سلسلة نصية فرعية بسلسلة أخرى، فيمكننا على سبيل المثال استبدال السلسلة النصية الفرعية "fedora" بالسلسلة النصية الفرعية "Ubuntu" ضمن السلسلة "foss" السابقة، وذلك وفق التالي: kabary@handbook:~/scripts$ echo ${foss/Fedora/Ubuntu} Ubuntu is a free operating system لنجرب استبدالًا آخر وهو تبديل السلسلة "free" بالسلسلة "popular" مثلًا: kabary@handbook:~/scripts$ echo ${foss/free/popular} Fedora is a popular operating system تجدر الإشارة هنا لأننا أجرينا في كلا المثالين السابقين استبدالًا ظاهريًا للسلاسل النصية الفرعية أي عند عرض السلاسل فقط من خلال الأمر echo، حيث أظهرنا السلسلة مرة باستبدال "fedora" ومرة "free" لكننا لم نبدل القيمة الفعلية للسلسلة "foss" حيث بقيت القيمة المخزنة فيها كما هي. حذف السلاسل النصية الفرعية يمكننا حذف أي سلسلة نصية فرعية من أي سلسلة نريدها باستخدام /، لننشئ مثلًا السلسلة النصية "fact" التالية: fact="Sun is a big star" لنحذف منها السلسلة الفرعية "big": kabary@handbook:~/scripts$ echo ${fact/big} Sun is a star لننشئ سلسلة أخرى مختلفة قليلًا اسمها "cell": cell="112-358-1321" لنفترض أننا نريد حذف الشرطات "-" منها، فإذا كتبنا الأمر التالي فإنه سيحذف الشرطة الأولى فقط كما يلي: kabary@handbook:~/scripts$ echo ${cell/-} 112358-1321 أما لحذف جميع الشرطات الموجودة في السلسلة فينبغي كتابة الرمز / مرتين، وفق التالي: kabary@handbook:~/scripts$ echo ${cell//-} 1123581321 نلاحظ أننا نحذف السلاسل النصية الفرعية في خرج الأمر echo فقط ولم نحذفها من القيمة الأصلية للسلسلة النصية "cell" التي بقيت كما هي ولم تتأثر بالحذف. أما إذا رغبنا بتعديل القيمة الأصلية للسلسلة النصية، وحذف الشرطات منها فعليًّا، فهذه هي الطريقة: kabary@handbook:~/scripts$ echo $cell 112-358-1321 kabary@handbook:~/scripts$ cell=${cell//-} kabary@handbook:~/scripts$ echo $cell 1123581321 تغيير حالة الحروف بين كبيرة وصغيرة في السلاسل النصية يتيح لنا باش تغيير حالة الحروف الأبجدية في السلاسل النصية من كبيرة إلى صغيرة أو بالعكس بسهولة. لننشئ في البداية سلسلتين نصيتين كما يلي: legend="john nash" actor="JULIA ROBERTS" لنحوّل حالة الحروف في السلسلة النصية الأولى "legend" من حروف صغيرة إلى حروف كبيرة، وفق التالي: kabary@handbook:~/scripts$ echo ${legend^^} JOHN NASH ولنحول أيضًا حروف السلسلة النصية "actor" إلى حروف صغيرة: kabary@handbook:~/scripts$ echo ${actor,,} julia roberts نستطيع أيضًا تحويل حالة الحرف الأول فقط في "legend" إلى حرف كبير: kabary@handbook:~/scripts$ echo ${legend^} John nash والحرف الأول فقط في "actor" إلى حرف صغير: kabary@handbook:~/scripts$ echo ${actor,} jULIA ROBERTS يمكننا أيضًا تحويل حالة حروف بعينها فقط من حروف كبيرة إلى صغيرة أو العكس؛ فعلى سبيل المثال يمكن تغيير الحرفين j و n من السلسلة النصية "legend" إلى حروف كبيرة كما يلي: kabary@handbook:~/scripts$ echo ${legend^^[jn]} JohN Nash الخلاصة بهذا نكون قد وصلنا إلى ختام المقال الذي شرحنا فيه كيفية التعامل مع السلاسل النصية في سكربتات باش، وسنتطرق في المقال القادم لشرح مهارات جديدة ونوضح طريقة اتخاذ القرار ضمن سكربتات باش من خلال الجمل الشرطية بمختلف أشكالها. ترجمة -وبتصرف- للمقال String Operations in Bash. اقرأ أيضًا المقال السابق: استخدام المعاملات الحسابية في سكربتات باش Bash أنواع المتغيرات في Bash ميزات صدفة Bash تنفيذ الأوامر في Bash
  13. تدخل العمليات الحسابية في العديد من سكربتات باش Bash Scripts، فقد تحتاج لحساب المساحة المتبقية من القرص الصلب مثلًا، أو حجوم الملفات أو عرض النطاق الترددي للشبكة، أو تواريخ انتهاء صلاحيات كلمات المرور، أو أعداد المضيفين hosts أو غير ذلك. وسنتعلم في مقالنا الخامس من سلسلة باش للمبتدئين طريقة استخدام معاملات باش bash operators لإجراء العمليات الحسابية داخل السكربت، وسنبدأ بجدول يتضمن المعاملات الحسابية: المعامل الوصف + الجمع - الطرح * الضرب / قسمة الأعداد الصحيحة بدون بواقي عشرية % قسمة المعاملات التي تُرجع باقي عملية القسمة فقط ** الأُس مثلًا x أُس y تنفيذ الجمع والطرح في باش لننشئ سكربت باش يدعى مثلًا addition.sh يجمع حجمي ملفين بالبايت Byte ويعطيك النتيجة، سنستعمل في السكربت وسطاء باش وقد شرحنا طريقة طريقة التعامل معها في مقال تمرير الوسطاء إلى سكربت باش، بالإضافة إلى الأمرين cut و du. يُستَخدَم الأمر du لمعرفة أحجام الملفات، ويمكن استخدام الراية أو الخيار d- بعد الأمر وهو اختصار لكلمة bytes لجعل المخرجات بالحجم الفعلي للملف بالبايتات فبدون هذا الخيار سيعرض du الحجم بالوحدة الافتراضية كيلوبايت. يُظهر هذا الأمر في النتيجة كلًا من اسم الملف وحجمه أي أنه يعطي عمودين أو مُخرَجَين، لذا سنحتاج الأمر cut لاقتطاع جزء النتيجة الذي نريده فقط وهو الحجم. وبالتالي سنستخدم أنبوب إعادة التوجيه | لتمرير خرج الأمر du إلى دخل الأمر cut. سيكون نص السكربت addition.sh كما يلي: #!/bin/bash fs1=$(du -b $1 | cut -f1) fs2=$(du -b $2 | cut -f1) echo "File size of $1 is: $fs1" echo "File size of $2 is: $fs2" total=$(($fs1 + $fs2)) echo "Total size is: $total" يحتاج السكربت السابق وسيطين، وهنا مرر له الملفين etc/passwd/ و etc/group/، ثم نفذنا السكربت وسنحصل على النتيجة التالية: kabary@handbook:~/scripts$ ./addition.sh /etc/passwd /etc/group File size of /etc/passwd is: 2795 File size of /etc/group is: 1065 Total size is: 3860 لاحظ السطر التالي الذي يتضمن عملية الجمع باستخدام المعامل +: total=$(($fs1 + $fs2)) ينبغي أن تكتب العمليات الحسابية دائمًا بين قوسين هلاليين مزدوجين (()) بالصيغة التالية: $((arithmetic-expression)) يمكنك استخدام معامل الطرح - بالطريقة نفسها، فمثلًا المتغير sub التالي سيحمل القيمة سبعة: sub=$((10-3)) تنفيذ عمليتي الضرب والقسمة في باش لننشئ سكربت بسيط اسمه giga2mega.sh مثلًا يجري عملية التحويل من جيجا بايت GB إلى ميجا بايت MB وفق التالي: #!/bin/bash GIGA=$1 MEGA=$(($GIGA * 1024)) echo "$GIGA GB is equal to $MEGA MB" لنُشغل السكربت الآن لنعرف كم تعادل 4 جيجا بايت بالميجا بايت: kabary@handbook:~/scripts$ ./giga2mega.sh 4 4 GB is equal to 4096 MB لقد استخدمنا معامل الضرب * لضرب عدد الجيجا بايت بالعدد 1024 للحصول على مكافئها بالميجا بايت: MEGA=$(($GIGA * 1024)) يمكنك تعديل السكربت نفسه ليحول الحجم من جيجا بايت إلى كيلو بايت: KILO=$(($GIGA * 1024 * 1024)) وبالطريقة نفسها حَوِّله إلى بايت. لنجرب الآن عملية القسمة باستخدام المعامل / كما يلي ونُخَزِّن النتيجة في المتغير div الذي سيحمل القيمة خمسة في مثالنا: div=$((20 / 4)) القسمة المستخدمة هنا هي قسمة الأعداد الصحيحة، وبالتالي ستكون النتيجة عددًا صحيحًا حتمًا، فلو قَسَّمتَ 5 على 2 باستخدام المعامل / فستحصل على 2 لأن هذه القسمة تهمل البواقي فهي لا تُرجع أعدادًا عشرية. kabary@handbook:~/scripts$ div=$((5 / 2)) kabary@handbook:~/scripts$ echo $div 2 إذا رغبت بالتعامل مع الأعداد العشرية فستحتاج للأمر bc، وهذا مثال على طريقة استخدامه مع القسمة الصحيحة: echo "5/2" | bc -l 2.50000000000000000000 وتبين الصورة أدناه استخدامه مع العمليات الحسابية الأخرى: استخدام الأس وباقي القسمة لننشئ سكربت اسمه power.sh يقبل وسيطين عدديين ليكونا مثلًا a و b، ويُظهر نتيجة b مرفوعًا للأُس b كما يلي: #!/bin/bash a=$1 b=$2 result=$((a**b)) echo "$1^$2=$result" إذًا فقد استخدمنا المعامل ** لحساب نتيجة a مرفوعًا للأس b، لنجرب السكربت على أعداد مختلفة كما يلي: kabary@handbook:~/scripts$ ./power.sh 2 3 2^3=8 kabary@handbook:~/scripts$ ./power.sh 3 2 3^2=9 kabary@handbook:~/scripts$ ./power.sh 5 2 5^2=25 kabary@handbook:~/scripts$ ./power.sh 4 2 4^2=16 سنتعلم الآن استخدام المعامل % أي باقي القسمة modulo، يرجع هذا المعامل باقي القسمة فقط ويكون عددًا صحيحًا، فالمتغير rem في المثال التالي سيحمل القيمة 2: rem=$((17%5)) الباقي هنا هو 2 لأننا نحصل على 17 بمضاعفة العدد 5 ثلاث مرات وإضافة 2 للنتيجة. إنشاء محول مقاييس لدرجات الحرارة باستخدام باش لنطبق مثالًا شاملًا يستخدم كل العمليات الحسابية التي تعلمناها في الفقرات السابقة، سنكتب سكربت جديد باسم c2f.sh يحول درجة الحرارة من درجة مئوية إلى فهرنهايت وفق المعادلة التالية: F = C x (9/5) + 32 الطريقة المستخدمة هنا هي إحدى الطرق المتبعة لكتابة هذا السكربت، مع العلم أنه توجد طرق أخرى تعطيك النتيجة نفسها، سنعرف بداية المتغيرات فالمتغيرCيمثل درجة الحرارة المئوية، والمتغيرFيمثل درجة الحرارة بالفهرنهايت. #!/bin/bash C=$1 F=$(echo "scale=2; $C * (9/5) + 32" | bc -l) echo "$C degrees Celsius is equal to $F degrees Fahrenheit." استخدمنا الأمر bc هنا لأننا نتعامل مع أعداد عشرية، والأمر scale=2 لإظهار خانتين فقط بعد الفاصلة العشرية. لنجري بعض الآن التحويلات باستخدام السكربت التالي الذي يقرأ درجة الحرارة المدخلة بالدرجة المئوية، وتحويلها إلى الفهرنهايت، ثم عرض النتيجة لكل قيمة مدخلة: kabary@handbook:~/scripts$ ./c2f.sh 2 2 degrees Celsius is equal to 35.60 degrees Fahrenheit. kabary@handbook:~/scripts$ ./c2f.sh -3 -3 degrees Celsius is equal to 26.60 degrees Fahrenheit. kabary@handbook:~/scripts$ ./c2f.sh 27 27 degrees Celsius is equal to 80.60 degrees Fahrenheit. الخلاصة إلى هنا نكون قد انتهينا من مقالنا الذي شرحنا فيه العمليات الحسابية الأساسية في سكربتات باش مع التطبيق العملي، نرجو أن يكون المقال قد قدم الفائدة المرجوة، وندعوك لمطالعة مقالنا التالي حول التعامل مع السلاسل النصية Strings في باش. ترجمة -وبتصرف- للمقال Using Arithmetic Operators in Bash Scripting. اقرأ أيضًا المقال السابق: استخدام المصفوفات في باش Bash قراءة وضبط متغيرات الصدفة Shell والبيئة في لينكس التوسعات في باش ميزات صدفة باش
  14. يسعى الكثير من مطوري الويب إلى تعلم إطار عمل جديد في مرحلة ما من مسارهم المهني، فأطر العمل تساعد على إنشاء تطبيقات أفضل وبسرعة أكبر كما أنها توسع خبرات المطورين وتعزز فرصهم في سوق العمل. وتوفر لغة جافا سكريبت الكثير من أطر العمل القوية والمستخدمة في تطوير مواقع وتطبيقات الويب والتي توفر وظائف مفيدة متنوعة، وكما هو معروف فإن لغة جافا سكريبت من أشهر لغات البرمجة المستخدمة في مجال تطوير الويب وهذا يشجع المطورين أكثر على تعلمها واستخدام أطر عملها. فإذا كنت مهتمًا بتعلم إطار عمل جديد لتطوير تطبيقات الويب الخاصة بك، تابع هذا المقال حيث سنبدأ فيه بشرح سريع لمفهوم إطار العمل في البرمجة، يليه عرض موجز لإثني عشر إطارًا من أفضل أطر العمل المستندة على جافا سكريبت لتختار من بينها ما يناسبك. ما هو إطار العمل ولماذا نحتاجه؟ يشير إطار العمل framework عمومًا إلى طريقة تنفيذ العمل، ويُقصد به في مجال البرمجة بيئة العمل working environment التي تمكننا من تنظيم عملنا، فيساعدنا إطار العمل البرمجي على تنظيم شيفراتنا وبالتالي يُسَهِّل ويُسَرِّع إنجازنا للمشاريع. تقسم أُطر العمل البرمجية إلى نوعين رئيسيين: أطر العمل التي تعمل كمكتبة library وهي تتميز بالمرونة، إذ تستطيع أخذ الأجزاء التي تحتاجها منها والتعديل عليها بما يناسب متطلبات عملك أطر العمل المتكاملة التي تفرض عليك طريقة وإجراءات محددة لكيفية إنشاء مشروعك وإنهائه سنعرض في الفقرات التالية أمثلة عديدة على كلا النوعين ليتضح الفرق بينهما أكثر، لكن دعنا في البداية نجيبك عن السؤال الأهم والذي لا بُدّ أنه تبادر في ذهنك وأذهاننا جميعًا: هل إطار العمل ضروري؟ وهل أحتاجه حقًا كمطور تطبيقات؟ تختلف الإجابة من حالة لأخرى وتتعلق بطبيعة المشروع، لكن الفائدة الكبيرة التي يقدمها إطار العمل على صعيد تنظيم بنية الشيفرة البرمجية وسهولة اختبارها تمنح المطورين سببًا وجيهًا لاستخدام أُطر العمل في مشاريعهم. فعندما تطور تطبيقاتك باستخدام إطار عمل ستكون شيفراتها المصدرية أكثر تنظيمًا وسيكون بنائها أسهل وأسرع مقارنة ببنائها من دون إطار عمل، وهذا يقودنا بالضرورة إلى الجزء الثاني من فائدة إطار العمل وهو سهولة الاختبار فالشيفرة المنظمة يسهل اختبارها وتصحيح أخطائها وهذه ميزة مهمة لا غنى عنها في تطبيقات الويب فأي تطبيق ويب أو موقع ويب ينبغي أن يُختَبَر بدقة قبل إطلاقه للعلن. خلاصة القول يمكنك بالتأكيد بناء تطبيقات ويب ناجحة بلغة جافا سكريبت JavaScript وحدها فقط من دون أي إطار عمل، لكن استخدام أحد أُطر العمل التي سنعرضها هنا سيسهل عملك ويوفر عليك الكثير من الوقت والجهد حتى في فترة الصيانة التي تلي إطلاق المنتج، فضلًا عن كونه مطلوبًا بكثرة في سوق العمل وتعلمه يعزز فرصتك في الحصول على وظيفة مناسبة. وإذا كنت تتقن لغة جافا سكريبت فسيسهل عليك تعلم أي إطار عمل من أُطرها فغالبًا ما يعتمد الإطار على اللغة نفسها، ولن تحتاج لتعلم أكثر من إطار أو إطارين فقط من بين الإثني عشر إطارًا التي سنذكرها تاليًا، وتذكر أن الشركات تفضل تعيين مطوري الويب المتقنين لبعض أطر العمل المعروفة إلى جانب تعلم لغة البرمجة نفسها. والآن إليك قائمتنا من أطر عمل جافا سكريبت التي نُرشحها لك والتي سنعرضها بمزيد من التفصيل: ريآكت React. فيو Vue JS. أنجولار Angular JS. بوليمر Polymer. إمبر Ember. باك بون Backbone. نيتف سكريبت NativeScript. نود جي اس Node JS. ميتيور Meteor. ميثريل Mithril. أوريليا Aurelia. سوكيت Socket. 1. ريآكت React ريآكت React هو مشروع مفتوح المصدر طوره مهندس البرمجيات جوردان ووك من شركة فيسبوك، وهو مكتبة أكثر من كونه إطار عمل متكامل تقليدي لذا يُعَدّ مناسبًا لمشاريع الويب التي تتطلب تخصيصًا عاليًّا. يستعمل ريآكت في بناء واجهة المستخدم UI لتطبيقات الهاتف الجوّال وتطبيقات الويب، فإذا كنت تعمل في هذا المجال سيكون مهارةً ممتازة تضيفها إلى مهاراتك إذ سيُحَسِّن كفاءة مشاريعك مقارنة بغيره من الأدوات، كما أنه جيدٌ في الاختبارات ويتوافق مع أُطر جافا سكريبت الأخرى. كما يُعدّ إطارًا سهل التعلُّم ويستطيع أي مطوّر جافا إتقانه بسرعة والبدء بإنشاء تطبيقاته الجديدة باستخدامه ذلك أن React في جوهره هو جافا سكريبت بسيط أُضيفت له بعض الوظائف الجديدة. وبالإضافة لما سبق يوفر ريآكت React شيفراتٍ برمجية قابلة لإعادة الاستخدام تختصر عليك الوقت فلن تضطر لإعادة اختراع العجلة وكتابة الشيفرات نفسها من الصفر في كل مرة، كما أن مكونات ريآكت معزولة عن بعضها فالتحديثات التي يجريها المطوّر على أحد المكونات لن تؤثر على البقية وهذا يعطيه أريحيةً وكفاءة أكبر في إنجاز العمل. كما يوفر ريآكت العديد من الأدوات التي تسهّل العمل على مشروعك، خاصة في المراحل المتقدمة. من بين هذه الأدوات React Developer Tools، وهو إضافة extension متوفرة لمتصفح جوجل كروم وأيضًا لمتصفح فايرفوكس. تتيح لك هذه الإضافة فحص شجرة DOM الخاصة بتطبيقك وتحريرها مباشرة باستخدام React، مما يمنحك تحكّمًا أكبر ويسهّل عليك اكتشاف الأخطاء وتحسين الكود. 2. فيو Vue JS أُطلِقَ إطار العمل Vue JS في شهر فبراير من العام 2014، وهو من أحدث أُطر عمل جافا سكريبت، ورغم حداثة عهده فقد أصبح بسرعة واحدًا من أكثر الخيارات شعبيةً لمطوّري الويب، وذلك لعدة أسباب، نعرض أبرزها: السبب الأول هو صغر حجمه، وهذه صفةٌ مهمة في أي إطار عمل تنوي استخدامه ومع أي لغة برمجة، فأُطر العمل كبيرة الحجم التي تستهلك مساحةً كبيرة في الذاكرة تعمل بكفاءة أقل وتستغرق وقتًا أطول لتحميل التطبيقات وتشغيلها، أما مع إطار عمل صغير الحجم مثل Vue.js الذي يتراوح حجمه بين 20 و 25 كيلو بايت فقط سيكون أفضل أداء ويمكن تحميله بسرعة ثم البدء باستخدامه مباشرة. أما السبب الثاني والمميز حقًا أنك تستطيع استخدام Vue JS مع تطبيقاتك الحالية المبنية بلغة جافا سكريبت فقد صُمم هذا الإطار أساسًا ليُدمَج بسهولة مع التطبيقات الجاهزة وتعزيزها بميزات جديدة، على سبيل المثال إذا احتجت لإضافة وظيفة معينة إلى تطبيقك وكان Vue.js يحققها فيمكنك استخدامه بأريحية، كما أنه يتضمن العديد من المكونات القادرة على التكامل بسهولة مع أي تطبيق تقريبًا. وأخيرًا من الضروي التنويه لميزة البساطة التي يتمتع بها Vue JS فتعابيره البرمجية بسيطة وسهلة الفهم، ويمكنك تعلُّمه بسرعة وبأي وقت خصوصًا إذا كنت تتقن إطار Angular أو React، وفي هذا المجال ننصحك بتحميل كتاب أساسيات إطار العمل Vue.js 1.0.0 الذي توفره أكاديمية حسوب مجانًا وباللغة العربية. 3. أنجولار Angular JS أطلقت جوجل إطار العمل أنجولار Angular عام 2012 وقد استغرق هذا الإطار بعض الوقت لينال شعبية بين المطورين وهو اليوم واحد من أكثر أُطر العمل شهرة ويستعمل في عدد كبير من مواقع الويب حول العالم، فما السر وراء انتشاره؟ وما الميزات التي يقدمها؟ سنجيبك عن هذا السؤال بخمس نقاط مختصرة: يلائم Angular منصات متعددة cross-platform، فيمكنك استخدامه لتطوير تطبيقات سطح المكتب، وتطبيقات الهاتف الجوال، وتطبيقات الويب، وهذه من أفضل مميزاته. إطار عمل مفتوح المصدر ويمكن تعديل شيفرته المصدرية حسب متطلبات العمل، وهذا يفيدنا في المشاريع المعقدة التي تحتاج متطلبات خاصة وجود مجتمع كبير من المطورين الداعمين له والساعين لتحسينه باستمرار يتلقى Angular دعمًا مستمرًا من جوجل وتحديثات منتظمة إلى جانب الدعم الذي يتلقاه من مجتمع المطورين إطار سهل الاستخدام وهذه ميزة مهمة للمبتدئين خاصة فلن يحتاجوا وقتًا طويلًا لتعلم Angular وإتقانه مقارنة بأُطر العمل الأخرى. كانت هذه بعض من مزايا Angular التي تجعله بدايةً جيدة لأي مطوّر يرغب باستخدام أطر عمل جافا سكريبت، علمًا أنه يتمتع بمزايا أخرى عديدة ستتعرف عندما تتعامل معه، ويمكنك البدء برحلة برحلة استكشافه بقراءة مقالات قسم Angular على أكاديمية حسوب. 4. بوليمر Polymer أنُشِئ إطار العمل Polymer من قبل مجموعة من مطوري شركة جوجل على غرار Angular، وهو أيضًا مفتوح المصدر لكنه أحدث عهدًا منه، ومستودعه على GitHub متاح لكل من يحب المساهمة في المشاريع مفتوحة المصدر. يُعَدّ Polymer أقرب للمكتبة منه لإطار العمل المتكامل التقليدي لذا فهو يمنحك أريحية كبيرة في التحكم بطريقة تنظيم بيئة العمل لمشروعك، ويتمتع بمزايا أخرى عديدة أبرزها سهولة الاستخدام، فعلى سبيل المثال يكفيك ملف HTML واحد فقط لبناء عدة عناصر ويب في تطبيقات Polymer وتخصيصها، وهذا يجعله سهل التعلم إذا ما قارناه بأطر عمل أخرى مثل Angular الذي ستحتاج فيه عدة ملفات HTML لفهم وتطوير عنصر واحد فقط. كما يتميز Polymer بسهولة التعامل مع متغيرات CSS ومخاليط mixin قابلة لإعادة الاستخدام، مما يزيد توافقيته مع CSS3 ويمنحك القدرة على استثمار كل ميزة جديدة تقدمها، وهذا ينعكس على جودة وجاذبية وتنوع تصميمات مواقع الويب التي تبنهيها يهذا الإطار. ونذكر من مميزاته أيضًا التوافق مع المكتبات والأدوات الأخرى المستخدمة في عملية التطوير، إذ يمكنه تبادل البيانات معها بسهولة ومباشرة من دون المرور بأي طبقة إضافية، فضلًا عمّا يوفره من توثيقات حديثة وغنية بالتفاصيل تساعدك على تعلم كافة جوانبه بسرعة وبإتقان، دون أن ننسى مجتمع المطورين الذين يستخدمون Polymer حول العالم وما يمكن أن يقدموه لك من مساعدة في إيجاد الحل لأي مشكلة تعترضك. 5. إمبر Ember JS يستخدم إطار العمل إمبر Ember لتطوير تطبيقات الويب المعقدة والغنية بالميزات، وهو إطار عمل متكامل بالمعنى التقليدي للكلمة إذ يتضمن عددًا كبيرًا من الواجهات البرمجية APIs، ولديه طريقة محددة لإنجاز عمل، فجميع المكونات التي قد تحتاج مكتوبة وجاهزة للنشر وتستطيع الاعتماد عليها من دون كتابتها من الصفر، يُعرف هذا الأسلوب بالاتفاقيات أو التقاليد conventions وهو يتضمن الآلية المثلى لإنجاز أعمال المشروع وسيزيد إنتاجيتك في العمل، لأنك لن تضيع وقتك في إعادة برمجة المكونات الجاهزة بل ستستفيد منه للتركيز على وظائف التطبيق. وفي هذا المجال يساعدك موقع Ember الرسمي EmberAddons.com في التعرف على جميع أنواع الإضافات plugins الجاهزة للاستخدام فهو مثل مستودع تعاوني للإضافات يساهم فيه مطورو Ember، فإذا كنت تبحث عن الشيفرة البرمجية الكاملة لإضافة معينة أو عن جزء من شيفرة برمجية معينة، يمكنك البحث في هذا المستودع وأخذ ما تحتاجه لتطبيقك، وهذا بدوره يرفع إنتاجيتك ويُسَرِّع وتيرة عملك فالإنتاجية العالية هي الميزة الأهم للإطار Ember. كما أن التوافقية مع الإصدارات السابقة ميزة أخرى مهمة في الإطار، فإذا كان تطبيقك يعمل باستقرار على إصدار معين من Ember لأنه يتوافق مع ميزات محددة فيه، وظهر تحديث جديد للإطار فلن تحتاج عندها لإيقاف تطبيقك وإجراء تغييرات جوهرية فيه أو حتى إعادة النظر في طريقة بنائه كما تفعل عادة مع الكثير من الأدوات البرمجية فإصدارات Ember الجديدة تعمل دائمًا بسلاسة مع التطبيقات التي ما زالت تستخدم إصدارات أقدم، وكل ما عليك الاهتمام هو إجراء بعض التغييرات لتستفيد من ميزات الإصدار الجديد حتى تُحسِّن عمل تطبيقك. 6. باك بون Backbone صدر إطار العمل باك بون Backbone عام 2016 وهو الأحدث في قائمتنا المعروضة ضمن المقال، ورغم حداثته فقد اكتسب انتشارًا واسعًا بين المطورين لما يتمتع به من ميزات أبرزها تعدد المنصات cross-platform، فيمكنك استخدامه مثلًا لبناء تطبيق يعمل على الجوال وعلى الويب في آن واحد، مما يوفر زمن التطوير والاختبار فلن تضطر لإعادة برمجة التطبيق مرتين. يساعد إطار العمل Backbone في تطوير الواجهة الخلفية back-end للتطبيق ويمكنه التفاعل بسلاسة مع الواجهة الخلفية لتطبيقك أو موقعك الحالي، كما يستطيع التفاعل مع واجهات برمجة التطبيقات APIs لتنفيذ مهام القراءة والكتابة والحذف. ويتضمن Backbone العديد من الاتفاقيات conventions التي تناسب معظم الأغراض وإذا التزمت بها ستوفر عليك الكثير من الوقت اللازم لكتابة التعليمات البرمجية من الصفر، وستمنحك شيفرة مصدرية نظيفة وسهلة الفهم والصيانة حتى على المطورين الآخرين. 7. نيتف سكريبت NativeScript يستخدم إطار العمل نيتف سكريبت NativeScript لتطوير تطبيقات جوال أصيلة Native لنظامي تشغيل أندرويد و IOS باستخدام جافا سكريبت، وهذه في الواقع هي ميزته الكبرى، فتطبيقات نيتف سكريبت NativeScript متعددة المنصات ستعمل على كلا نظامي التشغيل أندرويد و IOS في وقت واحد وبالتالي تحتاج لكتابة كود تطبيقك مرة واحدة فقط وسيعمل مباشرة على كلا النظامين بكل سلاسة. والنقطة المهمة الأخرى التي تحتسب لصالح نيتف سكريبت أنه مفتوح المصدر، وهو ما يُسهِّل الاستفادة من تجارب الآخرين وإيجاد الحلول لمعظم المشكلات التي قد تواجهك، أضف إلى ذلك أن المصدر المفتوح يمنح نيتف سكريبت مجتمعًا نشطًا من المطورين حول العالم يضيفون إليه ميزاتٍ جديدة باستمرار، ويتيح أمامك العديد من الإضافات الجاهزة لتستخدمها في تطبيقك تمامًا كما هي من دون أي مجهود إضافي. 8. نود جي اس Node JS يُعَدّ نود جي اس Node JS إطار العمل المفضل والأكثر شيوعًا لدى مطوري جافا سكريبت، فمنذ انطلاقته الأولى في العام 2009 أصبح واحدًا من الأدوات الرائدة في مجال إنشاء تطبيقات الويب من طرف الخادم وتشغيلها. وتعود شعبيته الكبيرة لأسباب متعددة في مقدمتها اعتماد الكثير من الشركات عليه، فكل شركة تقريبًا تستخدم Node.js في أحد جوانب عملها نحو أدوات الدردشة المباشرة live chat التي تجدها في معظم مواقع الويب اليوم والتي لا بدّ ستصادفها مستقبلًا في عملك ومعرفتك بالأداة لإطار Node JS ستعينك على التعامل بكفاءة مع هذا النوع من تطبيقات الاتصال، وهناك قائمة طويلة من التطبيقات الشهيرة التي تعتمد Node.js مثل: PayPal و LinkedIn و Yahoo و Uber و eBay، وقد دفع هذا الانتشار الواسع المطورين لتعلمه والإقبال على استخدامه. الدور الأساسي لإطار العمل Node.js هو تمكين التطوير من جانب الخادم server-side كما ذكرنا، لكنه في واقع الأمر يعمل بكفاءة مع طرف العميل client-side أيضًا ويسهل تبادل البيانات بين الطرفين والتنسيق بينهما ليكونا متزامنين، فأي تغيير تنفذه سينعكس مباشرة على التطبيق أو صفحة الويب، ولذا يُعَرّف Node.js بأنه بيئة تشغيل لجافا سكريبت JavaScript runtime environment أكثر من كونه إطار عمل بالمعنى الدقيق بسبب قدرته على التعامل مع هذه الاتصالات المتزامنة بين الطرفين والسرعة التي يوفرها لعملية التطوير. بالإضافة لكل الإيجابيات السابقة يطرح نود جي اس خيارات وميزات جديدة دوريًا ليواكب اتجاهات الويب الحديثة، ويتمتع بمجتمع كبير يدعمه ويساهم في تحسينه وتطوير العديد من وحداته modules وميزاته الجديدة، وستكون منتدايات هذا المجتمع مصدرًا جيدًا لإيجاد أي معلومة تبحث عنها ولحل أي مشكلة تواجهها، كما يمكنك المساهمة بدورك في تطوير هذا المشروع مفتوح المصدر بما يحقق الفائدة لجميع المستخدمين. ننصحك في ختام هذه الفقرة بمراجعة مقالات Node.js أو بتحميل كتاب دليلك إلى Node.js مجانًا من أكاديمية حسوب. 9. ميتيور Meteor حظيت أداة ميتيور Meteor بشعبية كبيرة رغم حداثتها، وتكمن قوتها الأساسية بأنها لا تقتصر على تطوير تطبيقات الويب بل يمكن استخدامها لتطوير التطبيقات لمختلف أنواع المنصات مثل التطبيقات التي تعمل على الويب والهاتف الجوال بذات الوقت فتوفر بذلك وقتك وجهدك. ومن مميزاتها أيضًا أنها تعمل في الزمن الحقيقي real-time وهذه ميزة يفضلها الكثير من المطوّرين خصوصًا في الآونة الأخيرة لأن أي تعديل تجريه على الشيفرة البرمجية ستظهر نتائجه تلقائيًا في خرج التطبيق في الزمن الحقيقي، وهذا يسهل عليك عمليات الاختبار وخاصةً تلك التي تقتصر على أجزاء معينة من التطبيق. تشبه Meteor إطار العمل Node.js في قدرتها على التعامل مع طرفي الخادم والعميل في عملية تطوير الويب، وتنفذ ذلك باستخدام شيفرات جافا سكريبت فقط من دون الحاجة لأي لغة برمجة أخرى، وهذا يجعلها سهلة التعلم فطالما أنك تتقن جافا سكريبت يمكنك تعلم Meteor بسرعة وسهولة. 10. ميثريل Mithril يتميز إطار العمل ميثريل Mithril بحجمه الصغير فهو يبلغ حوالي 9 كيلو بايت فقط، حتى أنه أصغر من Vue.js وهذا يجعله سريعًا مقارنة بغيره من الأطر، وهو يستخدم أساسًا لتطوير تطبيقات الويب ذات الصفحة الواحدة single-page web applications لأنه على عكس Node.js و Meteor لا يستطيع التعامل مع طرفي العميل والخادم في أثناء التطوير إنما يتخصص بتطوير طرف العميل client-side فقط. وإطار Mithril سهل التعلم ويناسب المطورين المبتدئين الراغبين بإتقان إطار عمل جديد بسرعة، إذ يمكنك البدء باستخدامه مباشرة بمجرد تعلم بعض التوابع methods الأساسية التي يوفرها، كما أنه يتصف بسرعة الأداء مقارنة بأطر العمل الأخرى مثل ريآكت React فقد يستغرق تحميل التطبيق في Mithril نصف الزمن الذي يستغرقه في ريآكت أو غيره. وعندما نتحدث عن السرعة سنتسأل بالتأكيد كيف سيتعامل إطار يعمل في الزمن الحقيقي مثل Mithril مع التحديثات الدورية updates التي ينبغي تطبيقها في الزمن الحقيقي؟ وهل يستطيع الحفاظ على جودة أدائه أثناء ذلك؟ بالرغم من صعوبة الأمر لكن Mithril يتعامل جيدًا مع التحديثات ويحافظ على سرعة أدائه معها. 11. أوريليا Aurelia يستخدم إطار العمل أوريليا Aurelia للتطوير من جانب العميل client-side، وهو سهل التعلم ويناسب المطورين المبتدئين إذ لن تحتاج لتعلمه سوى لتعلم أساسيات جافا سكريبت وتعلم لغة HTML الحديثة وهذه المعلومات متوفرة لدى جميع مطوري جافاسكريبت. يعتمد أوريليا مبدأ الاتفاقيات بدلاً من الإعدادات Conventions Over Configuration يعني ذلك أنه يوفر للمطور عدد من الاتفاقيات الثابتة التي سيعتمدها الإطار تلقائيًّا دون أن يكلف المطور عناء برمجة وكتابة الإعدادات لعناصر تقليدية توجد وتستخدم بكثرة في معظم التطبيقات، وهذا يجعله أسرع وأكثر كفاءة في التطوير ويمنحك شيفرة مصدرية نظيفة وفعالة. لا يقارن حجم المجتمع الذي يستخدم Aurelia بحجم المجتمع الذي يستخدم الأطر الأخرى مثل رياكت أو أنجولار مثلًا، لكنه مع ذلك يُعدّ مجتمعًا نشيطًا ويسعى لتطوير هذا الإطار وسيساعدك في حل مشكلاتك في التعامل معه. 12. سوكيت Socket.io صُممت Socket.io للعمل مع طرفي العميل والخادم، وهي مكتبة حديثة من مكتبات جافا سكريبت اكتسبت شهرة وشعبية واسعة لأنها في المقام الأول تعمل في الزمن الحقيقي وهي ميزة مفضلة للكثير من المطورين الراغبين بتطوير هذا النوع من التطبيقات، وتتصف تطبيقات الزمن الحقيقي بأن التعديلات التي تنفذ على شيفرتها المصدرية تنعكس مباشرةً على التطبيق في اللحظة نفسها. ورغم العديد من الآراء التي تنادي بعدم الحاجة إلى Socket.io اليوك بسبب وجود بدائل أخرى يمكنها الحلول مكانها، لكن انتشار هذه المكتبة ما زال مستمرًا ومازالت تحظى بشعبية وبمستخدمين جدد. وتتميز Socket.io بسهولة تعلُّمها وتنفيذها في المشاريع، فإذا كنت ترغب بإضافة مهارة جديدة إلى قائمة مهاراتك قد تفيدك في مسيرتك المهنية مستقبلًا فهذه الأداة تُعدّ خيارًا جيدًا. الخلاصة إلى هنا نكون قد وصلنا لنهاية مقالنا الذي تعرفنا فيه على أشهر أُطر عمل جافا سكريبت وأهم مميزات كل إطار، لتكون قادرًا على اختيار ما يناسب احتياجات عملك، فعلى سبيل المثال إذا كان تخصصك هو تطوير تطبيقات الجوال فقط فيمكنك اختيار NativeScript، وإذا كنت ترغب بتطوير تطبيقات متعددة المنصات تستطيع الاتجاه نحو Meteor، وإذا كانت غايتك الأولى هي السرعة فستبحث عن الإطار ذي الحجم الأصغر وما إلى ذلك، فكر في احتياجاتك ومتطلباتك أولًا ثم اتخذ قرارك. ترجمة -وبتصرف- لمقال 12 Best JavaScript Frameworks لصاحبه Mark Bynum. اقرأ أيضًا تعلم جافا سكريبت مدخل إلى إطار عمل الويب Express وبيئة Node تعرف على مفهوم إطار العمل Framework وأهميته في البرمجة متى نستعمل إطار عمل للتطوير باستخدام JavaScript التوثيق العربي للغة جافا سكريبت
  15. قد تحتاج في سكريتات باش للتعامل مع مئات المتغيرات التي يدخلها المستخدم وفي هذه الحالة لن يكون من المناسب أن تنشئ هذه المتغيرات يدويًا، وهنا يأتي دور المصفوفات Arrays الحل المنقذ في مثل هذه الحالات، سنشرح في هذا المقال أساسيات التعامل مع المصفوفات في باش وتعديلها بكفاءة. أنشئ مصفوفتك الأولى في باش لنفترض أنك تحتاج لكتابة سكربت بسيط لتحديث الطابع الزمني timestamp لخمس ملفات مختلفة، والطابع الزمنية هي مجموعة محارف وأرقام تدل على تاريخ ووقت إجراء تعديل معين على الملف. لننشئ السكربت timestamp.sh الخاص بهذه العملية أولًا بطريقة المتغيرات دون استخدام المصفوفات، فسيكون على الشكل التالي: #!/bin/bash file1="f1.txt" file2="f2.txt" file3="f3.txt" file4="f4.txt" file5="f5.txt" touch $file1 touch $file2 touch $file3 touch $file4 touch $file5 لننشئه الآن باستخدام المصفوفات ونلاحظ الفرق، سنخزن في المصفوفة أسماء الملفات الخمسة عوضًا عن تعريف خمس متغيرات تقابل أسماء الملفات الخمسة، وفيما يلي الصيغة العامة لتعريف المصفوفات في باش: array_name=(value1 value2 value3 … ) لنطبق الصيغة على حالتنا، فستكون مصفوفة أسماء الملفات وفق التالي: files=("f1.txt" "f2.txt" "f3.txt" "f4.txt" "f5.txt") السكربت الآن أوضح وأكثر كفاءة وأقرب لمعايير الكود النظيف، فقد استبدلنا خمسة متغيرات بمصفوفة واحدة فقط. الوصول لعناصر مصفوفات باش تبدأ فهرسة مصفوفات باش بالصفر 0 ويستخدم الدليل n-1 للوصول إلى العنصر n من المصفوفة. فإذا رغبنا بإظهار العنصر الثاني في المصفوفة فسنكتب التالي: echo ${files[1]} وللوصول للعنصر الثالث: echo ${files[2]} وهكذا لبقية العناصر. لنجرب أمرًا آخر، ألقِ نظرة على السكربت التالي reverse.sh الذي سيظهر كامل عناصر مصفوفتك بترتيب عكسي من العنصر الأخير إلى الأول: #!/bin/bash files=("f1.txt" "f2.txt" "f3.txt" "f4.txt" "f5.txt") echo ${files[4]} echo ${files[3]} echo ${files[2]} echo ${files[1]} echo ${files[0]} وستحصل بتنفيذه على الخرج التالي: سنتعلم لاحقًا كيف نظهر عناصر المصفوفة باستخدام الحلقات Loops، فتكرار الأمر echo للغرض نفسه عددً كبيرًا من المرات ليس الطريقة الأفضل لإظهار عناصر المصفوفة. يمكننا أيضًا إظهار عناصر المصفوفة دفعة واحدة في سطرٍ واحد كما في الأمر التالي: echo ${files[*]} f1.txt f2.txt f3.txt f4.txt f5.txt ويساعدك الأمر التالي على إظهار عدد عناصر المصفوفة الذي يسمى اصطلاحًا حجم المصفوفة size of array: echo ${#files[@]} 5 وبوسعك تغيير قيمة أي عنصر من عناصر المصفوفة بسهولة، ألقِ نظرة على السطر أدناه إذ نغير فيه قيمة العنصر الأول إلى القيمة "a.txt": files[0]="a.txt" إضافة عناصر جديدة إلى مصفوفة باش لننشئ مثلًا المصفوفة التالية التي تتضمن أسماء أشهر توزيعات لينكس: distros=("Ubuntu" "Red Hat" "Fedora") تحتوي المصفوفة الحالية على ثلاثة عناصر، ويمكنك إضافة عناصر أخرى إلى نهايتها باستخدام المعامل =+، دعنا نضيف مثلًا توزيعة Kali بكتابة: distros+=("Kali") تحتوي المصفوفة الآن أربعة عناصر، وتبين الصورة أدناه السكربت بعد إضافة العنصر الأخير: حذف عناصر من مصفوفة باش لننشئ مصفوفة تتضمن الأعداد من 1 إلى 5: num=(1 2 3 4 5) يمكنك إظهار كافة عناصر المصفوفة كما يلي: echo ${num[*]} 1 2 3 4 5 لنفترض أننا نرغب بحذف العنصر الثالث منها، فسنستخدم الأمر unset وفق التالي: unset num[2] أظهر الآن كافة عناصر المصفوفة كما يلي: echo ${num[*]} 1 2 4 5 ولاحظ حذف العنصر الثالث. يمكنك أيضًا حذف المصفوفة بالطريقة نفسها بكتابة الأمر أدناه: unset num تبين الصورة أدناه تنفيذ سكربت يتضمن كل ما تعلمناه في هذه الفقرة: إنشاء مصفوفة هجينة بأنواع مختلفة من البيانات تتميز باش عن الكثير من لغات البرمجة بقدرتها على إنشاء مصفوفات هجينة hybrid arrays تحتوي أنواعًا مختلفة من البيانات، مثلًا أعداد صحيحة وسلاسل نصية وغيرها كما في سكربت باش التالي باسم user.sh: #!/bin/bash user=("john" 122 "sudo,developers" "bash") echo "User Name: ${user[0]}" echo "User ID: ${user[1]}" echo "User Groups: ${user[2]}" echo "User Shell: ${user[3]}" لاحظ أن مصفوفة المستخدمين السابقة تتضمن أربع عناصر هي: العنصر "john" نوعه سلسلة نصية String العنصر 122 نوعه عدد صحيح Integer العنصر "sudo,developers" نوعه سلسلة نصية String العنصر "bash" نوعه سلسلة نصية String سيكون خرج السكربت على النحو التالي: الخلاصة نصل بذلك لنهاية مقال المصفوفات في لغة باش Bash الذي تعرفنا فيه على المصفوفات والوصول لعناصرها وعكس ترتيبها وتعديلها وإضافة وحذف عناصرها، كما تعرفنا على طريقة إلى إنشاء مصفوفات هجينة تحتوي على أنواع بيانات مختلفة، تابع المقال التالي حيث سنتعرف فيه على طريقة التعامل مع المعاملات الحسابية بكفاءة ضمن سكربتات باش Bash. ترجمة -وبتصرف- للمقال Using Arrays in Bash. المقال السابق: تمرير الوسطاء إلى سكربت باش Bash طريقة التعامل مع المتغيرات وتمرير الوسطاء لسكربت باش مدخل إلى صدفة باش Bash الحصول على مدخلات من لوحة المفاتيح وإجراء العمليات الحسابية في سكربتات الصدفة الأخطاء الشائعة التي تحدث عند كتابة سكربتات الصدفة
×
×
  • أضف...