Articles

Adam Automator

Gdy po raz pierwszy zaczniesz pisać skrypty PowerShell, nieuchronnie dojdziesz do miejsca, w którym będziesz musiał przetworzyć wiele elementów z kolekcji. To właśnie wtedy będziesz musiał zagłębić się w pętle foreach PowerShella i dowiedzieć się o co w nich chodzi.

Prawie wszystkie języki programowania posiadają konstrukcję zwaną pętlą; PowerShell nie jest inny. Jednym z najbardziej popularnych typów pętli w PowerShell jest pętla foreach. W najbardziej podstawowym wydaniu, pętla foreach czyta całą kolekcję elementów i na każdym elemencie uruchamia jakiś kod.

Jednym z najbardziej mylących aspektów pętli foreach w PowerShell dla początkujących są wszystkie opcje jakie mamy. Nie ma tylko jednego sposobu na przetwarzanie każdego elementu w kolekcji; są trzy!

W tym artykule dowiesz się, jak działa każdy typ pętli foreach i kiedy używać jednego nad drugim. Zanim skończysz ten artykuł, będziesz już dobrze rozumiał każdy typ pętli foreach.

Table of Contents

Pętla PowerShell ForEach Podstawy

Jednym z najczęstszych typów pętli, których będziesz używał w PowerShell, jest pętla typu foreach. Pętla foreach odczytuje zbiór obiektów (iteruje) i kończy działanie, gdy skończy z ostatnim z nich. Zbiór obiektów, które są odczytywane, jest zwykle reprezentowany przez tablicę lub tablicę hashtable.

UWAGA: Termin iteracja jest terminem programistycznym, który odnosi się do każdego przebiegu pętli. Za każdym razem, gdy pętla kończy cykl, jest to nazywane iteracją. Czynność uruchamiania pętli nad zestawem obiektów jest powszechnie określana jako iteracja nad zestawem.

Może trzeba utworzyć plik tekstowy w kilku folderach rozrzuconych po systemie plików. Załóżmy, że ścieżki do folderów to C:\FolderC:\Program Files\Folder2 i C:\Folder3. Bez pętli musielibyśmy odwołać się do cmdleta Add-Content trzy razy.

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'

Jaka jest jedyna różnica między każdym z tych odwołań do poleceń? Jest nią wartość Path. Wartość dla Path jest jedyną wartością, która zmienia się wśród każdego z nich.

Dublujesz mnóstwo kodu. Marnujesz czas na pisanie i otwierasz się na problemy w dół drogi. Zamiast tego powinieneś utworzyć „zestaw”, który zawiera wszystkie elementy, które się zmieniają. Dla tego przykładu, użyjemy tablicy.

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

Masz teraz każdą z tych ścieżek przechowywanych w pojedynczym „zestawie” lub tablicy. Jesteś teraz przygotowany do użycia pętli do iteracji nad każdym z tych elementów. Zanim jednak to zrobisz, jest to dobry moment, aby wspomnieć o temacie, który zazwyczaj przeszkadza osobom początkującym w PowerShell. W przeciwieństwie do innych typów pętli, pętle foreach nie są jednym z tych samych.

W PowerShellu istnieją technicznie trzy typy pętli foreach. Chociaż każdy z nich jest podobny w użyciu, ważne jest, aby zrozumieć różnicę.

Oświadczenie foreach

Pierwszym typem foreach pętli jest oświadczenie. foreach to wewnętrzne słowo kluczowe PowerShell, które nie jest cmdletem ani funkcją. Deklaracja foreach jest zawsze używana w formularzu: foreach ($i in $array).

Używając powyższego przykładu, zmienna $i reprezentuje iterator lub wartość każdego elementu w $path, ponieważ iteruje po każdym elemencie w tablicy.

Zauważ, że zmienna iteratora nie musi być $i. Nazwa zmiennej może być dowolna.

W poniższym przykładzie możesz osiągnąć to samo zadanie, co powtórzenie referencji Add-Content, wykonując to:

