לולאות ForEach ב-PowerShell: סוגים ושיטות מומלצות

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

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

אחד הנושאים המבלבלים ביותר בלולאת foreach של PowerShell עבור מתחילים הוא כלל האפשרויות שיש לך. אין רק דרך אחת לעבד כל פריט באוסף; יש שלושה!

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

בסיסיות לולאת ForEach של PowerShell

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

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

אולי עליך ליצור קובץ טקסט בכמה תיקיות מפוזרות במערכת הקבצים. נניח, נתיבי התיקיות הם C:\Folder, C:\Program Files\Folder2 ו־C:\Folder3. בלעדי לולאה, נצטרך להפנות ל־Add-Content שלוש פעמים.

Add-Content -Path 'C:\Folder\textfile.txt' -Value 'This is the content of the file'
Add-Content -Path 'C:\Program Files\Folder2\textfile.txt' -Value 'This is the content of the file'
Add-Content -Path 'C:\Folder2\textfile.txt' -Value 'This is the content of the file'

מה ההבחנה היחידה בין כל אחת מההפניות הללו? זהו ערך ה־Path. ערך זה הינו היחיד שמשתנה בין כל אחת מהן.

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

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

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

יש שלושה סוגים טכניים של לולאות foreach בפוורשל. ולמרות שכל אחת דומה לשימוש, חשוב להבין את ההבחנה.

ההצהרה foreach

הסוג הראשון של לולאת foreach הוא הצהרה. foreach הוא מילת מפתח פנימית של פוורשל שאינה cmdlet ולא פונקציה. ההצהרה foreach נמצאת תמיד בצורת: foreach ($i in $array).

בשימוש בדוגמה מעלה, המשתנה $i מייצג את ה-מתבצע או את ערך כל איבר ב-$path כשהוא עובר על כל איבר במערך.

שימו לב שהמשתנה המתבצע אינו חייב להיות $i. שם המשתנה יכול להיות כל דבר.

בדוגמה למטה, ניתן להשיג את אותה משימה כמו שחוזרים על ההפניה Add-Content על ידי עשיית זאת:

# יצירת מערך של תיקיות
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

# ביצוע איטרציה כדי ליצור את אותו קובץ בכל תיקייה
foreach ($i in $folders) {
    Add-Content -Path "$i\SampleFile.txt" -Value "This is the content of the file"
}

ה-foreach הצהרה ידועה כאלטרנטיבה מהירה יותר משימוש ב-ForEach-Object cmdlet.

