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:\Folder
C:\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 nazwieforeach
. W zależności od sposobu wywołania wyrażenia „foreach” uruchamiane jest oświadczenieforeach
lub uruchamiane jestForEach-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 dlaForEach-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.
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:
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.
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.
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.
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ć.
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