Цикл Foreach у PowerShell: синтаксис, приклади та поради

Цикл foreach у PowerShell дозволяє перебирати всі елементи колекції й виконувати блок коду для кожного елемента. Наприклад, за допомогою циклу foreach можна створити список усіх файлів у каталозі для аудиту, відобразити всі процеси, запущені в системі, або перемістити старі файли до архіву.

У цій статті детально описано синтаксис циклу foreach у PowerShell і розглянуто типові випадки використання з прикладами скриптів. Далі пояснюється, як уникнути поширених помилок, і пропонуються найкращі практики для ефективного використання циклів foreach.

Початок роботи з циклами PowerShell Foreach

Базовий синтаксис і обробка

Нижче наведено базовий синтаксис циклу foreach:

foreach ($item in $collection) {

    # Code to execute for each item

}

Обробка цього циклу foreach відбувається наступним чином:

  1. PowerShell завантажує колекцію $collection в пам’ять і визначає кількість об’єктів, які вона містить.
  2. Перший об’єкт з $collection присвоюється змінній $item, і для цього об’єкта виконується блок коду.
  3. Значення $item оновлюється до наступного об’єкта в $collection і для цього об’єкта виконується блок коду. Цей крок повторюється до тих пір, поки не будуть оброблені всі об’єкти в $collection.

Розширений синтаксис

Метод foreach пропонує більш стислий спосіб перебору колекцій і потенційно може забезпечити кращу продуктивність. Синтаксис методу наступний:

$collection.ForEach({ <expression> })

Порівняння ключового слова Foreach, Foreach-Object та методу Foreach

Ключове слово foreach, foreach-object та метод Foreach мають схоже призначення, але вони мають різні варіанти використання та поведінку.

Ключове слово Foreach

Ключове слово foreach використовується для перебору колекції елементів, завантажує всі елементи колекції в пам’ять і обробляє їх одночасно. Це робить його ідеальним вибором для роботи з колекціями, що зберігаються в пам’яті, для швидкої обробки.

Командлета ForEach-Object

Командлета foreach-object, з іншого боку, є орієнтованою на пайплайни, призначеною для обробки кожного об’єкта, що проходить через пайплайн, по одному за раз. Вона зменшує використання ресурсів, таких як пам’ять, і підходить для скриптів, де завантаження всієї колекції в пам’ять є недоцільним. Хоча вона дещо повільніша за foreach через наявність пайплайну, її перевага полягає в ефективному використанні пам’яті під час виконання скрипту.

Метод ForEach

Метод foreach був введений в PowerShell 5. Він корисний для певних типів колекцій, таких як масиви та списки, надає стислий синтаксис для виконання дій над кожним елементом. Метод foreach є об’єктноорієнтованим і використовується, коли потрібно створити ланцюжок методів, а для колекції в пам’яті він більш ефективний, ніж командлета foreach-object.

У наступному прикладі отримуються процеси, що споживають понад 400 Мб пам’яті за допомогою ключового слова foreach, командлети foreach-object та методу foreach.

# Get all processes consuming more than 400MB of memory

$processes = Get-Process | Where-Object { $_.WorkingSet64 -gt 400MB }

# Using foreach keyword (Table Format)

Write-Host "`n=== Using foreach Keyword (Table Format) ===" -ForegroundColor Cyan

$output = foreach ($proc in $processes) {

    [PSCustomObject]@{

        "Process Name"  = $proc.ProcessName

        "PID"           = $proc.Id

        "Memory (MB)"   = [math]::Round($proc.WorkingSet64 / 1MB, 2)

    }

}

$output | Format-Table -AutoSize

# Using Foreach-Object (List Format)

Write-Host "`n=== Using Foreach-Object Cmdlet (List Format) ===" -ForegroundColor Green

$processes | ForEach-Object {

    Write-Output "Process Name : $($_.ProcessName)"

    Write-Output "PID          : $($_.Id)"

    Write-Output "Memory Usage : $([math]::Round($_.WorkingSet64 / 1MB, 2)) MB"

    Write-Output "-----------------------------------"

}

