Adam der Automator
Wenn Sie zum ersten Mal mit dem Schreiben von PowerShell-Skripten beginnen, werden Sie unweigerlich an eine Stelle kommen, an der Sie mehrere Elemente aus einer Auflistung verarbeiten müssen. Dann müssen Sie sich mit PowerShell foreach-Schleifen beschäftigen und lernen, was es damit auf sich hat.
Nahezu alle Programmiersprachen haben ein Konstrukt namens Schleifen; PowerShell ist da nicht anders. Eine der beliebtesten Arten von Schleifen in PowerShell ist die foreach-Schleife. In ihrer einfachsten Form liest eine foreach-Schleife eine ganze Sammlung von Elementen und führt für jedes Element eine Art von Code aus.
Einer der verwirrendsten Aspekte der PowerShell foreach-Schleife für Anfänger sind die vielen Optionen, die Sie haben. Es gibt nicht nur eine Möglichkeit, jedes Element in einer Auflistung zu verarbeiten, sondern drei!
In diesem Artikel erfahren Sie, wie die einzelnen Typen der foreach-Schleife funktionieren und wann Sie die eine statt der anderen verwenden sollten. Wenn Sie mit diesem Artikel fertig sind, werden Sie ein gutes Verständnis für jede Art von foreach-Schleife haben.
Inhaltsverzeichnis
PowerShell ForEach-Schleifen-Grundlagen
Einer der häufigsten Schleifentypen, die Sie in PowerShell verwenden, ist der foreach
-Schleifentyp. Eine foreach
-Schleife liest eine Reihe von Objekten (iteriert) und wird abgeschlossen, wenn sie mit dem letzten Objekt fertig ist. Die Sammlung von Objekten, die gelesen werden, wird typischerweise durch ein Array oder eine Hashtabelle dargestellt.
Hinweis: Der Begriff Iteration ist ein Programmierbegriff, der sich auf jeden Durchlauf einer Schleife bezieht. Jedes Mal, wenn eine Schleife einen Zyklus durchläuft, nennt man dies eine Iteration. Der Vorgang, eine Schleife über eine Menge von Objekten laufen zu lassen, wird allgemein als Iteration über die Menge bezeichnet.
Vielleicht müssen Sie eine Textdatei in einigen Ordnern erstellen, die über ein Dateisystem verteilt sind. Sagen wir, die Ordnerpfade sind C:\Folder
C:\Program Files\Folder2
und C:\Folder3
. Ohne Schleife müssten wir das Add-Content
-Cmdlet dreimal referenzieren.
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'
Was ist der einzige Unterschied zwischen diesen Befehlsreferenzen? Es ist der Path
-Wert. Der Wert für Path
ist der einzige Wert, der sich bei jedem von ihnen ändert.
Sie duplizieren eine Menge Code. Sie verschwenden Zeit beim Tippen und öffnen sich selbst für Probleme auf dem Weg dorthin. Stattdessen sollten Sie ein „Set“ erstellen, das nur alle Elemente enthält, die sich ändern. Für dieses Beispiel werden wir ein Array verwenden.
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
Sie haben jetzt jeden dieser Pfade in einem einzigen „Set“ oder Array gespeichert. Sie sind nun bereit, eine Schleife zu verwenden, um über jedes dieser Elemente zu iterieren. Bevor Sie das tun, ist es jedoch ein guter Zeitpunkt, um ein Thema zu erwähnen, das PowerShell-Neulinge normalerweise verwirrt. Im Gegensatz zu anderen Schleifentypen sind foreach
-Schleifen nicht ein und dasselbe.
Es gibt technisch gesehen drei Arten von foreach
-Schleifen in PowerShell. Obwohl jede ähnlich zu verwenden ist, ist es wichtig, den Unterschied zu verstehen.
Die foreach-Anweisung
Der erste Typ der foreach
-Schleife ist eine Anweisung. foreach
ist ein internes PowerShell-Schlüsselwort, das weder ein Cmdlet noch eine Funktion ist. Die foreach
-Anweisung wird immer in der Form verwendet: foreach ($i in $array)
.
Anhand des obigen Beispiels stellt die Variable $i
den Iterator oder den Wert jedes Elements in $path
dar, während sie über jedes Element im Array iteriert.
Beachten Sie, dass die Iterator-Variable nicht
$i
sein muss. Der Variablenname kann beliebig sein.
Im folgenden Beispiel können Sie die gleiche Aufgabe wie bei der Wiederholung der Add-Content
-Referenz folgendermaßen erfüllen:
# 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"}
Die foreach
-Anweisung ist bekanntermaßen eine schnellere Alternative als die Verwendung des Cmdlets ForEach-Object
.
Das CmdLet „ForEach-Object“
Während foreach
eine Anweisung ist und nur auf eine einzige Weise verwendet werden kann, ist ForEach-Object
ein Cmdlet mit Parametern, das auf viele verschiedene Arten eingesetzt werden kann. Wie die foreach
-Anweisung kann das Cmdlet ForEach-Object
über eine Gruppe von Objekten iterieren. Nur dieses Mal übergibt es diesen Satz von Objekten und die Aktion, die für jedes Objekt ausgeführt werden soll, als Parameter, wie unten gezeigt.
$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")
Hinweis: Um die Dinge verwirrend zu machen, hat das Cmdlet
ForEach-Object
einen Alias namensforeach
. Je nachdem, wie der Begriff „foreach“ aufgerufen wird, wird dieforeach
-Anweisung oder dasForEach-Object
ausgeführt.Eine gute Möglichkeit, diese Situationen zu unterscheiden, ist es, darauf zu achten, ob auf den Begriff „foreach“ das
($someVariable in $someSet)
folgt. Andernfalls, in jedem anderen Kontext, verwendet der Code-Autor wahrscheinlich den Alias fürForEach-Object
.
Die foreach()-Methode
Eine der neuesten foreach
-Schleifen wurde in PowerShell v4 als foreach()
-Methode eingeführt. Diese Methode existiert auf einem Array- oder Auflistungsobjekt. Die foreach()
-Methode hat einen Standard-Skriptblockparameter, der die Aktionen enthält, die bei jeder Iteration ausgeführt werden sollen, genau wie die anderen.
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')$folders.ForEach({Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file"})
Der wichtigste Unterschied bei der foreach()
-Methode ist, wie sie unter der Haube funktioniert.
Die Verwendung der
foreach()
-Methode ist deutlich schneller und macht sich bei großen Mengen bemerkbar. Es wird empfohlen, diese Methode nach Möglichkeit den beiden anderen vorzuziehen.
Mini-Projekt: Schleife über einen Satz von Servernamen
Eine der häufigsten Verwendungen für eine Schleife in PowerShell ist das Lesen eines Satzes von Servern aus einer Quelle und das Ausführen einer Aktion auf jedem von ihnen. Für unser erstes Miniprojekt wollen wir einen Code erstellen, mit dem wir Servernamen (einen pro Zeile) aus einer Textdatei lesen und jeden Server anpingen können, um festzustellen, ob er online ist oder nicht.
Welcher Typ von Schleife würde für diesen Arbeitsablauf Ihrer Meinung nach am besten funktionieren?
Beachten Sie, dass ich das Wort „Satz“ wie in „Satz von Servern“ erwähnt habe. Das ist ein Anhaltspunkt. Sie haben bereits eine bestimmte Anzahl von Servernamen, und Sie möchten für jeden von ihnen eine Aktion durchführen. Das klingt nach einer guten Gelegenheit, die am häufigsten verwendete PowerShell-Schleife auszuprobieren; die foreach
-Schleife.
Die erste Aufgabe besteht darin, einen Satz von Servernamen im Code zu erstellen. Der gängigste Weg, dies zu tun, ist die Erstellung eines Arrays. Zum Glück für uns gibt Get-Content
standardmäßig ein Array zurück, wobei jedes Element im Array durch eine einzelne Zeile in der Textdatei repräsentiert wird.
Erstellen Sie zunächst ein Array mit allen meinen Servernamen und nennen Sie es $servers
.
$servers = Get-Content .\servers.txt
Nachdem Sie das Array erstellt haben, müssen Sie nun überprüfen, ob jeder Server online ist oder nicht. Ein gutes Cmdlet, um die Verbindung eines Servers zu testen, heißt Test-Connection
. Dieses Cmdlet führt ein paar Verbindungstests auf einem Computer durch, um zu sehen, ob er online ist oder nicht.
Indem Sie Test-Connection
innerhalb einer foreach
-Schleife ausführen, um jede Dateizeile zu lesen, können Sie jeden Servernamen, der durch die Variable $server
dargestellt wird, an Test-Connection
übergeben. So kann PowerShell eine Textdatei in einer Schleife durchlaufen. Ein Beispiel, wie das funktioniert, sehen Sie unten.
foreach ($server in $servers) {try {$null = Test-Connection -ComputerName $server -Count 1 -ErrorAction STOPWrite-Output "$server - OK"}catch {Write-Output "$server - $($_.Exception.Message)"}}
Wenn die foreach
-Schleife ausgeführt wird, sieht sie dann etwa so aus:
Sie haben nun erfolgreich die Verbindung einer Textdatei voller Servernamen getestet! An diesem Punkt können Sie nun nach Belieben Servernamen innerhalb der Textdatei hinzufügen oder entfernen, ohne den Code zu ändern.
Sie haben erfolgreich das praktiziert, was wir gepredigt haben, die DRY-Methode.
ForEach PowerShell-Beispiele
Lassen Sie uns ein paar Beispiele für die Verwendung der ForEach-Schleife betrachten. Diese basieren auf realen Anwendungsfällen, die das Konzept veranschaulichen und die Sie an Ihre Anforderungen anpassen können.
Beispiel 1: Erstellen einer Datei in jedem Unterordner in einem Verzeichnis mit der ForEach-Anweisung
Dieses Beispiel veranschaulicht die allgemeine Verwendung von PowerShell ForEach in einem Verzeichnis.
Angenommen, es gibt zehn Unterordner innerhalb des Ordners C:\ARCHIVE_VOLUMES
. Jeder Unterordner repräsentiert ein Archiv-Volume, das täglich gesichert wird. Nach Abschluss jedes Backups wird in jedem Ordner eine Datei mit dem Namen BackupState.txt
erstellt, die das Datum des Backups enthält.
Im Folgenden sehen Sie ein Beispiel dafür, wie dies aussehen könnte.
Das folgende Skript führt drei Aktionen aus:
- erhält die Liste aller UnterOrdner innerhalb von
C:\ARCHIVE_VOLUMES
- läuft durch jeden Ordner
- erzeugt eine Textdatei mit dem Namen
BackupState.txt
, die das aktuelle Datum und die Uhrzeit als Wert enthält
Das folgende Beispiel verwendet die Anweisung 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}
Mit dem Cmdlet Get-ChildItem
können Sie bestätigen, dass die Dateien in jedem der Unterordner erstellt oder aktualisiert wurden.
Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include backupstate.txt | Select-Object Fullname,CreationTime,LastWriteTime,Length
Der folgende Screenshot zeigt die Ausgabe des Skripts, die alle BackupState.txt
Dateien anzeigt, die in den einzelnen Unterverzeichnissen gefunden wurden.
Beispiel 2: Lesen des Inhalts jeder Textdatei in Unterverzeichnissen
Um die Verwendung von PowerShell foreach in einem Verzeichnis zu demonstrieren, liest das folgende Skript jede BackupState.txt
Datei, die in Beispiel 1 erstellt wurde.
- Rekursiv alle
BackupState.txt
Dateien in jedem Unterverzeichnis finden. - Lesen Sie mithilfe der
foreach
-Anweisung jede Textdatei, um den Wert für die „letzte Sicherungszeit“ zu erhalten. - Zeigen Sie das Ergebnis auf dem Bildschirm an.
## 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))}
Wenn das Skript in Ihrer PowerShell-Sitzung ausgeführt wird, sollten Sie eine ähnliche Ausgabe wie im folgenden Screenshot sehen. Dies zeigt, dass PowerShell eine Schleife durch Dateien durchläuft, den Inhalt liest und die Ausgabe anzeigt.
Beispiel 3: Abrufen von Diensten und Starten mit dem ForEach-Object CmdLet
Systemadministratoren müssen oft den Status von Diensten abfragen und entweder einen manuellen oder automatischen Workflow auslösen, um ausgefallene Dienste zu beheben. Schauen wir uns das Beispielskript mit dem Cmdlet ForEach-Object
an.
Für dieses Beispiel führt das folgende Skript Folgendes aus:
- Abrufen einer Liste von Diensten, die für einen automatischen Start konfiguriert sind, sich aber derzeit nicht in einem laufenden Zustand befinden.
- Als Nächstes werden die Elemente in der Liste an das Cmdlet
ForEach-Object
übergeben, um zu versuchen, jeden Dienst zu starten. - Abhängig vom Ergebnis des
Start-Service
-Befehls wird entweder eine Erfolgsmeldung oder eine Fehlermeldung angezeigt.
## 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)" }}
Wenn das Skript ausgeführt wird, sieht es so aus wie im folgenden Screenshot. Wie Sie sehen können, wurde für jeden Dienst ein Startbefehl ausgegeben. Einige wurden erfolgreich gestartet, während einige von ihnen nicht gestartet wurden.
Beispiel 4: Lesen von Daten aus CSV mit der Methode ForEach()
Die Verwendung von Daten aus CSV-Dateien ist bei Systemadministratoren sehr beliebt. Das Einfügen von Datensätzen in eine CSV-Datei erleichtert die Durchführung von Massenoperationen mit der Kombination Import-CSV
und ForEach
. Diese Kombination wird häufig zum Anlegen mehrerer Benutzer im Active Directory verwendet.
In diesem nächsten Beispiel wird davon ausgegangen, dass Sie eine CSV-Datei mit zwei Spalten – Vorname und Nachname – haben. Diese CSV-Datei soll dann mit den Namen der neu anzulegenden Benutzer befüllt werden. Die CSV-Datei würde in etwa wie unten aussehen.
"Firstname","Lastname""Grimm","Reaper""Hell","Boy""Rick","Rude"
Nun zum nächsten Skriptblock. Zunächst importieren Sie die CSV-Datei, indem Sie den Inhaltspfad an das Cmdlet Import-CSV
übergeben. Anschließend durchlaufen Sie mit der Methode foreach()
die Namen in einer Schleife und erstellen die neuen Benutzer im 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 } })
Nach der Ausführung sollten Sie nun für jede Zeile in der CSV-Datei einen AD-Benutzer erstellt haben!
Zusammenfassung
Die Logik hinter der foreach-Schleife in PowerShell unterscheidet sich nicht von der anderer Programmiersprachen. Sie ändert sich nur in der Art und Weise, wie sie verwendet wird und welche Variante der foreach-Schleife für eine bestimmte Aufgabe gewählt wird.
In diesem Artikel haben Sie die verschiedenen Typen von foreach-Schleifen kennengelernt, die in PowerShell verfügbar sind, und was Sie bei der Auswahl einer Schleife beachten sollten. Außerdem haben Sie die drei Typen von foreach-Schleifen anhand verschiedener Beispielszenarien in Aktion gesehen.
Weiterführende Literatur
- Verwalten von CSV-Dateien in PowerShell mit Import-Csv (foreach-Schleife)
- New-ADUser: Erstellen von Active Directory-Benutzern mit PowerShell
- Grundlagen von PowerShell-Schleifen: Foreach | Scripting Blog