היכרות עם קו הפקודה של PowerShell ויצירת פונקציות

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

הצינור של PowerShell מאפשר לכם לשרשר יחד פקודות כדי לבנות 'צינור' אחד שמפשט קוד, מאפשר עיבוד מקבילי ועוד. אם אתם מוכנים ללמוד על הצינור ולבנות פונקציות משלכם כדי להשתמש בצינור, בואו נתחיל!

דרישות מוקדמות

הפוסט הזה יהיה מדריך ויכלול הדגמות מעשיות. אם ברצונכם לעקוב, יהיה עליכם להשתמש ב- PowerShell v3+. במדריך זה נעשה שימוש ב- Windows PowerShell v5.1.

הבנת הצינור ב- PowerShell

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

פקודות בצינור פועלות כמו רצים אנושיים במכירת השתייה. כל רץ במרוץ, למעט הראשון והאחרון, מקבל את המקל (אובייקטים) מהקודם ומעביר אותו לבא.

לדוגמה, ל-Stop-Service cmdlet יש פרמטר בשם InputObject. פרמטר זה מאפשר לך להעביר סוג מסוים של אובייקט אל Stop-Service המייצג את השירות של Windows שתרצה לעצור.

כדי להשתמש בפרמטר InputObject, ניתן לאחזר את אובייקט השירות באמצעות Get-Service ולאחר מכן להעביר את האובייקט לפרמטר InputObject כפי שמוצג למטה. שיטה זו לספק קלט ל-Stop-Service cmdlet דרך הפרמטר InputObject עובדת מצוין ומבצעת את המשימה.

$service = Get-Service -Name 'wuauserv'
Stop-Service -InputObject $service

שיטה זו להעברת קלט לפקודת Stop-Service דורשת שני שלבים נפרדים. PowerShell חייב להפעיל Get-Service תחילה, לשמור את הפלט במשתנה ואז להעביר את הערך הזה ל-Stop-Service דרך הפרמטר InputObject.

כעת, נצמצם את קטע הקוד לעיל עם קטע הקוד למטה, המבצע את אותה הדבר. זה קל יותר משום שאין צורך ליצור משתנה $services או אפילו להשתמש בפרמטר InputObject בכלל. במקום זאת, PowerShell "יודע" שתכוונת להשתמש בפרמטר InputObject. הוא עושה זאת דרך מושג שנקרא קישור פרמטרים.

עכשיו, חיברת פקודות ביחד עם האופרטור |. יצרת צינור.

Get-Service -Name 'wuauserv' | Stop-Service

אך, אין צורך להשתמש רק בשתי פקודות כדי ליצור צינור; ניתן לרצוף כמה שתרצה (אם הפרמטרים של הפקודות תומכים בזה). לדוגמה, קטע הקוד למטה:

  1. עובר על כל האובייקטים שהפקודה Get-Service מחזירה לפקודת Where-Object.
  2. פקודת Where-Object מביטה במאפיין Status של כל אובייקט ומחזירה רק את האובייקטים שיש להם ערך Running.
  3. לאחר מכן, כל אחד מהאובייקטים האלה נשלח אל Select-Object, שמחזיר רק את מאפייני Name ו־DisplayName של האובייקטים.
  4. מאחר שאין פקודה אחרת שמקבלת את האובייקטים ש־Select-Object מחזירה, הפקודה מחזירה את האובייקטים ישירות לקונסולה.

פקודות Where-Object ו־Select-Object מבינות כיצד לעבד את קלט הפייפליין על ידי מושג הקרוס פרמטר בינדינג, המוסבר בקטע הבא.

Get-Service | Where-Object Status -eq Running | Select-Object Name, DisplayName

למידע נוסף על פייפליינים, הפעל את הפקודה Get-Help about_pipelines.

קישור הפרמטרים בפייפליין

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

המשימה של גילוי אילו פרמטרים להשתמש כאשר פקודה מקבלת קלט דרך הצינור נכונה כבינת הפרמטרים. כדי לקשור בהצלחה אובייקט הנכנס מהצינור לפרמטר, פרמטר(י) הפקודה הנכנסת חייבים לתמוך בכך. פרמטרים של פקודה תומכים בקישור פרמטר בצינור באחד משני הדרכים הבאות; ByValue ו/או ByPropertyName:

ByValue

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

הפקודה Get-ChildItem כוללת פרמטר בשם Path המקבל אובייקט מסוג מחרוזת וקלט צינור דרך ByValue. בגלל זה, בהרצת משהו כמו 'C:\Windows' | Get-ChildItem מחזיר את כל הקבצים בתיקיית C:\Windows מכיוון ש- C:\Windows הוא מחרוזת.

ByPropertyName

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

