كيف تستخدم بنى التحكم (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).
استخدام (( ... )) للحسابات الرياضية
bashif (( a % 2 == 0 )); then
echo "a is even"
fi
هذا الأسلوب مثالي عند التعامل مع العمليات الحسابية المعقدة داخل الشروط.
الحلقات المتداخلة (Nested Loops)
في سياقات أكثر تعقيدًا، قد تتطلب بعض العمليات استخدام حلقات متداخلة للتعامل مع بيانات متعددة الأبعاد، مثل ملفات داخل مجلدات متعددة أو تكرار العمليات على قوائم مركبة.
bashfor dir in /home/*; do
for file in "$dir"/*.txt; do
echo "Processing file $file"
done
done
يجب الانتباه إلى حدود الموارد في الحلقات المتداخلة، لأنها قد تؤدي إلى استهلاك مفرط للذاكرة أو وقت المعالجة إذا لم يتم التحكم بها بشكل صحيح.
جمل case المتقدمة
تستخدم case بشكل أساسي كبديل أكثر تنظيماً من if-elif-else، خصوصاً عند التحقق من قيمة متغير بناءً على مجموعة من الحالات المعروفة. من الممكن توسيعها لتشمل تعبيرات نمطية:
bashcase "$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 لاعتراض إشارات النظام وتنفيذ أوامر معينة عند الخروج أو حدوث خطأ.
bashcleanup() {
echo "Cleaning up temporary files..."
rm -f /tmp/mytempfile
}
trap cleanup EXIT
هذا الكود يضمن أن وظيفة cleanup يتم تنفيذها سواء انتهى السكربت بنجاح أو بشكل غير متوقع. يمكن أيضًا التعامل مع إشارات أخرى مثل SIGINT و SIGTERM:
bashtrap 'echo "Interrupted"; exit 1' SIGINT
استخدام exit مع رموز الخروج
من الأفضل دائمًا تحديد رمز الخروج exit لتعريف نجاح أو فشل السكربت:
bashif ! cp file1 file2; then
echo "Copy failed" >&2
exit 1
fi
القيمة 0 تدل على نجاح التنفيذ، والقيم الأخرى (مثل 1, 2, …) تُستخدم للتعبير عن أنواع مختلفة من الخطأ.
كتابة كود نظيف في سكربتات الشل
الكتابة الجيدة ليست مجرد كتابة كود يعمل، بل كود واضح، قابل للصيانة، ويسهل قراءته. تشمل النصائح العملية لذلك:
تجنب التكرار
يجب تجنب كتابة الكود نفسه في أكثر من موضع. الأفضل إنشاء دوال reusable:
bashlog_message() {
echo "$(date +%F\ %T) - $1"
}
log_message "Starting process..."
التوثيق
تعليق الكود أمر ضروري، خاصةً عند استخدام بنى تحكم معقدة:
bash# التحقق من أن الملف موجود وقابل للقراءة
if [[ -r "$filename" ]]; then
process_file "$filename"
fi
التنظيم باستخدام ملفات تكوين
بدلاً من تضمين الإعدادات داخل السكربت، يمكن تحميلها من ملف خارجي:
bashsource /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"; } |
الخلاصة
بنى التحكم في سكربتات الصدفة هي العمود الفقري لأي سكربت فعال، لأنها تُمكن من اتخاذ قرارات دقيقة، وتنفيذ تكرارات محسوبة، والتعامل مع الأخطاء، وتنظيم الكود بطريقة تجعله قابلاً للتوسعة والصيانة. في هذا الجزء الثالث، تم استعراض كيفية التعمق في هذه البنى باستخدام طرق متقدمة تجعل السكربتات أكثر مرونة وقوة، وهي مهارات ضرورية لأي مسؤول أنظمة أو مطور يهدف إلى أتمتة المهام على نحو احترافي.
المصادر
-
GNU Bash Manual: https://www.gnu.org/software/bash/manual/bash.html
-
Advanced Bash Scripting Guide: https://tldp.org/LDP/abs/html/

