التحقق من الطاقة: استكمال التبويب وقيم المعلمة

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

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

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

ملخص موجز لسمة PowerShell ValidateSet

ValidateSet هو سمة المعلمة التي تتيح لك تعريف مجموعة من العناصر التي يتم قبولها فقط كقيمة لتلك المعلمة.

على سبيل المثال ، ربما لديك سيناريو محدد للعمل مع مراقبي نطاق Active Directory. يحتوي هذا السيناريو على معلمة تحدد اسم مراقب النطاق. هل لا يكون من المنطقي تحديد قائمة القيم المقبولة لأسماء المراقبين الفعليين للنطاق؟ ليس هناك سبب للسماح للمستخدم باستخدام “foobar” كقيمة عندما تعرف مسبقًا ما هي القيم التي يحتاجها السيناريو. يوفر لك ValidateSet تلك القدرة.

المتطلبات

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

  • محرر الشفرة المرئية أو أي محرر شفرة آخر. سأستخدم محرر الشفرة المرئية.
  • على الأقل PowerShell 5.1 لمعظم الشفرة في هذا المقال. هناك قسم واحد يتطلب PowerShell 6.1 أو الأحدث وسأحدده عندما نصل إليه

جميع الشفرة في هذا المقال تم اختبارها في البيئات التالية:

Operating System PowerShell Versions
Windows 7 SP1 5.1, Core 6.2
Windows 10 1903 5.1, Core 6.2
Linux Mint 19.2 Core 6.2

لمساعدتك في شرح المفاهيم المتعلقة بـ ValidateSet ستقوم ببناء نص صغير يسمى Get-PlanetSize.ps1. هذا النص يعيد معلومات عن أحجام الكواكب في نظامنا الشمسي.

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

البدء

للبدء، انسخ والصق الشفرة PowerShell أدناه في محرر النص المفضل لديك واحفظه باسم Get-PlanetSize.ps1.

$planets = [ordered]@{
     'Mercury' = 4879
     'Venus'   = 12104
     'Earth'   = 12756
     'Mars'    = 6805
     'Jupiter' = 142984
     'Saturn'  = 120536
     'Uranus'  = 51118
     'Neptune' = 49528
     'Pluto'   = 2306
 }
 $planets.keys | Foreach-Object {
     $output = "The diameter of planet {0} is {1} km" -f $_, $planets[$_]
     Write-Output $output
 }

قم بتشغيل النص من سطر الأوامر PowerShell ويجب أن تحصل على ما يلي:

PS51> .\Get-PlanetSize.ps1
 The diameter of planet Mercury is 4879 km
 The diameter of planet Venus is 12104 km
 The diameter of planet Earth is 12756 km
 The diameter of planet Mars is 6805 km
 The diameter of planet Jupiter is 142984 km
 The diameter of planet Saturn is 120536 km
 The diameter of planet Uranus is 51118 km
 The diameter of planet Neptune is 49528 km
 The diameter of planet Pluto is 2306 km

مفيد ولكن ليس مرنًا للغاية؛ يتم إعادة المعلومات لكل كوكب، حتى إذا كنت تريد المعلومات فقط للمريخ.

ربما ترغب في القدرة على تحديد كوكب واحد بدلاً من إرجاع كلها. يمكنك القيام بذلك عن طريق إدخال معلمة. دعونا نلقي نظرة على كيفية تحقيق ذلك فيما يلي.

قبول الإدخال باستخدام معلمة

للسماح للنص بقبول معلمة ، أضف كتلة Param() إلى أعلى النص. اسم العلامة هو Planet. تظهر كتلة Param() المناسبة كما يلي.

في المثال أدناه ، تضمن السطر [Parameter(Mandatory)] دائمًا توفير اسم كوكب للنص. إذا كان الاسم مفقودًا ، سيطلب النص واحد.

Param(
     [Parameter(Mandatory)]
     $Planet
 )