# Create an array of folders$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')# Perform iteration to create the same file in each folderforeach ($i in $folders) { Add-Content -Path "$i\SampleFile.txt" -Value "This is the content of the file"}

Oświadczenie foreach jest znane jako szybsza alternatywa niż użycie cmdleta ForEach-Object.

The ForEach-Object CmdLet

Jeśli foreach jest deklaracją i może być użyty tylko w jeden sposób, ForEach-Object jest cmdletem z parametrami, który może być użyty na wiele różnych sposobów. Podobnie jak instrukcja foreach, cmdlet ForEach-Object może iterować po zestawie obiektów. Tylko tym razem, przekazuje ten zestaw obiektów i akcję do wykonania na każdym obiekcie jako parametr, jak pokazano poniżej.

$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")

Uwaga: Aby wszystko było bardziej skomplikowane, cmdlet ForEach-Object ma alias o nazwie foreach. W zależności od sposobu wywołania wyrażenia „foreach” uruchamiane jest oświadczenie foreach lub uruchamiane jest ForEach-Object.

Dobrym sposobem na rozróżnienie tych sytuacji jest zwrócenie uwagi, czy po wyrażeniu „foreach” występuje ($someVariable in $someSet). W przeciwnym razie, w każdym innym kontekście, autor kodu prawdopodobnie używa aliasu dla ForEach-Object.

Metoda foreach()

Jedna z najnowszych pętli foreach została wprowadzona w PowerShell v4 o nazwie foreach() metoda. Metoda ta istnieje na obiekcie tablicy lub kolekcji. Metoda foreach() posiada standardowy parametr bloku skryptu, który zawiera akcje do wykonania w każdej iteracji, tak jak pozostałe.

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

Najbardziej znaczącą różnicą w metodzie foreach() jest to, jak działa ona pod maską.

Używanie metody foreach() jest znacznie szybsze i jest zauważalne w przypadku dużych zestawów. Zalecane jest używanie tej metody zamiast dwóch pozostałych, jeśli to możliwe.

Mini-projekt: Looping Over a Set of Server Names

Jednym z najczęstszych zastosowań pętli w PowerShell jest odczytanie zestawu serwerów z jakiegoś źródła i wykonanie jakiejś akcji na każdym z nich. Dla naszego pierwszego mini-projektu, zbudujmy kod, który pozwoli nam odczytać nazwy serwerów (po jednej na linię) z pliku tekstowego i pingować każdy z nich, aby określić czy są online czy nie.

Lista nazw serwerów w pliku tekstowym
Lista nazw serwerów w pliku tekstowym

Dla tego przepływu pracy, jaki typ pętli według Ciebie działałby najlepiej?

Zauważ, że wspomniałem słowo „zestaw”, jak w „zestawie serwerów”. To jest wskazówka właśnie tam. Masz już określoną liczbę nazw serwerów i chciałbyś wykonać jakąś akcję na każdym z nich. To brzmi jak świetna okazja do wypróbowania najczęściej używanej pętli PowerShella; pętli foreach.

Pierwszym zadaniem jest stworzenie zestawu nazw serwerów w kodzie. Najczęstszym sposobem, aby to zrobić, jest utworzenie tablicy. Na szczęście Get-Content, domyślnie zwraca tablicę, której każdy element reprezentowany jest przez pojedynczą linię w pliku tekstowym.

Po pierwsze, utwórz tablicę wszystkich nazw moich serwerów i nazwij ją $servers.

$servers = Get-Content .\servers.txt

Teraz, gdy masz już utworzoną tablicę, musisz potwierdzić, czy każdy serwer jest online, czy nie. Świetny cmdlet do testowania połączenia serwera nazywa się Test-Connection. Ten cmdlet wykonuje kilka testów połączenia na komputerze, aby sprawdzić, czy jest on online, czy nie.

Wykonując Test-Connection wewnątrz pętli foreach w celu odczytania każdej linii pliku, możesz przekazać każdą nazwę serwera reprezentowaną przez zmienną $server do Test-Connection. W ten sposób PowerShell zapętla plik tekstowy. Możesz zobaczyć przykład jak to działa poniżej.

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