# Using .ForEach() Method (CSV-like Output)

Write-Host "`n=== Using .ForEach() Method (CSV-like Output) ===" -ForegroundColor Yellow

$processes.ForEach({

    "$($_.ProcessName),$($_.Id),$([math]::Round($_.WorkingSet64 / 1MB, 2)) MB"

}) | ForEach-Object { Write-Output $_ }
foreach, foreach-object and foreach method

Загальні випадки використання

Виведення всіх чисел у масиві

Наступний скрипт буде перебирати масив чисел і виводити кожне число в консоль:

# Define an array of numbers

$numbers = 1..5

# Loop through the array

foreach ($number in $numbers) {

    Write-Host "Number (foreach keyword): $number"

}
Display All Numbers in an Array

Відображення імен всіх файлів в каталозі

У наступному прикладі PowerShell foreach використовується командлета Get-ChildItem для отримання файлу з вказаного каталогу і відображається ім’я кожного файлу:

foreach ($file in Get-ChildItem -Path " D:\Office") {

    Write-Output $file.Name

}
Display the Names of All Files in a Directory

Робота з кожним об’єктом у колекції

Наступний скрипт отримує всі процеси, імена яких починаються з C або F, перевіряє, чи запущено кожен процес, і додає користувацьку властивість isActive до кожного процесу, щоб задокументувати його стан:

# Get all processes whose names start with 'C' or 'F'

$processes = Get-Process | Where-Object { $_.Name -like "C*" -or $_.Name -like "F*" }

foreach ($process in $processes) {

    $isActive = $process.Responding

    $process | Add-Member -NotePropertyName "IsActive" -NotePropertyValue $isActive

}

$processes | Format-Table -Property Name, Id, IsActive
Manipulate Each Object in a Collection

Прогресивні методи

Обробка об’єктів по-різному на основі потрібних критеріїв

Є можливість використовувати оператори If у циклах foreach для виконання різних дій залежно від характеристик кожного об’єкта, що обробляється. Наступний скрипт отримує всі файли у вказаному каталозі, але виводить імена лише тих, розмір яких перевищує 10 кб:

foreach ($file in Get-ChildItem -Path "D:\Office") {

  if ($file.Length -gt 10KB) {

    Write-Output $file.Name

  }

}
Processing Items Differently Based on Your Criteria

Цей скрипт можна легко модифікувати, щоб він виконував інші дії з великими файлами у каталозі, а не просто перелічував їх. Наприклад, можна скопіювати їх в інше місце, стиснути або видалити залежно від часу останнього доступу до них.

Використання вкладених циклів

Розміщення одного циклу foreach всередині іншого називається вкладеними циклами. Вкладені цикли foreach можна використовувати для перебору двох або більше колекцій та виконання операцій, що залучають елементи з обох колекцій. Наприклад, вкладені цикли можна використовувати для генерації комбінацій параметрів для тестування, створення декартових добутків множин або перебору багатовимірних даних.

Скрипт нижче використовує вкладені цикли foreach для генерації всіх можливих комбінацій елементів з двох різних масивів, один з яких містить три числа, а інший – три літери:

$outerArray = 1..3

$innerArray = 'A', 'B', 'C'

foreach ($outer in $outerArray) {

  foreach ($inner in $innerArray) {

    Write-Output "$outer $inner"

  }

}
Using Nesting

Обробка помилок

Якщо в циклі foreach виникає помилка, скрипт завершується. Одним з поширених прикладів є спроба ділення на нуль, що є математично неможливим. Повідомлення про помилку може допомогти визначити причину проблеми.

Є можливість використати блок Try-Catch для того, щоб вчасно усунути потенційні проблеми й переконатися, що скрипт продовжує обробляти додаткові елементи в колекції. У наведеному нижче скрипті блок try намагається виконати задачу на ділення, використовуючи кожне число з колекції. Якщо операція пройшла успішно, результат записується на хост, якщо ні, то блок catch виводить повідомлення про помилку червоним кольором. У будь-якому випадку цикл переходить до наступного елементу колекції.