أبسط طريقة لدمج هذه المعلمة Planet في النص هي تغيير السطر $planets.keys | Foreach-Object { إلى $Planet | Foreach-Object {. الآن لا تعتمد على الجدول الديناميكي المعرف سابقًا ولكن بدلاً من ذلك تقرأ قيمة معلمة Planet.

الآن إذا قمت بتشغيل النص وحددت كوكبًا باستخدام معلمة Planet ، سترى معلومات حول ذلك الكوكب الخاص بك.

PS51> .\Get-PlanetSize.ps1 -Planet Mars
 The diameter of planet Mars is 6805 km

ممتاز. هل اكتمل النص؟ ربما لا.

الخيارات مفتوحة جدًا

ماذا يحدث إذا حاولت العثور على قطر الكوكب Barsoom باستخدام Get-PlanetSize.ps1؟

PS51> .\Get-PlanetSize.ps1 -Planet Barsoom
The diameter of planet Barsoom is  km

همم، هذا غير صحيح. Barsoom ليس على قائمة الكواكب ، ولكن النص يعمل على أي حال. كيف يمكننا إصلاح هذا؟

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

ضمان استخدام قيم معينة فقط

A ValidateSet list is a comma-separated list of string values, wrapped in single or double-quotes. Adding a ValidateSet attribute to a script or function parameter consists of adding a line of text to the Param() block, as shown below. Replace the Param() block in your copy of Get-PlanetSize.ps1 with the one below and save the file.

Param(
     [Parameter(Mandatory)]
     [ValidateSet("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto")]
     $Planet
 )

جرب تشغيل السكربت مرة أخرى باستخدام Barsoom كمعلمة Planet. الآن يتم إرجاع رسالة خطأ مفيدة. الرسالة محددة في ما الذي حدث خطأ وتوفر حتى قائمة من القيم الممكنة للمعلمة.

PS51> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. The argument "Barsoom" does not belong to the set
 "Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 

جعل PowerShell تفحص ValidateSet حساسية لحالة الأحرف

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

الخيار IgnoreCase في ValidateSet، سمة التحقق، تحدد ما إذا كانت القيم الموردة للمعلمة تتطابق بالضبط مع قائمة القيم الصالحة. بشكل افتراضي، يتم تعيين IgnoreCase إلى $True (تجاهل الحالة). إذا قمت بتعيينه إلى $False، فإن توريد mars كقيمة لمعلمة Planet لـ Get-PlanetSize.ps1 سيولد رسالة خطأ.

تستخدم الخيار IgnoreCase عن طريق تعيين قيمة $true له في نهاية القائمة من القيم الصالحة كما هو موضح أدناه.

[ValidateSet("Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", IgnoreCase = $false)]  

الآن عندما تحاول استخدام قيمة لـ Planet التي ليست بالضبط مثل القيمة في القائمة، ستفشل عملية التحقق.

استخدام الاكمال التلقائي بالتبويب

ميزة أخرى لاستخدام ValidateSet هي أنه يمنحك الاكمال التلقائي بالتبويب. هذا يعني أنه يمكنك التنقل خلال القيم الممكنة لمعلمة باستخدام مفتاح TAB. هذا يحسن بشكل كبير من قابلية استخدام نص أو وظيفة، خاصةً من الوحدة التحكم.

في الأمثلة أدناه، هناك بعض الأشياء التي يجب ملاحظتها:

  • يعيد الاكمال بالتبويب إلى القيمة الأولى بعد عرض الأخيرة.
  • تُعرض القيم بترتيب أبجدي، على الرغم من أنها ليست مُدرجة في ValidateSet بترتيب أبجدي.
  • كتابة الحرف الأول والضغط على TAB يقيّد القيم المُعرضة بالاكمال التلقائي بالتبويب إلى تلك التي تبدأ بهذا الحرف.
Cycling through parameter values using Tab Completion
Restricting values returned by Tab Completion

يمكنك أيضًا الاستفادة من اكمال التلقائي بالتبويب في بيئة كتابة النصوص المتكاملة لـ PowerShell (ISE)، كما هو موضح في المثال أدناه. تُظهر ميزة Intellisense في ISE لك قائمة القيم الممكنة في صندوق اختيار جميل.

تُعيد Intellisense جميع القيم التي تحتوي على الحرف الذي تكتبه، بدلاً من تلك التي تبدأ به.

Tab Completion and Intellisense in ISE

الآن بعد أن غطينا سمات التحقق ValidateSet كما هي في Windows 5.1، دعونا نلقي نظرة على ما تم إضافته في PowerShell Core 6.1 ونرى ما إذا كان ذلك يمكن أن يعطي نصنا المزيد من إمكانيات التحقق.

فهم التغييرات على ValidateSet في PowerShell 6.1

بمجرد وصول PowerShell Core 6.1، تمت إضافة قدرات جديدة إلى سمات التحقق ValidateSet:

  • خاصية ErrorMessage
  • استخدام الفئات في ValidateSet من خلال الوصول إلى System.Management.Automation.IValidateSetValuesGenerator

خاصية ErrorMessage

الرسالة الخطأ الافتراضية التي تظهر عند توفير اسم كوكب غير صحيح لـ Get-PlanetSize.ps1 مفيدة، ولكنها قليلة الوضوح:

PS61> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. The argument "Barsoom" does not belong to the set
 "Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 

استخدم خاصية ErrorMessage لسمة التحقق ValidateSet لتعيين رسالة خطأ مختلفة، كما هو موضح في المثال أدناه. يتم استبدال {0} تلقائيًا بالقيمة المقدمة، ويتم استبدال {1} تلقائيًا بقائمة القيم المسموح بها.

Param(
     [Parameter(Mandatory)]
     [ValidateSet("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto",ErrorMessage="Value '{0}' is invalid. Try one of: '{1}'")]
     $Planet
 )

استبدل كتلة Param() في ملف النص البرمجي واحفظه. ثم حاول Get-PlanetSize.ps1 -Planet Barsoom مرة أخرى. لاحظ أدناه أن الخطأ أقل كلامًا وأكثر وضوحًا.

PS61> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. Value 'Barsoom' is invalid. Try one of: 'Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto'
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 

بعد ذلك، تحقق من طريقة جديدة لتحديد القيم المقبولة في ValidateSet عبر PowerShell class.

فئات PowerShell

الأنواع المخصصة، المعروفة في PowerShell بأسماء الفئات، كانت متاحة منذ الإصدار 5. مع وصول PowerShell Core 6.1، هناك ميزة جديدة تسمح باستخدام فئة لتوفير القيم لـ ValidateSet.

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

الميزة الجديدة التي تعمل مع ValidateSet هي القدرة على استخدام فئة System.Management.Automation.IValidateSetValuesGenerator. يمكننا استخدام هذا كقاعدة لفئاتنا الخاصة باستخدام التوريث. للعمل مع ValidateSet، يجب أن تستند الفئة إلى System.Management.Automation.IValidateSetValuesGenerator ويجب أن تنفذ طريقة تسمى GetValidValues().

الطريقة GetValues() تُرجع قائمة القيم التي ترغب في قبولها. كتلة Param() مع القائمة الثابتة للكواكب التي تم استبدالها بفئة [Planet] ستبدو كما يلي. لن يعمل هذا المثال بعد. استمر في القراءة لتتعلم كيفية تنفيذ ذلك.

Param(
     [Parameter(Mandator)]
     [ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
     $Planet
 )

استخدام فئة لقائمة قيم ValidateSet: مثال حقيقي

لتوضيح استخدام فئة لقائمة قيمة ValidateSet، ستقوم بتعويض القائمة الثابتة للكواكب المستخدمة سابقًا بقائمة تم تحميلها من ملف نصي CSV. لن تحتاج بعد الآن إلى الحفاظ على قائمة ثابتة من القيم داخل النص نفسه!

إنشاء مصدر بيانات للفئة

أولاً، ستحتاج إلى إنشاء ملف CSV يحتوي على كل القيم الصالحة. للقيام بذلك، قم بنسخ ولصق هذه المجموعة من البيانات في ملف نصي جديد واحفظه كملف نصي بالاسم planets.csv في نفس المجلد الذي يحتوي على النص البرمجي Get-PlanetSize.ps1.

Planet,Diameter
 "Mercury","4879"
 "Venus","12104"
 "Earth","12756"
 "Mars","6805"
 "Jupiter","142984"
 "Saturn","120536"
 "Uranus","51118"
 "Neptune","49528"
 "Pluto","2306"

إضافة الفئة إلى النصي

أي فئة تُستخدم بواسطة ValidateSet يجب أن تكون قد تم تعريفها بالفعل قبل أن يحاول ValidateSet استخدامها. هذا يعني أن هيكل Get-PlanetSize.ps1 كما هو لن يعمل.

يجب تعريف الفئة [Planet] قبل أن يمكن استخدامها، لذلك يجب أن توضع في بداية النصي. تبدو تعريف الفئة الخاصة بالهيكل مناسبة على النحو التالي:

class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
 }
 }

داخل الطريقة GetValidValues() للفئة، استخدم cmdlet Import-CSV لاستيراد ملف النص planets.csv الذي تم إنشاؤه مسبقًا. يتم استيراد الملف إلى متغير بنطاق عالمي يُسمى $planets، لذا يمكن الوصول إليه لاحقًا في النصي.

استخدم البيان return لإرجاع قائمة أسماء الكواكب عبر GetValidValues(). بعد هذه التغييرات، يجب أن تبدو الفئة الآن كما هو موضح أدناه.

class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
             $Global:planets = Import-CSV -Path planets.csv
             return ($Global:planets).Planet
     }
 }