המדבר ה-Get-Process יש לו פקודת Name המוכנה לקבל קלט מצינור בפרמטר ByPropertyName. כאשר אתה מעביר אובייקט עם מאפיין Name לפקודת Get-Process כמו בדוגמא [pscustomobject]@{Name='firefox'} | Get-Process, PowerShell מתאים או מקשר את מאפיין Name של האובייקט הנכנס לפרמטר Name ומשתמש בערך זה.

גילוי פרמטרים של פקודה התומכת בקלט מצינור

כפי שאומר למעלה, לא כל פקודה תומכת בקלט מצינור. יוצר הפקודה חייב ליצור פונקציונליות זו בפיתוח. הפקודה חייבת לכלול לפחות פרמטר אחד התומך בקלט מצינור, המגדיר ByValue או ByPropertyName.

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

Get-Help <COMMAND> -Parameter <PARAMETER>

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

PowerShell pipeline input is allowed

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

# קריאה לא דרך צינור
Get-ChildItem -Path 'C:\\Program Files', 'C:\\Windows'

# קריאה דרך צינור
'C:\\Program Files', 'C:\\Windows' | Get-ChildItem

אך, מצד שני, הפרמטר DisplayName בGet-Service לא תומך בקלט מצינור.

PowerShell pipeline input is not allowed

בניית פונקציית צינור משלך

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

להדגמה, בואו נתחיל עם פונקציה קיימת בשם Get-ConnectionStatus.

  • לפונקציה זו יש פרמטר יחיד (שלא מקבל קלט מצינור) בשם ComputerName, שמאפשר לך להעביר לו מחרוזת אחת או יותר. אתה יכול לדעת שהפרמטר ComputerName לא מקבל קלט מצינור מכיוון שהוא לא מוגדר כתכונת פרמטר ([Parameter()]).
  • אז הפונקציה קוראת כל אחת מהמחרוזות הללו ומריצה את הפקודת Test-Connection נגד כל אחת מהן.
  • עבור כל מחרוזת ששולחים כשם המחשב, היא מחזירה אובייקט עם מאפיינים של ComputerName ו־Status.
function Get-ConnectionStatus
{
    [CmdletBinding()]
    param
    (
        [Parameter()] ## אין קלט מצינור
        [string[]]$ComputerName
    )

    foreach($c in $ComputerName)
    {
        if(Test-Connection -ComputerName $c -Quiet -Count 1)
        {
            $status = 'Ok'
        }
        else
        {
            $status = 'No Connection'
        }

        [pscustomobject]@{
            ComputerName = $c
            Status = $status
        }
    }
}

אז עליך לקרוא לפונקציה Get-ConnectionStatus ולמסור לה את הפרמטר ComputerName עם שמות מחשב או כתובות IP אחת או יותר, כמו שמוצג למטה.

Get-ConnectionStatus -ComputerName '127.0.0.1', '192.168.1.100'

שלב 1: הרשאת קלט מצינור

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

לדוגמה זו, הוסף את התכונה ValueFromPipeline בתוך הסוגריים של ההגדרה של [Parameter()], כפי שמוצג למטה.

 [Parameter(ValueFromPipeline)]
 [string[]]$ComputerName

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

שלב 2: הוספת בלוק עיבוד

כאשר ברצונך ש־PowerShell יעבוד עם כל האובייקטים שנשלחים מהצינור, עליך להוסיף בלוק Process. בלוק זה אומר ל־PowerShell ל־עבד כל אובייקט הנשלח מהצינור.

בלעדיו של בלוק Process, PowerShell יעבד רק את האובייקט הראשון הנשלח מהצינור. בלוק Process אומר ל־PowerShell להמשיך לעבד אובייקטים.

הוסף בלוק Process כמצויין מטה על ידי לאחוז את כל הפונקציונליות של הפונקציה.

function Get-ConnectionStatus
{
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline)]
        [string[]]$ComputerName
    )

    Process ## בלוק Process חדש
    {
        foreach($c in $ComputerName)
        {
            if(Test-Connection -ComputerName $c -Quiet -Count 1)
            {
                $status = 'Ok'
            }
            else
            {
                $status = 'No Connection'
            }

            [pscustomobject]@{
                ComputerName = $c
                Status = $status
            }
        }
    } ## סיום בלוק Process
}

תמיד שלח את הפלט מבתוך הבלוק Process. שליחת הפלט מבתוך הבלוק Process "משדרת" את האובייקטים לפייפליין ומאפשרת לפקודות אחרות לקבל את האובייקטים האלה מהפייפליין.

מעבר אובייקטים לפייפליין של PowerShell

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

