ค้นหาเว็บไซต์

วิธีใช้ Powershell Foreach Loop พร้อมตัวอย่าง


คำสั่ง foreach ใน PowerShell เป็นคำสั่งวนซ้ำซึ่งช่วยให้คุณสามารถรันคำสั่งหรือสคริปต์ซ้ำกับแต่ละรายการในคอลเลกชันหรืออาร์เรย์ สิ่งนี้ช่วยให้คุณสามารถเร่งการประมวลผลสำหรับคอลเลกชันรายการจำนวนมากที่กระบวนการหรือฟังก์ชันเดียวกันจำเป็นต้องรันกับแต่ละรายการ

ในบทช่วยสอนนี้ ฉันจะลงรายละเอียดเพื่ออธิบายประโยชน์ของการใช้คำสั่ง foreach ภายในสคริปต์ PowerShell ของคุณและให้ตัวอย่างที่เป็นประโยชน์แก่คุณซึ่งอาจมีประโยชน์

Foreach loop มีประโยชน์อย่างไร

หากไม่มี foreach loop การดำเนินการที่คล้ายกันมากกว่าหนึ่งครั้งกับหลายรายการจะน่าเบื่อและไม่มีประสิทธิภาพ ไม่เพียงแต่จะใช้เวลานานในการเขียนคำสั่งเดิมซ้ำแล้วซ้ำอีก แต่ยังเป็นการใช้โค้ดที่ไม่มีประสิทธิภาพอีกด้วย คำสั่ง foreach ช่วยแก้ปัญหานี้ได้

พูดง่ายๆ ก็คือ คุณสามารถใช้คำสั่ง foreach เพื่อเปลี่ยนโค้ดดังนี้:

$report = [System.Collections.Generic.List[Object]]::new()
$report += Get-Service -name BITS -RequiredServices | Select Name, ServiceType, Status
$report += Get-Service -name DHCP -RequiredServices | Select Name, ServiceType, Status
$report += Get-Service -name gpsvc -RequiredServices | Select Name, ServiceType, Status
$report += Get-Service -name iphlpsvc -RequiredServices | Select Name, ServiceType, Status

เป็นสิ่งที่ดูหรูหรากว่าเล็กน้อย:

$services = Get-Service
$report = [System.Collections.Generic.List[Object]]::new()
Foreach ($Service in $Services) {
    $report += $service | Select Name, ServiceType, Status
}

หรือแม้กระทั่ง:

อย่างที่คุณเห็นจากด้านบน สำหรับโค้ดที่มีขนาด 50 บรรทัดนั้น มันถูกจำกัดให้เหลือเพียง 5 บรรทัด หรือแม้แต่เพียง 2 บรรทัด หากคุณต้องการให้มีประสิทธิภาพจริงๆ! ตัวอย่างที่ได้รับการปรับปรุงทั้งสองตัวอย่างใช้งานได้ดี อย่างไรก็ตาม ขึ้นอยู่กับระดับประสบการณ์ของผู้ที่อ่านโค้ดของคุณ ตัวอย่างแรก (ที่มี 5 บรรทัด) อาจจะอ่านและเข้าใจง่ายกว่า

$report = [System.Collections.Generic.List[Object]]::new()
Get-Service | Foreach-Object {$report += $_ | Select Name, ServiceType, Status}

วิธีเขียน Foreach loop

Foreach loop มีโครงสร้างพื้นฐานเมื่อคุณเขียนมัน คำสั่ง foreach ตามด้วยวงเล็บเหลี่ยม และภายในวงเล็บคือตัวดำเนินการ in เมื่อดูที่โครงสร้างด้านล่าง $item จะกำหนดรายการเดียวในแต่ละรอบของการวนซ้ำ และ $itemcollection เป็นตัวแปรที่มีหลายรายการที่มีการวนซ้ำ

จากนั้นตามด้วยเครื่องหมายปีกกา (หรือวงเล็บปีกกา) และภายในเครื่องหมายปีกกาเหล่านี้คือคำสั่งที่คุณสามารถเรียกใช้กับแต่ละรายการในคอลเลกชันได้โดยใช้ตัวแปร $item

