تعلم كيفية استخدام كتل Try Catch في PowerShell مع الأمثلة

هل سبق لك أن قمت بتشغيل سكربت أو أمر PowerShell وواجهتك جدار من النص – باللون الأحمر – مثل الذي يظهر أدناه؟

Example of errors in PowerShell

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

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

في هذه المقالة ، ستتعرف على الأخطاء في PowerShell وكيف يمكن اعتراضها لأداء معالجة الأخطاء باستخدام كتل الأمر Try Catch (وكتل finally).

فهم كيفية عمل الأخطاء في PowerShell

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

النسق التلقائي $Error

في PowerShell ، هناك الكثير من المتغيرات التلقائية ، وأحدها هو المتغير التلقائي $Error. يستخدم PowerShell المتغير $Error لتخزين جميع الأخطاء التي يتم مواجهتها في الجلسة. المتغير $Error عبارة عن مصفوفة من الأخطاء مرتبة حسب الأحدث.

عند فتح جلسة PowerShell للمرة الأولى، المتغير $Error فارغ. يمكنك التحقق من ذلك عن طريق استدعاء المتغير $Error.

The $Error variable is empty

كما يمكنك أن ترى، يكون المتغير $Error فارغًا في البداية. ولكن، بمجرد حدوث خطأ، سيتم إضافة الخطأ وتخزينه في المتغير $Error.

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

PS> Get-Service xyz
PS> $Error
PS> $Error.Count
The error is added to the $Error variable

كما يمكنك رؤية في الناتج أعلاه، تمت إضافة الخطأ الذي تم إنشاؤه إلى المتغير $Error.

يحتوي المتغير $Error على مجموعة من الأخطاء التي تم إنشاؤها في جلسة PowerShell. يمكن الوصول إلى كل خطأ عن طريق استدعاء موقعه في مصفوفة. سيكون آخر خطأ دائمًا في المؤشر 0.

على سبيل المثال، يمكن استرجاع آخر خطأ باستخدام $Error[0].

خصائص كائن $Error

نظرًا لأن كل شيء في PowerShell هو كائن، المتغير $Error هو كائن، والكائنات لديها خصائص. عن طريق توجيه المتغير $Error إلى cmdlet Get-Member، يجب أن ترى قائمة الخصائص المتاحة.

$Error | Get-Member
The $Error object properties

لتحديد سبب الخطأ، يمكنك عرض محتوى خاصية InvocationInfo باستخدام الأمر أدناه.

$Error[0].InvocationInfo
The InvocationInfo property

الآن، يمكنك القيام بالشيء نفسه مع الخصائص الأخرى واكتشاف المعلومات الأخرى التي يمكنك العثور عليها!

أخطاء الإنهاء

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

كما ترى في اللقطة أدناه، عند تشغيل الأمر Get-Process notepad، الأمر صحيح، وتُعرض تفاصيل عملية notepad.

The notepad process details

ولكن، عند استخدام معلمة غير موجودة مثل Get-Process notepad -handle 251، يعرض cmdlet خطأ بأن معلمة handle غير صالحة. ثم، يخرج cmdlet دون عرض تفاصيل عملية notepad.

Error is thrown because the parameter is invalid

الأخطاء غير الفادحة

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

$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

محتويات ملف filelist.txt هي أسماء الملفات المعروضة في القائمة أدناه.

File_1.log
File_2.log
File_3.log
File_4.log
File_5.log
File_6.log
File_7.log
File_8.log
File_9.log
File_10.log

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

Example of non-terminating error

كما يمكنك رؤية من اللقطة المصغرة للنتيجة أعلاه، كان السكريبت قادرًا على قراءة أول خمس ملفات في القائمة، ولكن عندما حاول قراءة الملف File_6.txt, تم إرجاع خطأ. ثم واصل السكريبت قراءة بقية الملفات قبل الخروج. لم يقم بـ إنهاء.

المتغير $ErrorActionPreference

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

لـ PowerShell، هناك مفهوم يُسمى بالمتغيرات المرجعية. تُستخدم هذه المتغيرات لتغيير كيفية سلوك PowerShell بطرق متعددة. أحد هذه المتغيرات يُسمى $ErrorActionPreference.