Get-ConnectionStatus -ComputerName '127.0.0.1', '192.168.1.100'
## או
'127.0.0.1', '192.168.1.100' | Get-ConnectionStatus

בנקודה זו, תוכל לנצל את הכוח האמיתי של הפייפליין ולהתחיל לכלול פקודות נוספות במערכת. לדוגמה, אולי יש לך קובץ טקסט, C:\Test\computers.txt, עם שורה של כתובות IP מופרדות באמצעות שורה חדשה כמו שמוצג למטה.

127.0.0.1
192.168.1.100

אז תוכל להשתמש בפקודת ה- Get-Content כדי לקרוא כל אחת מאותן כתובות IP בקובץ הטקסט ולהעביר אותן ישירות לפונקציית Get-ConnectionStatus.

Get-Content -Path C:\Test\computers.txt | Get-ConnectionStatus 

לקחת את ההגדרה הזו צעד אחד קדימה, תוכל לשלוף את האובייקטים ש- Get-ConnectionStatus מחזירה ישירות לפקודת ה- ForEach-Object.

הקוד הבא:

  • קורא את כל שמות המחשבים בקובץ הטקסט ומעביר אותם לפונקציית Get-ConnectionStatus.
  • Get-ConnectionStatus מעבדת כל שם מחשב ומחזירה אובייקט עם התכונות ComputerName ו-Status.
  • Get-ConnectionStatus מעבירה לאחר מכן כל אובייקט לפקודת ForEach-Object, שמחזירה מחרוזת יחידה בצבע כחול עם סטטוס שקריא לאדם.
Get-Content -Path C:\Test\computers.txt |
Get-ConnectionStatus |
ForEach-Object { Write-Host "$($_.ComputerName) connection status is: $($_.Status)" -ForegroundColor Cyan }

אם קליטת הצינור לא הופעלה על הפרמטר ComputerName או אם Get-ConnectionStatus לא החזירה אובייקט בתוך בלוק ה-Process, PowerShell לא היה מחזיר שום סטטוס למסוף עד שכל האובייקטים (כתובות IP) יועברו.

קשירת צינור על פי שם המאפיין

עד כה, פקודת Get-ConnectionStatus מוגדרת לקבל קלט צינור לפי הערך (ValueFromPipeline) על ידי קבלת מערך של מחרוזות כמו '127.0.0.1', '192.168.1.100'. האם פונקציה זו תעבוד גם עם קלט שיקבל מקובץ CSV בניגוד לקובץ טקסט של כתובות IP?

אולי יש לך קובץ CSV שנראה כמו מטה ב- C:\Test\pc-list.csv.

ComputerName,Location
127.0.0.1,London
192.168.1.100,Paris

שים לב ששדה ComputerName בקובץ ה-CSV הוא אותו שם כמו הפרמטר ComputerName של Get-ConnnectionStatus.

אם תנסה לייבא את ה-CSV ולהעביר אותו דרך הצינור ל-Get-ConnectionStatus, הפונקציה תחזיר תוצאה בלתי צפויה בעמודה ComputerName.

Import-Csv -Path 'C:\Test\pc-list.csv' | Get-ConnectionStatus

ComputerName                                  Status       
------------                                  ------       
@{ComputerName=127.0.0.1; Location=London}    No Connection
@{ComputerName=192.168.1.100; Location=Paris} No Connection

האם תצליח לנחש מה השתבש? לאחר הכל, שם הפרמטר תואם, אז למה לא קישרה צינורות הפוורשל של PowerShell את הפלט שהחזיר Import-CSV לפרמטר ה-ComputerName ב־Get-ConnectionStatus? מכיוון שאתה צריך את המאפיין הפרמטר ValueFromPipelineByPropertyName.

כרגע, לפרמטר ה-ComputerName של הפונקציה יש הגדרת פרמטר הנראית כמו [Parameter(ValueFromPipeline)]. לכן, עליך להוסיף ValueFromPipelineByPropertyName כדי להגדיר את פרמטר ה־ComputerName כך שיתמוך בקלט לפי שם המאפיין, כפי שמוצג למטה.

    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName
    )

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

PS> Import-Csv -Path 'C:\Test\pc-list.csv' | Get-ConnectionStatus

ComputerName  Status       
------------  ------       
127.0.0.1     Ok           
192.168.1.100 No Connection

סיכום

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

אף על פי שפונקציות תעבוד בלי הצינור, הן לא יעבירו "שזרימה" של אובייקטים מפקודה אחת לאחרת ויפשיטו קוד.

האם יש לך פונקציה שכתבת, או אולי מתכוון לכתוב, שיכולה להרוויח מהפכה להיות מוכנה לצינור?

Source:
https://adamtheautomator.com/ppowershell-pipeline/