# Sample collection of items.

$numbers = @(1, 2, 0, 4, 5)

foreach ($number in $numbers) {

  try {

    $result = 10 / $number

    Write-Host "10 divided by $number is: $result"

  } catch {

    Write-Host "Error: Cannot divide by zero. Skipping $number." -ForegroundColor Red

  }

}
Handling Errors

Уникнення поширених помилок

Ось деякі помилки, яких часто припускаються з циклами foreach, і поради, як їх уникнути.

Використання однакових імен змінних у вкладених циклах

Використання однакових імен змінних у вкладених циклах може призвести до неочікуваної поведінки та конфліктів. Наприклад, у наступному скрипті використовується однакова змінна $item у зовнішньому та внутрішньому циклах:

foreach ($item in $collection) {

  foreach ($item in $nestedCollection) { # Overwrites outer $item

    Write-Host $item

  }

}

Щоб уникнути проблем, слід використовувати унікальні імена змінних для кожного циклу:

foreach ($outerItem in $collection) {

  foreach ($innerItem in $nestedCollection) {

    Write-Host $innerItem

  }

}

Передчасний вихід з циклу

Для виходу з циклу foreach після виконання потрібної умови можна використовувати break. Однак, неправильне використання операторів break може призвести до передчасного завершення циклу, що призведе до неповної обробки колекції.

Відповідно, варто використовувати break з обережністю. Альтернативою може бути пропуск певних ітерацій без виходу з циклу.

Нескінченні цикли

Якщо умова циклу ніколи не набуває значення false або колекція змінюється неналежним чином, цикл може тривати нескінченно довго. Наприклад, у наведеному нижче скрипті під час циклу над колекцією він продовжує додавати нові елементи до цієї колекції:

$collection = @("Item1", "Item2", "Item3")

foreach ($item in $collection) {

  $collection.Add("NewItem")  # Changes collection size dynamically

}

Один зі способів уникнути цього нескінченного циклу – зробити копію оригінальної колекції, а потім переглядати копію, вносячи зміни в оригінал:

# Original collection

$collection = @("Item1", "Item2", "Item3")

# Iterate over a copy of the collection

foreach ($item in $collection.Clone()) {

  Write-Host "Processing $item"

  $collection.Add("NewItem")  # Modify the original collection safely

}

Write-Host "Final Collection: $collection"

Найкращі практики

Наведені нижче поради допоможуть ефективніше використовувати цикли foreach.

Для даних пайплайну слід використовувати командлету foreach-object.

Foreach у PowerShell завантажує колекцію в пам’ять, а потім послідовно обробляє кожен елемент. Однак іноді потрібно обробити об’єкти, передані через конвеєр. У таких випадках слід використовувати командлету foreach-object, яка також використовує менше пам’яті.

Максимізація читабельності та зручності супроводу скриптів.

Щоб полегшити читання і супровід скриптів, слід використовувати однакові відступи й інтервали для циклів foreach, додавати змістовні коментарі та скорочувати тіло циклу, визначаючи функції або методи, як показано на цьому прикладі:

# Define a collection of file paths

$filePaths = Get-ChildItem -Path "D:\Office" -File

# Process each file in the collection

foreach ($file in $filePaths) {

  # Check if the file is larger than 1KB

  if ($file.Length -gt 1KB) {

    # Log the file name and size

    Write-Host "Large file found: $($file.Name) - Size: $($file.Length / 1KB) KB"

  } else {

    # Skip smaller files

    Write-Host "Skipping small file: $($file.Name)"

  }

}
Maximize the readability and maintainability of scripts

Висновок

Цикл foreach у PowerShell є цінним інструментом для автоматизації завдань, які вимагають перебору колекції та запуску коду на кожному елементі, наприклад, керування файлами та управління процесами. Варто почати з експериментів з простими прикладами, наведеними в цій статті, а потім перейти до складніших методів, таких як вкладені цикли та обробка помилок.

Підписатися на новини