ديف أوبس

بنى التحكم في سكربت الشل

كيف تستخدم بنى التحكم (Flow Control) في سكربتات الصدفة (Shell Scripts) – الجزء 3

تُعد سكربتات الصدفة (Shell Scripts) من الأدوات الأساسية والمهمة في إدارة الأنظمة وتبسيط المهام المتكررة في أنظمة التشغيل المبنية على يونكس وLinux. وهي تسمح بكتابة أوامر النظام بطريقة برمجية منظمة تؤدي وظائف مختلفة دون تدخل مباشر من المستخدم في كل مرة. من بين المفاهيم المحورية في هذه السكربتات، تأتي بنى التحكم (Flow Control) التي تمنح السكربت القدرة على اتخاذ قرارات، وتنفيذ تكرارات، والتحكم في تدفق التنفيذ بما يتوافق مع المنطق البرمجي المطلوب.

في هذا الجزء الثالث من سلسلة “كيف تستخدم بنى التحكم في سكربتات الصدفة”، سيتم التعمق في بعض المفاهيم المتقدمة والمتفرعة من بنى التحكم، وذلك بعد أن تم تغطية الأساسيات في الأجزاء السابقة. يشمل هذا الجزء مناقشة تفصيلية للبنى الشرطية المتقدمة، الحلقات المتداخلة، استخدام جمل case المعقدة، إدارة الأخطاء والتحكم في تدفق التنفيذ عبر trap و exit، بالإضافة إلى استراتيجيات الكتابة البرمجية النظيفة (clean code) داخل السكربتات.


البنية الشرطية المتقدمة (Advanced Conditional Structures)

في سكربتات الصدفة، تمثل الجملة if حجر الأساس لأي بنية شرطية. إلا أنه يمكن تطويرها لتعبر عن حالات أكثر تعقيدًا من خلال تركيب الشروط باستخدام أدوات المقارنة المنطقية، والاستفادة من أدوات مثل [[ ... ]] و (( ... )).

استخدام [[ ... ]] مقابل [ ... ]

bash
# تقليدية if [ "$a" -eq 5 ]; then echo "a is 5" fi # حديثة وتدعم تعبيرات أكثر if [[ $a -eq 5 && $b -lt 10 ]]; then echo "a is 5 and b is less than 10" fi

البناء [[ ... ]] يسمح باستخدام تعبيرات منطقية أكثر مرونة، ويعالج المشاكل المتعلقة بالمسافات البيضاء، كما أنه يُفضل عند العمل مع سلاسل النصوص (strings) والتعبيرات النمطية (regex).

استخدام (( ... )) للحسابات الرياضية

bash
if (( a % 2 == 0 )); then echo "a is even" fi

هذا الأسلوب مثالي عند التعامل مع العمليات الحسابية المعقدة داخل الشروط.


الحلقات المتداخلة (Nested Loops)

في سياقات أكثر تعقيدًا، قد تتطلب بعض العمليات استخدام حلقات متداخلة للتعامل مع بيانات متعددة الأبعاد، مثل ملفات داخل مجلدات متعددة أو تكرار العمليات على قوائم مركبة.