Gdy pętla foreach zostanie wykonana, będzie wyglądała mniej więcej tak:

Pętla Test-Connection poprzez listę nazw serwerów
Pętla Test-.Połączenie poprzez listę nazw serwerów

Teraz udało Ci się pomyślnie przetestować połączenie pliku tekstowego pełnego nazw serwerów! W tym momencie, możesz dodawać i usuwać nazwy serwerów wewnątrz pliku tekstowego bez zmiany kodu.

Praktykowałeś to, co głosiliśmy, metodę DRY.

Przykłady ForEach PowerShell

Przyjrzyjrzyjmy się kilku przykładom użycia pętli ForEach. Są one oparte na rzeczywistych przypadkach użycia pokazujących koncepcję, którą możesz zmodyfikować, aby dopasować ją do swoich wymagań.

Przykład 1: Tworzenie pliku w każdym podfolderze w katalogu przy użyciu instrukcji ForEach

Przykład ten demonstruje powszechne zastosowanie PowerShell foreach folderu w katalogu.

Załóżmy, że wewnątrz folderu C:\ARCHIVE_VOLUMES znajduje się dziesięć podfolderów. Każdy podfolder reprezentuje wolumin archiwum, którego kopia zapasowa jest tworzona codziennie. Po zakończeniu każdej kopii zapasowej w każdym folderze tworzony jest plik o nazwie BackupState.txt zawierający datę wykonania kopii zapasowej.

Przykład tego, jak to może wyglądać, można zobaczyć poniżej.

Podkatalogi pod C:\ARCHIVE_VOLUMES
Podkatalogi pod C:\\ARCHIVE_VOLUMES

Poniższy skrypt wykonuje trzy czynności:

  • pobiera listę wszystkich podkatalogów znajdujących się wewnątrz
  • pętla po każdym folderze
  • tworzy plik tekstowy o nazwie BackupState.txt zawierający aktualną datę i czas jego wartość

Przykład poniżej wykorzystuje instrukcję foreach.

# Define the TOP-level folder$TOP_FOLDER = "C:\ARCHIVE_VOLUMES"# Get all sub folders recursively$Child_Folders = Get-ChildItem -Path $TOP_FOLDER -Recurse | Where-Object { $_.PSIsContainer -eq $true }# Create a text file in each sub-folder and add the current date/time as value.foreach ($foldername in $Child_Folders.FullName) { (get-date -Format G) | Out-File -FilePath "$($foldername)\BackupState.txt" -Force}

Używając cmdleta Get-ChildItem można potwierdzić, że pliki zostały utworzone lub zaktualizowane wewnątrz każdego z podfolderów.

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

Zrzut ekranu poniżej przedstawia dane wyjściowe skryptu wyświetlającego wszystkie BackupState.txt pliki znalezione w każdym podkatalogu.

Plik tekstowy jest tworzony w każdym podkatalogu
Plik tekstowy jest tworzony w każdym podkatalogu

Przykład 2: Odczyt zawartości każdego pliku tekstowego w podkatalogach

Następnie, aby zademonstrować użycie PowerShell foreach file w katalogu, poniższy skrypt odczyta każdy BackupState.txt plik utworzony w przykładzie 1.

  • Rekursywnie znajdź wszystkie BackupState.txt pliki wewnątrz każdego podkatalogu.
  • Używając instrukcji foreach, odczytaj każdy plik tekstowy, aby uzyskać wartość „last backup time”.
  • Wyświetl wynik na ekranie.
## Find all BackupState.txt files in C:\ARCHIVE_VOLUMES$files = Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include 'BackupState.txt' | Select-Object DirectoryName,FullName## Read the contents of each fileforeach ($file in $files) { Write-Output ("$($file.DirectoryName) last backup time - " + (Get-Content $file.FullName))}

Po wykonaniu skryptu w sesji PowerShell powinieneś zobaczyć wyjście podobne do poniższego zrzutu ekranu. To pokazuje, że PowerShell zapętla pliki, odczytuje ich zawartość i wyświetla dane wyjściowe.