Foreach ($item in $itemcollection) {
    "Do something" $item
}

Foreach กับ Foreach-Object

แม้ว่าคำสั่ง foreach และ foreach-object cmdlet จะดูคล้ายกัน แต่ก็มีจุดประสงค์ที่แตกต่างกัน สิ่งที่น่าสับสนก็คือ foreach ยังเป็นนามแฝงของ foreach-object ดังนั้นแม้ว่าจุดประสงค์ของแต่ละคำสั่งจะเหมือนกัน แต่ก็มีการใช้ต่างกันเล็กน้อย

โดยพื้นฐานแล้ว เมื่อใช้คำสั่ง foreach คอลเลกชันของรายการของคุณจะถูกเก็บไว้ในหน่วยความจำล่วงหน้าในตัวแปร เช่นในตัวอย่างข้างต้น โดยก่อนที่จะใช้คำสั่ง foreach รายการของฉันถูกบันทึกไว้ในตัวแปร $itemcollection เนื่องจาก cmdlet foreach-object ได้รับการออกแบบมาเพื่อใช้ในไปป์ไลน์คำสั่งของคุณ และถึงแม้จะใช้เวลานานกว่าในการดำเนินการ แต่ก็มีพารามิเตอร์ที่ใช้งานได้มากกว่า

คุณสามารถดูความแตกต่างของความเร็วระหว่างคำสั่ง foreach และ foreach-object cmdlet ได้โดยใช้คำสั่ง Measure-Command

เริ่มต้นด้วยการวัดความเร็วของการรัน Foreach-Object ในไปป์ไลน์ของเรา

Measure-Command {
    $report = [System.Collections.Generic.List[Object]]::new()
    Get-Service | Foreach-Object {$report += $_ | Select Name, ServiceType, Status}
}

อย่างที่คุณเห็นใช้เวลาทั้งหมด 72 มิลลิวินาทีในการรันคำสั่ง

ตอนนี้เราจะดูความเร็วเมื่อรันคำสั่งเดียวกันโดยใช้คำสั่ง foreach

Measure-Command {
    $services = Get-Service
    $report = [System.Collections.Generic.List[Object]]::new()
    Foreach ($Service in $Services) {
        $report += $service | Select Name, ServiceType, Status
    }
}

อย่างที่คุณเห็น แม้ว่าในสถานการณ์ของเรา เวลาจะต่างกันเพียงเล็กน้อย แต่คำสั่งก็เสร็จสิ้นเร็วกว่ามาก โดยใช้เวลาเพียง 47 มิลลิวินาที เมื่อเทียบกับการใช้ foreach-object

ตัวอย่างวง foreach

ด้านล่างนี้เป็นตัวอย่างที่แตกต่างกันสำหรับการใช้ foreach loops กับ PowerShell

เรียกใช้คำสั่งเดียวกันสำหรับหลายรายการ

ในรูปแบบที่ง่ายที่สุด เมื่อใช้ foreach loop คุณสามารถรันคำสั่งเดียวกันกับหลายรายการในคอลเลกชันได้ ในตัวอย่างด้านล่าง ฉันได้กำหนดชุดของชื่อหรือบริการที่พบในเครื่องคอมพิวเตอร์ด้วยตนเอง จากนั้น ฉันวนซ้ำแต่ละบริการและรันทั้งคำสั่ง stop-service และ start-service ซึ่งจะดำเนินการที่เกี่ยวข้องกับแต่ละบริการ

$Services = "Spooler","NetLogon","dbupdate","BITS"

Foreach ($service in $services) {
    Stop-service $service
    start-service $service
}

แก้ไขการตั้งค่าสำหรับหลายรายการ

Foreach loop สามารถใช้สำหรับงานที่ซับซ้อนมากขึ้นได้ ตัวอย่างเช่น เนื่องจากฉันทำงานใน Active Directory บ่อยครั้ง ฉันจึงอาจต้องการแก้ไขคุณสมบัติเดียวสำหรับผู้ใช้หลายคน ในตัวอย่างด้านล่าง ฉันจัดเก็บผู้ใช้ทั้งหมดที่มีการตั้งค่าเฉพาะไว้ในตัวแปร $users จากนั้น ฉันจะวนซ้ำผู้ใช้แต่ละรายเพื่อแก้ไขการตั้งค่านั้นเป็นค่าใหม่ ในกรณีนี้คือการตั้งค่าชื่อผู้ใช้หลัก

$users = Get-ADUser -Filter * | Where {$_.UserPrincipalName -match "ourcloudnetwork.co.uk"}
Foreach ($user in $users) {
        $upn = $user.UserPrincipalName.Replace("ourcloudnetwork.co.uk","ourcloudnetwork.com")
        Set-ADUser -Identity $user.SamAccountName -UserPrincipalName $upn
}

ใช้ฟังก์ชันภายใน Foreach Loop

เมื่อจัดการกับสคริปต์ขนาดใหญ่ที่มีการดำเนินการที่ซับซ้อนมากขึ้นภายในลูป foreach มันอาจจะเหมาะสมกว่าที่จะย้ายการดำเนินการบางอย่างไปยังฟังก์ชัน จากนั้นเรียกใช้ฟังก์ชันนั้นจากภายในลูป แม้ว่าตัวอย่างของฉันจะไม่ยุติธรรม แต่แนวคิดยังคงเหมือนเดิมสำหรับงานที่ซับซ้อนมากขึ้น

$olddomain = "ourcloudnetwork.com"
$newdomain = "ourcloudnetwork.co.uk"

function Change-Upn {
    $upn = $user.UserPrincipalName.Replace("$olddomain","$newdomain")
    Set-ADUser -Identity $user.SamAccountName -UserPrincipalName $upn
}

$users = Get-ADUser -Filter * | Where {$_.UserPrincipalName -match "$olddomain"}
Foreach ($user in $users) {
        Change-Upn
}

สร้างอาร์เรย์ด้วย foreach loop

สิ่งที่ฉันพบว่าตัวเองทำเกือบทุกครั้งที่ใช้ลูป foreach คือการสร้างอาร์เรย์บางรูปแบบ สามารถสร้างอาร์เรย์ได้อย่างรวดเร็วและมีประสิทธิภาพโดยใช้ลูป foreach ขั้นแรกให้เริ่มต้นด้วยการสร้างอาร์เรย์ว่างก่อนลูป foreach จากนั้นใช้ตัวดำเนินการ += เพื่อเพิ่มรายการใหม่ลงในอาร์เรย์ของคุณ

ด้านล่างนี้เป็นตัวอย่างง่ายๆ ที่เราวนซ้ำแต่ละรายการที่จัดเก็บไว้ใน $items จากนั้นวนซ้ำแต่ละรายการและเพิ่มคุณสมบัติของรายการลงในอาร์เรย์

$items = "1","2","3","4","5"

$array = @()

Foreach ($item in $items) {
        $array += $item.property
}

สร้างรายงานด้วย foreach loop

การสร้างรายงานด้วยออบเจ็กต์ PowerShell แบบกำหนดเองถือเป็นทักษะที่มีประโยชน์อย่างเหลือเชื่อ แม้ว่าคุณจะสามารถค้นหาข้อมูลทั้งหมดที่ต้องการได้บ่อยครั้งโดยใช้คำสั่ง GET ที่เกี่ยวข้อง แต่การสร้างรายงานที่มีการวนซ้ำ foreach จะทำให้คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับแต่ละรายการในคอลเลกชัน และสร้างรายงานโดยละเอียดตรงตามความต้องการของคุณได้ ข้อมูลที่จำเป็น

ในตัวอย่างด้านล่าง ฉันกำลังค้นหาผู้ใช้ทั้งหมดใน Active Directory จากนั้นฉันใช้ข้อมูลนั้นเพื่อวนซ้ำผู้ใช้แต่ละรายและค้นหากล่องจดหมาย Exchange ของพวกเขาด้วยคำสั่ง Get-Mailbox

$users = Get-ADUser -Filter *

$Report = [System.Collections.Generic.List[Object]]::new()

