PowerShell ValidateSet: השלמת טאב וערכי פרמטר

כאשר אתה כותב סקריפט או פונקציה ב-PowerShell, לעיתים תרצה לקבל קלט מהמשתמש דרך פרמטרים. אם אין לך הגבלה על הערכים שהפרמטרים יכולים לקבל, אין אפשרות לוודא שלא יתרחשו מצבים בהם ייספקו ערכים לא ראויים. במאמר זה, למד כיצד להשתמש במאפיין האימות של פרמטר ב-PowerShell, ValidateSet, כדי להגביל את הערכים שניתן לספק לערכים שאתה מגדיר.

בעת כתיבת סקריפט או פונקציה ב-PowerShell, ניתן להשתמש במגוון מאפייני אימות שונים כדי לבדוק שהערכים שנספקים לך כפרמטרים הם נתונים ולהתריע למשתמש אם לא.

מאמר זה מתמקד במאפיין האימות ValidateSet. תלמד מהו ValidateSet, למה כדאי להשתמש בו בקוד שלך וכיצד לעשות זאת. תלמד גם על יכולת השלמה אוטומטית שמופעלת על ידי ValidateSet ושתעזור למשתמשים של קוד שלך לספק ערכי פרמטר חוקיים.

PowerShell ValidateSet: מבט קצר

ValidateSet הוא תכונת פרמטר שמאפשרת לך להגדיר קבוצה של אלמנטים שניתן לקבל רק כערכים לפרמטר זה.

לדוגמה, אולי יש לך סקריפט שמוגדר לעבוד עם שלטי תחום של Active Directory. לסקריפט זה יש פרמטר שמציין את שם שלט התחום. האם לא יהיה נכון להגביל את רשימת הערכים המקובלים לשמות הממשקים המסוימים של שלטי התחום? אין סיבה שהמשתמש יוכל להשתמש בערך כמו "foobar" כאשר יש לך מראש את הערכים שהסקריפט זקוק להם. ValidateSet נותן לך את היכולת לעשות זאת.

דרישות

מאמר זה יהיה הליכי למידה. אם אתה מתכנן לעקוב אחריו, תצטרך את הבאים:

כל הקוד במאמר זה עבר בדיקה בסביבות הבאות:

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. סקריפט זה מחזיר מידע על גדלי הכוכבים במערכת השמש שלנו.

תתחיל בסקריפט פשוט ותשפר את יכולתו לטפל בקלט מהמשתמש הסופי ולהפוך את גילוי הערכים האפשריים של הפרמטר לקל עבורם.

להתחלה

כדי להתחיל, העתק והדבק את קוד הפאוורשל למטה לעורך הטקסט האהוב עליך ושמור אותו בשם 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
 }

הרץ את הסקריפט משורת הפאוורשל ותקבל את הבא:

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

מצויין. התסריט הושלם? אולי לא.

האפשרויות פתוחות מידי

מה קורה אם תנסה למצוא את הקוטר של הכוכב ברסום באמצעות Get-PlanetSize.ps1?

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

המפתח, זה לא נכון. ברסום אינו ברשימת הכוכבים, אך התסריט רץ בכל זאת. איך נוכל לתקן זאת?

הבעיה כאן היא שהסקריפט מקבל קלט כלשהו ומשתמש בו, ללא תלות בכך האם זה ערך תקף או לא. הסקריפט צריך דרך להגביל אילו ערכים מקובלים עבור פרמטר ה־`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 

הגדרת ValidateSet של PowerShell כתלוית אותיות גדולות וקטנות

ברירת המחדל, המאפיין 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

בנוסף, תוכל גם להשתמש בהשלמת טאב של ValidateSet בסביבת הפיתוח המשולבת של 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 

באמצעות PowerShell class חדשה ניתן להגדיר את הערכים המתקבלים ב-ValidateSet בדרך חדשה.

כיתות 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 או גרסה מאוחרת יותר

כיצד להשתמש בתסריט זה?

הרצת התסריט

לא ניתן להשתמש בגרסה החדשה של התסריט ישירות על ידי הרצת התסריט. כל הקוד השימושי כעת מעוטר בפונקציה. עליך להשתמש ב- פונקציית dot source במקום כדי לאפשר גישה לפונקציה מתוך ההפעלה של PowerShell.

PS61> . .\Get-PlanetSize.ps1

לאחר שהופעל באמצעות dot source, יש לך כעת גישה לפונקציה החדשה Get-PlanetDiameter בהפעלת PowerShell, עם השלמה בלחיצת כפתור TAB.

מה היתרון של כל העבודה הזו?”, אני שומע אותך שואל. “נראה שהתסריט עובד באותו אופן, אבל זה יותר קשה להשתמש בקוד!”

נסה את זה:

  • פתח את קובץ ה- 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/