يُستخدم المتغير $ErrorActionPreference لتغيير الطريقة التي يتعامل بها PowerShell مع الأخطاء التي لا تنتهي. بشكل افتراضي، يتم تعيين قيمة $ErrorActionPreference إلى Continue. تغيير قيمة المتغير $ErrorActionPreference إلى STOP يُجبر PowerShell على معالجة جميع الأخطاء كأخطاء تنتهي.

استخدم الكود أدناه لتغيير قيمة $ErrorActionPreference.

$ErrorActionPreference = "STOP"

لمعرفة المزيد حول قيم متغير $ErrorActionPreference الصحيحة الأخرى، قم بزيارة PowerShell ErrorActionPreference.

الآن، ارجع إلى المثال المستخدم في قسم أخطاء غير متوقفة في هذا المقال. يمكن تعديل السيناريو ليشمل التغيير في $ErrorActionPreference مثل الشيفرة المعروضة أدناه:

# تعيين قيمة $ErrorActionPreference إلى STOP
$ErrorActionPreference = "STOP"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

تشغيل الشيفرة المعدلة أعلاه سيتصرف بشكل مختلف عما كان عليه في السابق عندما تكون قيمة $ErrorActionPreference مضبوطة على القيمة الافتراضية Continue.

Forcing a terminating error using the $ErrorActionPreference variable

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

قيمة $ErrorActionPreference صالحة فقط في جلسة PowerShell الحالية. تُعيد ضبط القيمة إلى القيمة الافتراضية بمجرد بدء جلسة PowerShell جديدة.

المعلمة الشائعة ErrorAction

إذا تم تطبيق قيمة $ErrorActionPreference على جلسة PowerShell، فإن معلمة ErrorAction تنطبق على أي cmdlet يدعم المعلمات الشائعة. تقبل معلمة ErrorAction نفس القيم التي تقبلها المتغير $ErrorActionPreference.

القيمة لمعلمة ErrorAction تأخذ الأولوية على القيمة للمتغير $ErrorActionPreference.

لنعود ونستخدم نفس الشيفرة في المثال السابق. ولكن، في هذه المرة، سنضيف معلمة ErrorAction إلى سطر Get-Content.

# قم بتعيين قيمة $ErrorActionPreference إلى الافتراضي (CONTINUE)
$ErrorActionPreference = "CONTINUE"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
		# استخدم المعلمة الشائعة -ErrorAction
		Get-Content $file -ErrorAction STOP
}

بعد تشغيل الشيفرة المعدلة، سترى أن السكربت انتهى على الرغم من أن قيمة $ErrorActionPreference مضبوطة على Continue. انتهى السكربت لأن قيمة معلمة PowerShell ErrorAction في Get-Content مضبوطة على STOP.

Forcing a terminating error using the PowerShell ErrorAction parameter

استخدام كتل PowerShell Try Catch

في هذه المرحلة، لقد تعلمت عن أخطاء PowerShell وكيفية عمل المتغير $ErrorActionPreference ومعلمات PowerShell ErrorAction. الآن، حان الوقت لتتعلم عن الأمور الجيدة – كتل PowerShell Try Catch Finally.

كتل try catch في PowerShell (وكتل finally block الاختيارية) هي طريقة لصب شبكة حول قطعة من الكود والتقاط أي أخطاء تعود.

الكود أدناه يوضح بناء البيانات للبيان Try.

try {
    <statement list>
}
catch [[<error type>][',' <error type>]*]{
    <statement list>
}
finally {
    <statement list>
}

يحتوي كتلة Try على الكود الذي ترغب في أن يحاول PowerShell مراقبته للأخطاء. إذا واجه الكود في كتلة Try خطأً، يتم إضافة الخطأ إلى المتغير $Error ثم يتم تمريره إلى كتلة Catch.

تحتوي كتلة Catch على الإجراءات التي يجب تنفيذها عند استلامها لخطأ من كتلة Try. يمكن أن توجد عدة كتل Catch في بيان Try.