بعد ذلك، احذف تعريف الجدول المتجانس $planets من النصي كما هو موضح أدناه. المتغير العالمي $planets الذي يتم ملؤه بواسطة الفئة [Planet] يحتوي على بيانات الكواكب بدلاً من ذلك.

$planets = [ordered]@{
     'Mercury' = 4879
     'Venus'   = 12104
     'Earth'   = 12756
     'Mars'    = 6805
     'Jupiter' = 142984
     'Saturn'  = 120536
     'Uranus'  = 51118
     'Neptune' = 49528
     'Pluto'   = 2306
 }

الآن قم بلف الكود الأصلي المتبقي في وظيفة وسميه Get-PlanetDiameter للتمييز عن اسم النصي. ضع كتلة Param() التي كانت في بداية النصي داخل الوظيفة. استبدل القائمة الثابتة للكواكب بإشارة إلى الفئة [Planet] كما هو موضح أدناه.

[ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]`

استبدل السطر $output = "The diameter of planet {0} is {1} km" -f $_, $planets[$_] بالسطرين التاليين. تسمح هذه السطور للنص بالبحث عن كوكب في مجموعة الكائنات التي تم إنشاؤها بواسطة Import-CSV، بدلاً من الجدول الهاش الذي قمت بإنشائه سابقًا والذي قمت بإزالته من النص:

$targetplanet = $planets | Where -Property Planet -match $_
 $output = "The diameter of planet {0} is {1} km" -f $targetplanet.Planet, $targetplanet.Diameter

بعد هذه التغييرات، يجب أن يبدو النص النهائي كما يلي:

class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
         $Global:planets = Import-CSV -Path planets.csv
         return ($Global:planets).Planet
     }
 }
 Function Get-PlanetDiameter {
     Param(
         [Parameter(Mandatory)]
         [ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
         $Planet
     )
     $Planet | Foreach-Object {
         $targetplanet = $planets | Where -Property Planet -match $_
         $output = "The diameter of planet {0} is {1} km" -f $targetplanet.Planet, $targetplanet.Diameter
         Write-Output $output
     }
 }

تذكر، من هذه النقطة فصاعدًا، يعمل النص فقط في PowerShell 6.1 أو الإصدارات الأحدث

الآن، كيفية استخدام هذا النص؟

تشغيل النص

لا يمكنك استخدام الإصدار الجديد من النص مباشرةً عن طريق تنفيذ النص. جميع الشفرة المفيدة مغلفة الآن في وظيفة. تحتاج إلى تحديد مصدر النص بدلاً من ذلك للسماح بالوصول إلى الوظيفة من داخل جلسة PowerShell.

PS61> . .\Get-PlanetSize.ps1

بعد تحديد مصدر النص، لديك الآن الوصول إلى الوظيفة الجديدة Get-PlanetDiameter في جلسة PowerShell، مع استكمال علامات التبويب.

ما هو الفائدة من كل هذا العمل؟” أسمعك تسأل. “يبدو أن النص يعمل بنفس الطريقة، ولكن من الأصعب استخدام الشفرة!”

جرب هذا:

  • افتح ملف planets.csv الذي قمت بإنشائه سابقًا.
  • أضف صفًا جديدًا بـ اسم وقطر جديد.
  • احفظ ملف CSV.

في نفس الجلسة التي قمت فيها بتضمين النص الخاص بالسيناريو الخاص بك أصليًا، حاول البحث عن القطر الخاص بالكوكب الجديد باستخدام Get-PlanetDiameter. إنه يعمل!

باستخدام فئة بهذه الطريقة يمنحنا العديد من الفوائد:

  • قائمة القيم الصحيحة الآن مفصولة عن الكود نفسه، ولكن أي تغييرات على القيم في الملف يتم استقبالها من قبل السيناريو.
  • يمكن صيانة الملف من قبل شخص لا يتصل بالسيناريو أبدًا.
  • A more complex script could look up information from a spreadsheet, database, Active Directory or a web API.

كما يمكنك رؤية، الإمكانيات تقريبًا لا تنتهي عند استخدام فئة لتوفير قيم ValidateSet.

في الختام

لقد غطينا الكثير من المواضيع بينما بنينا Get-PlanetSize.ps1، لذا دعنا نلخص.

في هذه المقالة تعلمت:

  • ما هو سمة التحقق ValidateSet ولماذا قد ترغب في استخدامها
  • كيفية إضافة ValidateSet إلى دالة PowerShell أو سيناريو
  • كيف يعمل استكمال التبويب مع ValidateSet
  • كيفية استخدام خاصية IgnoreCase للتحكم في ما إذا كانت ValidateSet حساسة للحالة
  • كيفية استخدام خاصية ErrorMessage مع ValidateSet الخاص بك و PowerShell 6.1
  • كيفية استخدام فئة لجعل ValidateSet ديناميكيًا مع PowerShell 6.1

ماذا تنتظر؟ ابدأ استخدام ValidateSet اليوم!

قراءة إضافية

Source:
https://adamtheautomator.com/powershell-validateset/