bash
for dir in /home/*; do for file in "$dir"/*.txt; do echo "Processing file $file" done done

يجب الانتباه إلى حدود الموارد في الحلقات المتداخلة، لأنها قد تؤدي إلى استهلاك مفرط للذاكرة أو وقت المعالجة إذا لم يتم التحكم بها بشكل صحيح.


جمل case المتقدمة

تستخدم case بشكل أساسي كبديل أكثر تنظيماً من if-elif-else، خصوصاً عند التحقق من قيمة متغير بناءً على مجموعة من الحالات المعروفة. من الممكن توسيعها لتشمل تعبيرات نمطية:

bash
case "$input" in start|START) echo "Starting service..." ;; stop|STOP) echo "Stopping service..." ;; restart|RESTART) echo "Restarting service..." ;; *) echo "Unknown command" ;; esac

يمكن استخدام case لمعالجة امتدادات الملفات، خيارات المستخدم، أو فحص أسماء العمليات قيد التشغيل.


التحكم في الأخطاء: استخدام trap و exit

التحكم في الأخطاء عنصر جوهري في السكربتات المتقدمة. عند تنفيذ أوامر حساسة، ينبغي التأكد من التعامل المناسب مع حالات الفشل. لذلك تُستخدم جملة trap لاعتراض إشارات النظام وتنفيذ أوامر معينة عند الخروج أو حدوث خطأ.

bash
cleanup() { echo "Cleaning up temporary files..." rm -f /tmp/mytempfile } trap cleanup EXIT

هذا الكود يضمن أن وظيفة cleanup يتم تنفيذها سواء انتهى السكربت بنجاح أو بشكل غير متوقع. يمكن أيضًا التعامل مع إشارات أخرى مثل SIGINT و SIGTERM:

bash
trap 'echo "Interrupted"; exit 1' SIGINT

استخدام exit مع رموز الخروج

من الأفضل دائمًا تحديد رمز الخروج exit لتعريف نجاح أو فشل السكربت:

bash
if ! cp file1 file2; then echo "Copy failed" >&2 exit 1 fi

القيمة 0 تدل على نجاح التنفيذ، والقيم الأخرى (مثل 1, 2, …) تُستخدم للتعبير عن أنواع مختلفة من الخطأ.


كتابة كود نظيف في سكربتات الشل

الكتابة الجيدة ليست مجرد كتابة كود يعمل، بل كود واضح، قابل للصيانة، ويسهل قراءته. تشمل النصائح العملية لذلك:

تجنب التكرار

يجب تجنب كتابة الكود نفسه في أكثر من موضع. الأفضل إنشاء دوال reusable:

bash
log_message() { echo "$(date +%F\ %T) - $1" } log_message "Starting process..."

التوثيق

تعليق الكود أمر ضروري، خاصةً عند استخدام بنى تحكم معقدة:

bash
# التحقق من أن الملف موجود وقابل للقراءة if [[ -r "$filename" ]]; then process_file "$filename" fi

التنظيم باستخدام ملفات تكوين

بدلاً من تضمين الإعدادات داخل السكربت، يمكن تحميلها من ملف خارجي:

bash
source /etc/my_script.conf

يساعد هذا الأسلوب في فصل المنطق البرمجي عن المعطيات المتغيرة.


مثال عملي متكامل لبنى التحكم

فيما يلي مثال تطبيقي على سكربت يستخدم عدة بنى تحكم للتحقق من حالة خدمة ومعالجتها بناءً على نتائج متعددة:

bash
#!/bin/bash SERVICE="nginx" check_service() { systemctl is-active --quiet $SERVICE } restart_service() { echo "Restarting $SERVICE..." systemctl restart $SERVICE } trap 'echo "Script interrupted"; exit 1' SIGINT if check_service; then echo "$SERVICE is running" else echo "$SERVICE is not running" read -p "Do you want to restart it? (y/n): " answer case "$answer" in y|Y) restart_service ;; *) echo "Service was not restarted" ;; esac fi

جدول ملخص لأهم بنى التحكم في سكربتات الشل

البنية الاستخدام مثال
if, elif, else اتخاذ قرارات بناءً على شروط if [[ $a -gt 10 ]]; then echo "ok"; fi
for التكرار عبر قائمة عناصر for i in *.txt; do echo $i; done
while, until التكرار بشرط while [[ $count -lt 5 ]]; do ... done
case مطابقة قيمة متغير مع حالات متعددة case $x in 1) ... ;; 2) ... ;; *) ... ;; esac
trap اعتراض إشارات النظام trap cleanup EXIT
exit الخروج من السكربت مع رمز معين exit 1
function إنشاء دوال قابلة لإعادة الاستخدام my_function() { echo "Hello"; }

الخلاصة

بنى التحكم في سكربتات الصدفة هي العمود الفقري لأي سكربت فعال، لأنها تُمكن من اتخاذ قرارات دقيقة، وتنفيذ تكرارات محسوبة، والتعامل مع الأخطاء، وتنظيم الكود بطريقة تجعله قابلاً للتوسعة والصيانة. في هذا الجزء الثالث، تم استعراض كيفية التعمق في هذه البنى باستخدام طرق متقدمة تجعل السكربتات أكثر مرونة وقوة، وهي مهارات ضرورية لأي مسؤول أنظمة أو مطور يهدف إلى أتمتة المهام على نحو احترافي.


المصادر