ה-ForEach-Object CmdLet

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

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
$folders | ForEach-Object (Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file")

לתת דברים מבלבל, ה-cmdlet ForEach-Object יש כינוי נקרא foreach. תלוי איך נקרא המונח "foreach", יתבצעת ההצהרה foreach, או תופעל ה-ForEach-Object.

A good way to differentiate these situations is to notice if the term “foreach” is followed by ($someVariable in $someSet). Otherwise, in any other context, the code author is probably using the alias for ForEach-Object.

השיטה foreach()

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

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
$folders.ForEach({
	Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file"
})

ההבדל החשוב ביותר עם שיטת foreach() הוא איך היא פועלת מתחת למסך.

שימוש בשיטת foreach() הוא מהיר רבות יותר וזה מובן מאליו על סטים גדולים. מומלץ להשתמש בשיטה זו על פני שתי השיטות האחרות אם אפשר.

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

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

List of server names in a text file

עבור תהליך זה, איזה סוג של לולאה אתה חושב שיהיה הכי מתאים?

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

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

ראשית, יצור מערך של כל השמות של השרתים שלי וקרא לו $servers.

$servers = Get-Content .\servers.txt

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

על ידי ביצוע Test-Connection בתוך לולאת foreach לקריאת כל שורת קובץ, תוכל להעביר כל שם שרת המיוצג על ידי משתנה $server ל-Test-Connection. זהו כיצד פוש של לולאה ב- PowerShell עובר דרך קובץ טקסט. תוכל לראות דוגמה של כיצד זה עובד למטה.

foreach ($server in $servers) {
	try {
		$null = Test-Connection -ComputerName $server -Count 1 -ErrorAction STOP
		Write-Output "$server - OK"
	}
	catch {
		Write-Output "$server - $($_.Exception.Message)"
	}
}

כאשר לולאת foreach מופעלת, היא תיראה משהו כמו זה:

Looping Test-Connection through a list of server names

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

ביצת צלחת במה שאנחנו מלמדים, את השיטה DRY method.

דוגמאות ForEach ב- PowerShell

בואו נסתכל על מספר דוגמאות לשימוש בלולאת ForEach. אלה מבוססות על מקרי שימוש בעולם האמיתי המראים את המושג שניתן לשנות כדי להתאים אותו לדרישותיך.

דוגמה 1: יצירת קובץ בכל תת-תיקייה בתיקייה באמצעות הצהרת ה-ForEach

דוגמה זו מדגימה את השימוש הנפוץ של PowerShell foreach בתיקייה.

נניח שיש עשר תת-תיקיות בתוך התיקייה C:\ARCHIVE_VOLUMES. כל תת-תיקייה מייצגת אמצעי ארכיון שמקבל גיבוי יומי. לאחר סיום כל גיבוי, נוצר קובץ בשם BackupState.txt בתוך כל תיקייה שמכיל את התאריך שבו נעשה הגיבוי.

ניתן לראות דוגמה לכך למטה.

Sub-directories under C:\ARCHIVE_VOLUMES

הסקריפט למטה מבצע שלוש פעולות:

  • מקבל את רשימת כל התת-תיקיות בתוך C:\ARCHIVE_VOLUMES
  • עובר דרך כל תיקייה
  • יוצר קובץ טקסט בשם BackupState.txt שמכיל את התאריך והשעה הנוכחיים

הדוגמה למטה משתמשת בהצהרת foreach.

# הגדר את התיקייה ברמת ה- TOP
$TOP_FOLDER = "C:\ARCHIVE_VOLUMES"

# קבל את כל התת-תיקיות באופן רקורסיבי
$Child_Folders = Get-ChildItem -Path $TOP_FOLDER -Recurse | Where-Object { $_.PSIsContainer -eq $true }

# צור קובץ טקסט בכל תת-תיקייה והוסף את התאריך/השעה הנוכחיים כערך.
foreach ($foldername in $Child_Folders.FullName) {
   (get-date -Format G) | Out-File -FilePath "$($foldername)\BackupState.txt" -Force
}

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

Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include backupstate.txt | Select-Object Fullname,CreationTime,LastWriteTime,Length

התמונה למטה מציגה את פלט התסריט המציג את כל קבצי BackupState.txt שנמצאו בכל תת-תיקייה.

A text file is created in each sub-directory

דוגמה 2: קריאת תוכן כל קובץ טקסט בתת-תיקיות

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

  • לחפוש באופן רקורסיבי אחר כל הקבצים BackupState.txt בפנים של כל תת-תיקייה.
  • להשתמש בהצהרת foreach כדי לקרוא את כל הקבצים הטקסט ולקבל את הערך "זמן הגיבוי האחרון".
  • להציג את התוצאה על המסך.
## למצוא את כל קבצי BackupState.txt בתיקיית C:\ARCHIVE_VOLUMES
$files = Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include 'BackupState.txt' | Select-Object DirectoryName,FullName

## לקרוא את תוכן כל קובץ
foreach ($file in $files) {
    Write-Output ("$($file.DirectoryName) last backup time - " + (Get-Content $file.FullName))
}

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

Using foreach to loop through files and read its contents.

דוגמה 3: קבלת שירותים והפעלתם באמצעות ForEach-Object CmdLet

מנהלי מערכות תמיד צריכים לקבל את מצב השירותים ולהפעיל זרימת עבודה ידנית או אוטומטית כדי לתקן את השירותים שנכשלו. בואו נסתכל על התסריט הדוגמתי שמשתמש ב- ForEach-Object cmdlet.

לדוגמה זו, התסריט למטה יבצע את הפעולות הבאות:

  • לקבל רשימת שירותים שמוגדרים להפעלה אוטומטית אך כרגע לא במצב רץ.
  • הבא, הפריטים ברשימה מופנים לְפקודת ForEach-Object כדי לנסות להתחיל כל שירות.
  • A message of either success or failed is displayed depending on the result of the Start-Service command.
## Get a list of automatic services that are stopped.
$services = Get-Service | Where-Object {$.StartType -eq 'Automatic' -and $.Status -ne 'Running'}

## Pass each service object to the pipeline and process them with the Foreach-Object cmdlet
$services | ForEach-Object {
    try {
        Write-Host "Attempting to start '$($.DisplayName)'"
        Start-Service -Name $.Name -ErrorAction STOP
        Write-Host "SUCCESS: '$($.DisplayName)' has been started"
    } catch {
        Write-output "FAILED: $($.exception.message)"
    }
}

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

Using the ForEach-Object loop to start services

דוגמה 4: קריאת נתונים מ- CSV באמצעות השיטה ForEach()

שימוש בנתונים מקבצי CSV פופולרי בקרב מנהלי מערכות. לשים רשומות בתוך קובץ CSV הופך את הרצת הפעולות המסובכות לקלה באמצעות השילוב של Import-CSV ו־ForEach. שילוב זה נהוג לרבות בתהליכי יצירת משתמשים מרובים בפעילות הנדרשת.

בדוגמה הבאה, נניח שיש לך קובץ CSV עם שתי עמודות – שם פרטי ו־שם משפחה. יש למלא את הקובץ CSV עם שמות המשתמשים החדשים שיש ליצור. הקובץ CSV יראה משהו דומה למטה.

"Firstname","Lastname"
"Grimm","Reaper"
"Hell","Boy"
"Rick","Rude"

עכשיו לבלוק הסקריפט הבא. ראשית, יש לייבא את קובץ ה-CSV על ידי מעבר נתיב התוכן ל-Import-CSV cmdlet. לאחר מכן, באמצעות שיטת foreach(), יש לעבור דרך השמות וליצור את המשתמשים החדשים ב-Active Directory.

# יבוא רשימת שמות פרטיים ומשפחה מקובץ CSV
$newUsers = Import-Csv -Path .\Employees.csv

Add-Type -AssemblyName System.Web

# עיבוד הרשימה
$newUsers.foreach(
    {
        # יצירת סיסמה אקראית
        $password = [System.Web.Security.Membership]::GeneratePassword((Get-Random -Minimum 20 -Maximum 32), 3)
        $secPw = ConvertTo-SecureString -String $password -AsPlainText -Force

        # יצירת שם משתמש
        $userName = '{0}{1}' -f $_.FirstName.Substring(0, 1), $_.LastName

        # בניית מאפייני משתמש חדש
        $NewUserParameters = @{
            GivenName       = $_.FirstName
            Surname         = $_.LastName
            Name            = $userName
            AccountPassword = $secPw
        }

        try {
            New-AdUser @NewUserParameters -ErrorAction Stop
            Write-Output "User '$($userName)' has been created."
        }
        catch {
            Write-Output $_.Exception.Message
        }
    }
)

בעת הרצתו, כעת תצטרך ליצור משתמש AD עבור כל שורה בקובץ ה-CSV!

סיכום

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

במאמר זה, למדת על סוגי הלולאות foreach השונות הזמינות בפוורשל, ומה יש לשקול לגביהן להשתמש. ראית גם את שלושת סוגי הלולאות foreach בפעולה באמצעות תרחישים שונים לדוגמה.

קריאה נוספת

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