تحتوي كتلة Finally على الكود الذي سيتم تنفيذه في نهاية بيان Try. تعمل هذه الكتلة سواء حدث خطأ أم لم يحدث.

التقاط الأخطاء غير المحددة (Catch-All) بواسطة ErrorAction في PowerShell

A simple Try statement contains a Try and a Catch block. The Finally block is optional.

على سبيل المثال، لالتقاط استثناء غير محدد، يجب أن يكون المعامل Catch فارغًا. الكود المعروض أدناه يستخدم نفس النص البرمجي الذي تم استخدامه في متغير $ErrorActionPreference ولكن معدّل لاستخدام كتل Try Catch.

كما يمكن ملاحظة من الكود أدناه، في هذه المرة، تم تضمين foreach بيان داخل كتلة Try. ثم، تحتوي كتلة Catch على الكود لعرض السلسلة An Error Occurred إذا حدث خطأ. والكود في كتلة Finally يقوم فقط بمسح المتغير $Error.

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch {
    Write-Host "An Error Occured" -ForegroundColor RED
}
finally {
    $Error.Clear()
}

الكود أعلاه، بعد تشغيله في PowerShell، سيعطيك الناتج المعروض أدناه.

Script terminated when an error occurred

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

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

الكود أدناه تم تعديله، بالضبط الكود داخل كتلة Catch، لعرض رسالة الاستثناء من الخطأ الحالي الذي تم تمريره عبر الأنبوب – $PSItem.Exception.Message

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch {
    Write-Host $PSItem.Exception.Message -ForegroundColor RED
}
finally {
    $Error.Clear()
}

هذه المرة، عند تشغيل الكود المعدل أعلاه، تكون الرسالة المعروضة وصفًا أكثر تفصيلًا.

Script terminated with a descriptive error message

التقاط أخطاء محددة

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

كيف يمكنك تحديد نوع الخطأ؟ من خلال فحص قيمة TypeName لخاصية Exception لأخر خطأ. على سبيل المثال، للعثور على نوع الخطأ من المثال السابق، استخدم هذا الأمر:

$Error[0].Exception | Get-Member

نتيجة الكود أعلاه ستبدو كما في اللقطة أدناه. كما ترى، تم عرض قيمة TypeNameSystem.Management.Automation.ItemNotFoundException.

Getting the error TypeName value

الآن بمجرد أن تعرف نوع الخطأ الذي تحتاج إلى اعتراضه، قم بتعديل الكود لالتقاطه بشكل محدد. كما ترى من الكود المعدل أدناه، هناك الآن اثنين من كتل Catch. تعترض الكتلة الأولى Catch نوع محدد من الخطأ (System.Management.Automation.ItemNotFoundException). بينما تحتوي الكتلة الثانية Catch على رسالة خطأ عامة.

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch [System.Management.Automation.ItemNotFoundException]{
    Write-Host "The file $file is not found." -ForegroundColor RED
}
catch {
    Write-Host $PSItem.Exception.Message -ForegroundColor RED
}
finally {
    $Error.Clear()
}

الصورة أدناه تظهر إخراج الكود المعدل أعلاه.

Script terminated with a specific error message

الاستنتاج

في هذا المقال، لقد تعلمت عن الأخطاء في PowerShell، وخصائصها، وكيف يمكنك تحديد نوع محدد للخطأ. كما تعلمت الفرق بين كيفية تأثير المتغير $ErrorActionPreference ومعامل ErrorAction في PowerShell على كيفية معالجة PowerShell للأخطاء غير المنتهية.

لقد تعلمت أيضًا كيفية استخدام كتل Try Catch Finally في PowerShell لأداء معالجة الأخطاء، سواء للأخطاء المحددة أو بشكل عام.

الأمثلة التي تُعرض في هذا المقال توضح فقط أساسيات كيفية عمل كتل Try Catch Finally. المعرفة التي آمل أن تكون قد اكتسبتها في هذا المقال يجب أن تمنحك البداية لتبدأ في تطبيق معالجة الأخطاء في نصوصك.

قراءة إضافية

Source:
https://adamtheautomator.com/powershell-try-catch/