Użycie foreach do zapętlenia plików i odczytania jego zawartości.
Użycie foreach do zapętlenia plików i odczytania jego zawartości.

Przykład 3: Pobieranie usług i ich uruchamianie za pomocą polecenia ForEach-Object

Administratorzy systemu często muszą uzyskać informacje o stanie usług i uruchomić ręczny lub automatyczny przepływ pracy, aby naprawić wszelkie niedziałające usługi. Przyjrzyjmy się przykładowemu skryptowi wykorzystującemu ForEach-Object cmdlet.

Dla tego przykładu, poniższy skrypt wykona następujące czynności:

  • Poznaj listę usług, które są skonfigurowane do automatycznego uruchamiania, ale obecnie nie są w stanie uruchomienia.
  • Następnie elementy z listy są przekazywane do cmdleta ForEach-Object w celu podjęcia próby uruchomienia każdej z usług.
  • W zależności od wyniku polecenia Start-Service wyświetlany jest komunikat o powodzeniu lub niepowodzeniu.
## 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)" }}

Po wykonaniu skryptu będzie on wyglądał tak, jak na poniższym zrzucie ekranu. Jak widać, każda usługa otrzymała polecenie startu. Niektóre z nich zostały uruchomione pomyślnie, podczas gdy niektóre z nich nie udało się uruchomić.

Używanie pętli ForEach-Object do uruchamiania usług
Używanie pętli ForEach-Object do uruchamiania usług

Przykład 4: Odczyt danych z CSV za pomocą metody ForEach()

Używanie danych z plików CSV jest popularne wśród administratorów systemów. Umieszczanie rekordów wewnątrz pliku CSV ułatwia wykonywanie operacji masowych przy użyciu kombinacji Import-CSV i ForEach. Ta kombinacja jest powszechnie używana do tworzenia wielu użytkowników w Active Directory.

W następnym przykładzie założono, że masz plik CSV z dwiema kolumnami – Firstname i Lastname. Ten plik CSV powinien być następnie wypełniony nazwami nowych użytkowników, którzy mają zostać utworzeni. Plik CSV będzie wyglądał jak poniżej.

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

Teraz kolejny blok skryptu. Najpierw importujemy plik CSV przekazując ścieżkę zawartości do cmdleta Import-CSV. Następnie, używając metody foreach(), zapętlamy nazwy i tworzymy nowych użytkowników w Active Directory.

# Import list of Firstname and Lastname from CSV file$newUsers = Import-Csv -Path .\Employees.csvAdd-Type -AssemblyName System.Web# Process the list$newUsers.foreach( { # Generate a random password $password = ::GeneratePassword((Get-Random -Minimum 20 -Maximum 32), 3) $secPw = ConvertTo-SecureString -String $password -AsPlainText -Force # Formulate a username $userName = '{0}{1}' -f $_.FirstName.Substring(0, 1), $_.LastName # Build new user attributes $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 } })

Po uruchomieniu, powinieneś mieć teraz utworzonego użytkownika AD dla każdego wiersza w pliku CSV!

Podsumowanie

Logika stojąca za pętlą foreach w PowerShell nie różni się od logiki innych języków programowania. Zmienia się jedynie sposób jej użycia oraz to, która odmiana pętli foreach jest wybrana do konkretnego zadania.

W tym artykule poznałeś różne typy pętli foreach dostępnych w PowerShell oraz dowiedziałeś się, co należy wziąć pod uwagę przy wyborze pętli foreach. Zobaczyłeś również trzy typy pętli foreach w akcji, używając różnych przykładowych scenariuszy.

Dalsza lektura

  • Zarządzanie plikami CSV w PowerShell za pomocą Import-Csv (pętla foreach)
  • New-ADUser: Tworzenie użytkowników Active Directory za pomocą PowerShell
  • Podstawy pętli PowerShell: Foreach | Scripting Blog
  • Podstawy pętli PowerShell.
  • Dodaj komentarz

    Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *