كيفية اصطياد الأخطاء في نصوص Bash على Linux

بشكل افتراضي، سيبلغ البرنامج النصي Bash على Linux عن خطأ ولكنه سيستمر في العمل. سنوضح لك كيفية التعامل مع الأخطاء بنفسك حتى تتمكن من تحديد ما يجب أن يحدث بعد ذلك.




معالجة الأخطاء في البرامج النصية

إن التعامل مع الأخطاء يشكل جزءًا من البرمجة. فحتى إذا كتبت كودًا خاليًا من العيوب، فلا يزال من الممكن أن تواجه حالات خطأ. تتغير البيئة على جهاز الكمبيوتر الخاص بك بمرور الوقت، مع تثبيت البرامج وإلغاء تثبيتها، وإنشاء الدلائل، وإجراء الترقيات والتحديثات.

على سبيل المثال، قد يواجه البرنامج النصي الذي كان يعمل دون مشاكل صعوبات إذا تغيرت مسارات الدليل أو تغيرت الأذونات على ملف. الإجراء الافتراضي لـ Bash shell هو طباعة رسالة خطأ ومواصلة تنفيذ البرنامج النصي. وهذا خطأ افتراضي خطير.

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

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


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

اكتشاف حالة الخروج

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

يمكننا التحقق من حالة الخروج – المعروفة أيضًا باسم رمز الإرجاع – للأوامر التي يستخدمها البرنامج النصي، وتحديد ما إذا كان الأمر ناجحًا أم لا.

في Bash، يساوي الصفر القيمة true. إذا كانت استجابة الأمر مختلفة عن القيمة true، فإننا نعلم أن هناك مشكلة حدثت ويمكننا اتخاذ الإجراء المناسب.

انسخ هذا البرنامج النصي إلى محرر، ثم احفظه في ملف يسمى “bad_command.sh”.


#!/bin/bash 

if ( ! bad_command ); then

  echo "bad_command flagged an error."

  exit 1

fi

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

chmod +x bad_command.sh

جعل البرنامج النصي قابلاً للتنفيذ باستخدام chmod

عندما نقوم بتشغيل البرنامج النصي نرى رسالة الخطأ المتوقعة.

./bad_command.sh

التحقق من حالة خروج الأمر لتحديد ما إذا كان هناك خطأ


لا يوجد أمر مثل “bad_command”، ولا هو اسم وظيفة داخل البرنامج النصي. لا يمكن تنفيذه، لذا فإن الاستجابة ليست صفرًا. إذا لم تكن الاستجابة صفرًا، فسيتم استخدام علامة التعجب هنا كعلامة منطقية NOT المشغل—جسم if تم تنفيذ العبارة.

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

قد يبدو مثل exit 1 السطر زائد عن الحاجة. بعد كل شيء، لا يوجد شيء آخر في البرنامج النصي وسينتهي على أي حال. ولكن باستخدام exit يسمح لنا الأمر بتمرير حالة الخروج إلى shell. إذا تم استدعاء البرنامج النصي الخاص بنا من داخل برنامج نصي ثانٍ، فسوف يعرف هذا البرنامج النصي الثاني أن هذا البرنامج النصي واجه أخطاء.

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


command_1 || command_2

يعمل هذا لأنه يتم تشغيل الأمر الأول OR الأمر الثاني. يتم تنفيذ الأمر الموجود في أقصى اليسار أولاً. إذا نجح الأمر، فلن يتم تنفيذ الأمر الثاني. ولكن إذا فشل الأمر الأول، فسيتم تنفيذ الأمر الثاني. لذا يمكننا هيكلة الكود على هذا النحو. هذا هو “logical-or./sh”.

#!/bin/bash 

error_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 $?

استخدام عامل OR المنطقي لاستدعاء معالج الخطأ في البرنامج النصي

طفلنا الصغير 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.

ذات صلة: إصلاح: خطأ “مترجم سيئ: لا يوجد مثل هذا الملف أو الدليل” في Linux


للقبض أيضًا على المتغيرات غير المهيئة، أضف -u خيار (غير محدد). للتأكد من اكتشاف الأخطاء في التسلسلات المنسقة، أضف -o pipefail الخيار. بدون هذا، تكون حالة الخروج لتسلسل أوامر متسلسل هي حالة الخروج للأمر الأخير في التسلسل. لن يتم اكتشاف أمر فاشل في منتصف التسلسل المتسلسل. -o pipefail يجب أن يأتي الخيار في قائمة الخيارات.

التسلسل الذي يجب إضافته إلى أعلى النص الخاص بك هو:

set -Eeuo pipefail

فيما يلي نص برمجي قصير يسمى “unset-var.sh”، مع وجود متغير غير محدد بداخله.

#!/bin/bash 

set -Eeou pipefail

echo "$unset_variable"

echo "Do we see this line?"

عندما نقوم بتشغيل البرنامج النصي، يتم التعرف على unset_variable كمتغير غير مهيأ ويتم إنهاء البرنامج النصي.

./unset-var.sh

استخدام الأمر set في البرنامج النصي لإنهاء البرنامج النصي في حالة حدوث خطأ


الثاني echo الأمر لن يتم تنفيذه أبدًا.

استخدام المصيدة مع الأخطاء

يتيح لك أمر Bash trap ترشيح أمر أو وظيفة يجب استدعاؤها عند رفع إشارة معينة. عادةً ما يتم استخدام هذا لالتقاط إشارات مثل SIGINT الذي يظهر عند الضغط على تركيبة المفاتيح Ctrl+C. هذا النص هو “sigint.sh”.

#!/bin/bash 

trap "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 في البرنامج النصي للقبض على Ctrl+c


يمكننا أن نستخدم trap مع ERR إشارة لالتقاط الأخطاء عند حدوثها. ويمكن بعد ذلك إرسالها إلى أمر أو وظيفة. هذا هو “trap.sh”. نرسل إشعارات الخطأ إلى وظيفة تسمى error_handler.

#!/bin/bash 

trap '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


استخدام trap مع ERR لالتقاط الأخطاء في البرنامج النصي

ملكنا error_handler تسرد الوظيفة ببساطة تفاصيل الخطأ في نافذة المحطة الطرفية. إذا أردت، يمكنك إضافة exit أمر إلى الوظيفة لإنهاء البرنامج النصي. أو يمكنك استخدام سلسلة من if/elif/fi عبارات لأداء إجراءات مختلفة لأخطاء مختلفة.

قد يكون من الممكن معالجة بعض الأخطاء، في حين أن أخطاء أخرى قد تتطلب إيقاف البرنامج النصي.

نصيحة أخيرة

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

إذا استخدمت هذا الأمر لتشغيل البرنامج النصي الخاص بك، فسوف يعرض لك Bash إخراج التتبع أثناء تنفيذ البرنامج النصي:


bash -x your-script.sh

يكتب Bash مخرجات التتبع في نافذة المحطة الطرفية. ويعرض كل أمر مع وسيطاته – إذا كان لديه أي وسيطات. ويحدث هذا بعد توسيع الأوامر ولكن قبل تنفيذها.

يمكن أن يكون بمثابة مساعدة هائلة في تعقب الأخطاء المراوغة.

ذات صلة: كيفية التحقق من صحة بناء جملة نص برمجي Bash في Linux قبل تشغيله

أضف تعليق