بشكل افتراضي، سيبلغ البرنامج النصي Bash على Linux عن خطأ ولكنه سيستمر في العمل. سنوضح لك كيفية التعامل مع الأخطاء بنفسك حتى تتمكن من تحديد ما يجب أن يحدث بعد ذلك.
معالجة الأخطاء في البرامج النصية
إن التعامل مع الأخطاء يشكل جزءًا من البرمجة. فحتى إذا كتبت كودًا خاليًا من العيوب، فلا يزال من الممكن أن تواجه حالات خطأ. تتغير البيئة على جهاز الكمبيوتر الخاص بك بمرور الوقت، مع تثبيت البرامج وإلغاء تثبيتها، وإنشاء الدلائل، وإجراء الترقيات والتحديثات.
على سبيل المثال، قد يواجه البرنامج النصي الذي كان يعمل دون مشاكل صعوبات إذا تغيرت مسارات الدليل أو تغيرت الأذونات على ملف. الإجراء الافتراضي لـ Bash shell هو طباعة رسالة خطأ ومواصلة تنفيذ البرنامج النصي. وهذا خطأ افتراضي خطير.
إذا كان الإجراء الذي فشل حاسمًا لبعض عمليات المعالجة الأخرى أو الإجراء الذي يحدث لاحقًا في البرنامج النصي الخاص بك، فلن يكون هذا الإجراء الحاسم ناجحًا. ويعتمد مدى الكارثة التي قد تنتج عن ذلك على ما يحاول البرنامج النصي الخاص بك القيام به.
إن المخطط الأكثر قوة من شأنه أن يكشف عن الأخطاء ويسمح للبرنامج النصي بالعمل إذا كان من الضروري إيقاف تشغيله أو محاولة إصلاح حالة الخطأ. على سبيل المثال، إذا كان هناك دليل أو ملف مفقود، فقد يكون من المرضي أن يقوم البرنامج النصي بإعادة إنشائه.
إذا واجه البرنامج النصي مشكلة لا يمكنه التعافي منها، فيمكنه إيقاف التشغيل. وإذا كان لابد من إيقاف تشغيل البرنامج النصي، فيمكنه الحصول على فرصة لإجراء أي عملية تنظيف مطلوبة، مثل إزالة الملفات المؤقتة أو كتابة حالة الخطأ وسبب إيقاف التشغيل في ملف سجل.
اكتشاف حالة الخروج
تولد الأوامر والبرامج قيمة يتم إرسالها إلى نظام التشغيل عند انتهائها. وهذا ما يسمى حالة خروجهم. لها قيمة صفر إذا لم تكن هناك أخطاء، أو بعض القيم غير الصفرية إذا حدث خطأ.
يمكننا التحقق من حالة الخروج – المعروفة أيضًا باسم رمز الإرجاع – للأوامر التي يستخدمها البرنامج النصي، وتحديد ما إذا كان الأمر ناجحًا أم لا.
في Bash، يساوي الصفر القيمة true. إذا كانت استجابة الأمر مختلفة عن القيمة true، فإننا نعلم أن هناك مشكلة حدثت ويمكننا اتخاذ الإجراء المناسب.
انسخ هذا البرنامج النصي إلى محرر، ثم احفظه في ملف يسمى “bad_command.sh”.
#!/bin/bashif ( ! bad_command ); then
echo "bad_command flagged an error."
exit 1
fi
سوف تحتاج إلى جعل البرنامج النصي قابلاً للتنفيذ باستخدام chmod
الأمر. هذه خطوة مطلوبة لجعل أي نص برمجي قابلاً للتنفيذ، لذا إذا كنت تريد تجربة النصوص البرمجية على جهازك، فتذكر القيام بذلك لكل منها. استبدل اسم النص البرمجي المناسب في كل حالة.
chmod +x bad_command.sh
عندما نقوم بتشغيل البرنامج النصي نرى رسالة الخطأ المتوقعة.
./bad_command.sh
لا يوجد أمر مثل “bad_command”، ولا هو اسم وظيفة داخل البرنامج النصي. لا يمكن تنفيذه، لذا فإن الاستجابة ليست صفرًا. إذا لم تكن الاستجابة صفرًا، فسيتم استخدام علامة التعجب هنا كعلامة منطقية NOT
المشغل—جسم if
تم تنفيذ العبارة.
في البرنامج النصي في العالم الحقيقي، قد يؤدي هذا إلى إنهاء البرنامج النصي، وهو ما يفعله مثالنا، أو قد يحاول معالجة حالة الخطأ.
قد يبدو مثل exit 1
السطر زائد عن الحاجة. بعد كل شيء، لا يوجد شيء آخر في البرنامج النصي وسينتهي على أي حال. ولكن باستخدام exit
يسمح لنا الأمر بتمرير حالة الخروج إلى shell. إذا تم استدعاء البرنامج النصي الخاص بنا من داخل برنامج نصي ثانٍ، فسوف يعرف هذا البرنامج النصي الثاني أن هذا البرنامج النصي واجه أخطاء.
يمكنك استخدام المنطقي OR
عامل مع حالة الخروج لأمر ما، واستدعاء أمر آخر أو وظيفة في البرنامج النصي الخاص بك إذا كان هناك استجابة غير صفرية من الأمر الأول.
command_1 || command_2
يعمل هذا لأنه يتم تشغيل الأمر الأول OR
الأمر الثاني. يتم تنفيذ الأمر الموجود في أقصى اليسار أولاً. إذا نجح الأمر، فلن يتم تنفيذ الأمر الثاني. ولكن إذا فشل الأمر الأول، فسيتم تنفيذ الأمر الثاني. لذا يمكننا هيكلة الكود على هذا النحو. هذا هو “logical-or./sh”.
#!/bin/basherror_handler()
{
echo "Error: ($?) $1"
exit 1
}
bad_command || error_handler "bad_command failed, Line: ${LINENO}"
لقد قمنا بتعريف دالة تسمى error_handler
يؤدي هذا إلى طباعة حالة الخروج للأمر الفاشل، المحفوظة في المتغير $?
وسطر من النص يتم تمريره إليه عند استدعاء الوظيفة. يتم الاحتفاظ بهذا في المتغير $1
تقوم الوظيفة بإنهاء البرنامج النصي بحالة خروج واحدة.
يحاول البرنامج النصي التشغيل bad_command
وهو ما يفشل بشكل واضح، لذا فإن الأمر الموجود على يمين المنطقي OR
المشغل ||
يتم تنفيذ ذلك. هذا يستدعي error_handler
تقوم الدالة بتمرير سلسلة تحتوي على اسم الأمر الذي فشل، وتحتوي على رقم السطر الخاص بالأمر الفاشل.
سوف نقوم بتشغيل البرنامج النصي لرؤية رسالة معالج الخطأ، ثم التحقق من حالة خروج البرنامج النصي باستخدام الصدى.
./logical-or.sh
echo $?
طفلنا الصغير error_handler
توفر الوظيفة حالة الخروج لمحاولة التشغيل bad_command
، اسم الأمر ورقم السطر. تعد هذه معلومات مفيدة عند تصحيح أخطاء البرنامج النصي.
حالة الخروج من البرنامج النصي هي 1. حالة الخروج 127 التي تم الإبلاغ عنها بواسطة error_handler
تعني “لم يتم العثور على الأمر”. إذا أردنا، يمكننا استخدام ذلك كحالة خروج للنص البرمجي عن طريق تمريره إلى exit
يأمر.
وهناك نهج آخر يتمثل في التوسع error_handler
للتحقق من القيم المختلفة المحتملة لحالة الخروج ولإجراء إجراءات مختلفة وفقًا لذلك، باستخدام هذا النوع من البناء:
exit_code=$?if ( $exit_code -eq 1 ); then
echo "Operation not permitted"
elif ( $exit_code -eq 2 ); then
echo "Misuse of shell builtins"
.
.
.
elif ( $status -eq 128 ); then
echo "Invalid argument"
fi
استخدام set لفرض الخروج
إذا كنت تعلم أنك تريد أن يخرج البرنامج النصي الخاص بك عند وجود خطأ، فيمكنك إجباره على القيام بذلك. وهذا يعني أنك تتخلى عن فرصة أي تنظيف – أو أي ضرر إضافي أيضًا – لأن البرنامج النصي الخاص بك ينتهي بمجرد اكتشاف خطأ.
للقيام بذلك، استخدم set
الأمر مع -e
خيار (خطأ). يخبر هذا البرنامج النصي بالخروج كلما فشل أمر أو أعاد رمز خروج أكبر من الصفر. أيضًا، باستخدام -E
يضمن الخيار اكتشاف الأخطاء واحتجازها في وظائف shell.
للقبض أيضًا على المتغيرات غير المهيئة، أضف -u
خيار (غير محدد). للتأكد من اكتشاف الأخطاء في التسلسلات المنسقة، أضف -o pipefail
الخيار. بدون هذا، تكون حالة الخروج لتسلسل أوامر متسلسل هي حالة الخروج للأمر الأخير في التسلسل. لن يتم اكتشاف أمر فاشل في منتصف التسلسل المتسلسل. -o pipefail
يجب أن يأتي الخيار في قائمة الخيارات.
التسلسل الذي يجب إضافته إلى أعلى النص الخاص بك هو:
set -Eeuo pipefail
فيما يلي نص برمجي قصير يسمى “unset-var.sh”، مع وجود متغير غير محدد بداخله.
#!/bin/bashset -Eeou pipefail
echo "$unset_variable"
echo "Do we see this line?"
عندما نقوم بتشغيل البرنامج النصي، يتم التعرف على unset_variable كمتغير غير مهيأ ويتم إنهاء البرنامج النصي.
./unset-var.sh
الثاني echo
الأمر لن يتم تنفيذه أبدًا.
استخدام المصيدة مع الأخطاء
يتيح لك أمر Bash trap ترشيح أمر أو وظيفة يجب استدعاؤها عند رفع إشارة معينة. عادةً ما يتم استخدام هذا لالتقاط إشارات مثل SIGINT
الذي يظهر عند الضغط على تركيبة المفاتيح Ctrl+C. هذا النص هو “sigint.sh”.
#!/bin/bashtrap "echo -e '\nTerminated by Ctrl+c'; exit" SIGINT
counter=0
while true
do
echo "Loop number:" $((++counter))
sleep 1
done
ال trap
يحتوي الأمر على echo
الأمر و exit
الأمر. سيتم تشغيله عندما SIGINT
يتم رفعه. بقية البرنامج النصي عبارة عن حلقة بسيطة. إذا قمت بتشغيل البرنامج النصي وضغطت على Ctrl+C، فسترى الرسالة من trap
التعريف، وسيتم إنهاء البرنامج النصي.
./sigint.sh
يمكننا أن نستخدم trap
مع ERR
إشارة لالتقاط الأخطاء عند حدوثها. ويمكن بعد ذلك إرسالها إلى أمر أو وظيفة. هذا هو “trap.sh”. نرسل إشعارات الخطأ إلى وظيفة تسمى error_handler
.
#!/bin/bashtrap 'error_handler $? $LINENO' ERR
error_handler() {
echo "Error: ($1) occurred on $2"
}
main() {
echo "Inside main() function"
bad_command
second
third
exit $?
}
second() {
echo "After call to main()"
echo "Inside second() function"
}
third() {
echo "Inside third() function"
}
main
الجزء الأكبر من النص موجود داخل main
وظيفة، والتي تستدعي second
و third
الوظائف. عندما يتم مواجهة خطأ—في هذه الحالة، لأن bad_command
لا وجود له— trap
يوجه البيان الخطأ إلى error_handler
الوظيفة. تقوم بتمرير حالة الخروج من الأمر الفاشل ورقم السطر إلى error_handler
وظيفة.
./trap.sh
ملكنا error_handler
تسرد الوظيفة ببساطة تفاصيل الخطأ في نافذة المحطة الطرفية. إذا أردت، يمكنك إضافة exit
أمر إلى الوظيفة لإنهاء البرنامج النصي. أو يمكنك استخدام سلسلة من if/elif/fi
عبارات لأداء إجراءات مختلفة لأخطاء مختلفة.
قد يكون من الممكن معالجة بعض الأخطاء، في حين أن أخطاء أخرى قد تتطلب إيقاف البرنامج النصي.
نصيحة أخيرة
غالبًا ما يعني اكتشاف الأخطاء استباق الأمور التي قد تحدث خطأً، ووضع التعليمات البرمجية للتعامل مع هذه الاحتمالات في حالة حدوثها. هذا بالإضافة إلى التأكد من صحة تدفق التنفيذ والمنطق الداخلي للنص البرمجي.
إذا استخدمت هذا الأمر لتشغيل البرنامج النصي الخاص بك، فسوف يعرض لك Bash إخراج التتبع أثناء تنفيذ البرنامج النصي:
bash -x your-script.sh
يكتب Bash مخرجات التتبع في نافذة المحطة الطرفية. ويعرض كل أمر مع وسيطاته – إذا كان لديه أي وسيطات. ويحدث هذا بعد توسيع الأوامر ولكن قبل تنفيذها.
يمكن أن يكون بمثابة مساعدة هائلة في تعقب الأخطاء المراوغة.