forEach ($user in $users) {
    $Aliases = $null
    $mailbox = $null
    $mailbox = Get-Mailbox -Identity $user.UserPrincipalName
    $Aliases = ($mailbox.EmailAddresses | ForEach-Object {$_ -replace "smtp:",""}) -join ","
    $obj = [PSCustomObject][ordered]@{
        "DisplayName" = $cuser.DisplayName
        "UserPrincipalName" = $cuser.UserPrincipalName
        "Aliases" = $Aliases
    }
    $report.Add($obj)
 }

คุณสามารถดูได้จากโค้ดที่ฉันสร้างออบเจ็กต์ Collection ด้วย:

$Report=[System.Collections.Generic.List[Object]]::new()

จากนั้น ฉันสร้างออบเจ็กต์ PowerShell แบบกำหนดเองซึ่งจะถูกเพิ่มลงในคอลเลกชันด้วยโค้ดต่อไปนี้:

$report.Add($obj)

สร้างลูป foreach ที่ซ้อนกัน

คุณยังสามารถซ้อนคำสั่ง foreach ภายในคำสั่ง foreach ที่มีอยู่ได้ ในตัวอย่างด้านล่าง ฉันกำลังสร้างรายงานเกี่ยวกับกลุ่มอีเมลใน Exchange ฉันวนซ้ำแต่ละกลุ่ม จากนั้นรับสมาชิกกลุ่มของแต่ละกลุ่ม และวนซ้ำสมาชิกแต่ละคนเพื่อดึงข้อมูลเฉพาะและสร้างรายงานพื้นฐาน

$Report = [System.Collections.Generic.List[Object]]::new()

$GroupList = Get-DistributionGroup

Foreach ($group in $grouplist) {
    $groupmembers = $null
    $groupmembers = Get-DistributionGroupMember -Identity $group.Name
    Foreach ($member in $groupmembers) {
        $obj = [PSCustomObject][Ordered]@{  
         "Member"                 = $member.PrimarySmtpAddress
         "Member Type"            = $Member.RecipientType
         "Group"                  = $Group.Name
         "Group Address"          = $Group.PrimarySmtpAddress
         }
    $Report.Add($obj)
    }
}

รวมคำสั่ง IF ภายในวง foreach

เช่นเดียวกับการซ้อนคำสั่ง foreach คุณยังสามารถซ้อนหรือรวมคำสั่ง IF ภายในลูปของคุณเพื่อดำเนินการเฉพาะเจาะจงตามเกณฑ์หรือเพื่อให้มีการตรวจสอบข้อผิดพลาดเพิ่มเติมในสคริปต์ของคุณ ในลักษณะพื้นฐานมาก เราสามารถใช้คำสั่ง IF ในตัวอย่างก่อนหน้าของเราเพื่อให้แน่ใจว่าเราได้รวบรวมกลุ่มใดๆ ในรายงานของเราที่ไม่มีสมาชิกใดๆ

$Report = [System.Collections.Generic.List[Object]]::new()

$GroupList = Get-DistributionGroup

Foreach ($group in $grouplist) {
    $groupmembers = $null
    $groupmembers = Get-DistributionGroupMember -Identity $group.Name
    If ($groupmembers -eq "") {
        $obj = [PSCustomObject][Ordered]@{  
         "Member"                 = "No members"
         "Member Type"            = "N/A"
         "Group"                  = $Group.Name
         "Group Address"          = $Group.PrimarySmtpAddress
         }
         $Report.Add($obj)
    } else {
         Foreach ($member in $groupmembers) {
                $obj = [PSCustomObject][Ordered]@{  
                 "Member"                 = $member.PrimarySmtpAddress
                 "Member Type"            = $Member.RecipientType
                 "Group"                  = $Group.Name
                 "Group Address"          = $Group.PrimarySmtpAddress
                }
         $Report.Add($obj)
    }
}

ในสคริปต์สุดท้าย หากกลุ่มไม่มีสมาชิก จะไม่มีการเพิ่มข้อมูลลงในรายงาน ตอนนี้เราได้บันทึกกลุ่มว่างโดยใช้คำสั่ง IF ที่จุดเริ่มต้นของการวนซ้ำของเรา

บทความที่เกี่ยวข้อง