PowerShell ForEach-Schleifen: Arten und bewährte Methoden

Wenn Sie zum ersten Mal PowerShell-Skripte schreiben, werden Sie unweigerlich an einen Punkt kommen, an dem Sie mehrere Elemente aus einer Sammlung verarbeiten müssen. Dann müssen Sie sich mit PowerShell-Foreach-Schleifen befassen und herausfinden, worum es dabei geht.

Fast alle Programmiersprachen haben eine Konstruktion namens Schleifen; PowerShell ist da nicht anders. Eine der beliebtesten Arten von Schleifen in PowerShell ist die foreach-Schleife. Im einfachsten Fall liest eine foreach-Schleife eine gesamte Sammlung von Elementen und für jedes Element wird Code ausgeführt.

Einer der verwirrendsten Aspekte der PowerShell foreach-Schleife für Anfänger sind all die Optionen, die Sie haben. Es gibt nicht nur eine Möglichkeit, jedes Element in einer Sammlung zu verarbeiten; es gibt drei!

In diesem Artikel erfahren Sie, wie jede Art von foreach-Schleife funktioniert und wann Sie eine gegenüber der anderen verwenden sollten. Wenn Sie diesen Artikel gelesen haben, haben Sie ein gutes Verständnis für jede Art von foreach-Schleife.

Grundlagen der PowerShell-Foreach-Schleife

Eine der häufigsten Arten von Schleifen, die Sie in PowerShell verwenden werden, ist die foreach-Schleife. Eine foreach-Schleife liest eine Menge von Objekten (iteriert) und wird abgeschlossen, wenn sie mit dem letzten Element fertig ist. Die gelesene Sammlung von Objekten wird in der Regel durch ein Array oder eine Hashtable dargestellt.

HINWEIS: Der Begriff „Iteration“ ist ein Programmierbegriff, der auf jeden Durchlauf einer Schleife verweist. Jedes Mal, wenn eine Schleife einen Zyklus abschließt, spricht man von einer Iteration. Das Ausführen einer Schleife über eine Menge von Objekten wird häufig als Iteration über die Menge bezeichnet.

Vielleicht müssen Sie eine Textdatei über mehrere auf dem Dateisystem verteilte Ordner erstellen. Angenommen, die Ordnerpfade lauten C:\Ordner, C:\Program Files\Ordner2 und C:\Ordner3. Ohne eine 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 Wert von Path. Der Wert für Path ändert sich bei jeder dieser Referenzen.

Sie duplizieren viel Code. Sie verschwenden Zeit beim Tippen und setzen sich für Probleme in der Zukunft aus. Stattdessen sollten Sie eine „Gruppe“ erstellen, die nur die Elemente enthält, die sich ändern. In diesem Beispiel verwenden wir ein Array.

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

Jetzt haben Sie jeden dieser Pfade in einer einzigen „Gruppe“ oder einem Array gespeichert. Sie sind jetzt 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 anzusprechen, das Anfänger in PowerShell normalerweise durcheinander bringt. Im Gegensatz zu anderen Arten von Schleifen sind foreach-Schleifen nicht alle gleich.

Es gibt technisch gesehen drei Arten von foreach-Schleifen in PowerShell. Obwohl jede ähnlich verwendet wird, ist es wichtig, den Unterschied zu verstehen.

Die foreach-Anweisung

Die erste Art von 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).

Mit dem obigen Beispiel repräsentiert die Variable $i den Iterator oder den Wert jedes Elements in $path, während es über jedes Element im Array iteriert.

Beachten Sie, dass die Iterator-Variable nicht unbedingt $i sein muss. Der Variablenname kann beliebig sein.

Im folgenden Beispiel können Sie die gleiche Aufgabe wie das wiederholte Hinzufügen der Add-Content-Referenz wie folgt erledigen:

# Erstellen Sie ein Array von Ordnern
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

# Führen Sie eine Iteration durch, um die gleiche Datei in jedem Ordner zu erstellen
foreach ($i in $folders) {
    Add-Content -Path "$i\SampleFile.txt" -Value "This is the content of the file"
}

Die foreach-Anweisung ist bekanntlich eine schnellere Alternative zur Verwendung des ForEach-Object-Cmdlets.

Das ForEach-Object-CmdLet

Wenn 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 verwendet werden kann. Wie die foreach-Anweisung kann das ForEach-Object-Cmdlet über eine Reihe von Objekten iterieren. Diesmal übergibt es diese Objektmenge und die Aktion, die mit jedem Objekt durchgefü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 zu verwirren, hat das ForEach-Object-Cmdlet einen Alias ​​namens foreach. Je nachdem, wie der Begriff „foreach“ aufgerufen wird, wird entweder die foreach-Anweisung ausgeführt oder das ForEach-Object wird ausgeführt.

A good way to differentiate these situations is to notice if the term “foreach” is followed by ($someVariable in $someSet). Otherwise, in any other context, the code author is probably using the alias for ForEach-Object.

Die foreach()-Methode

Einer der neuesten foreach-Schleifen wurde in PowerShell v4 eingeführt und wird als foreach()-Methode bezeichnet. Diese Methode existiert auf einem Array- oder Sammlungsobjekt. Die foreach()-Methode verfügt über einen Standard-Skriptblockparameter, der die Aktionen enthält, die bei jeder Iteration ausgeführt werden sollen, genauso wie bei den anderen Schleifenarten.

$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 bedeutendste Unterschied bei der foreach()-Methode liegt in ihrer internen Funktionsweise.

Die Verwendung der foreach()-Methode ist deutlich schneller und zeigt dies insbesondere bei großen Datenmengen. Es wird empfohlen, diese Methode zu verwenden, wenn möglich.

Mini-Projekt: Durchlaufen einer Liste von Servernamen

Eine der häufigsten Verwendungen einer Schleife in PowerShell besteht darin, eine Liste von Servern aus einer bestimmten Quelle zu lesen und für jeden von ihnen eine Aktion auszuführen. Für unser erstes Mini-Projekt wollen wir Code erstellen, der es uns ermöglicht, Servernamen (einer pro Zeile) aus einer Textdatei zu lesen und jeden Server anzupingen, um festzustellen, ob er online ist oder nicht.

List of server names in a text file

Für diesen Ablauf, welche Art von Schleife denkst du, würde am besten funktionieren?

Beachte, dass ich das Wort „Liste“ erwähnt habe, wie in „Liste von Servern“. Das ist bereits ein Hinweis. Du hast bereits eine bestimmte Anzahl von Servernamen und möchtest eine Aktion an jedem von ihnen ausführen. Das klingt nach einer großartigen Gelegenheit, die gängigste PowerShell-Schleife auszuprobieren – die foreach-Schleife.

Die erste Aufgabe besteht darin, eine Gruppe von Servernamen im Code zu erstellen. Die häufigste Methode dafür ist das Erstellen eines Arrays. Glücklicherweise gibt Get-Content standardmäßig ein Array zurück, wobei jedes Element im Array durch eine einzelne Zeile in der Textdatei dargestellt wird.

Erstellen Sie zunächst ein Array mit allen meinen Servernamen und nennen Sie es $servers.

$servers = Get-Content .\servers.txt

Jetzt, da Sie das Array erstellt haben, müssen Sie überprüfen, ob jeder Server online ist oder nicht. Ein großartiges Cmdlet zum Testen der Verbindung zu einem Server heißt Test-Connection. Dieses Cmdlet führt einige Verbindungstests auf einem Computer durch, um festzustellen, ob er online ist oder nicht.

Indem Sie Test-Connection innerhalb einer foreach-Schleife ausführen, um jede Zeile der Datei zu lesen, können Sie jeden Servernamen, der durch die Variable $server dargestellt wird, an Test-Connection übergeben. So kann PowerShell eine Textdatei durchlaufen. Ein Beispiel dafür finden Sie unten.

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

Wenn die foreach-Schleife ausgeführt wird, sieht sie etwa so aus:

Looping Test-Connection through a list of server names

Sie haben nun erfolgreich die Verbindung zu einer Textdatei voller Servernamen getestet! An diesem Punkt können Sie Servernamen in der Textdatei nach Belieben hinzufügen oder entfernen, ohne den Code ändern zu müssen.

Sie haben erfolgreich in die Praxis umgesetzt, was wir gepredigt haben, die DRY-Methode.

PowerShell-Beispiele für ForEach

Lassen Sie uns einige Beispiele dafür betrachten, wie die ForEach-Schleife verwendet werden kann. 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 Hilfe der ForEach-Anweisung

Dieses Beispiel zeigt die häufige Verwendung von PowerShell für jeden Ordner in einem Verzeichnis.

Angenommen, es gibt zehn Unterordner im Ordner C:\ARCHIVE_VOLUMES. Jeder Unterordner repräsentiert ein Archivvolumen, das täglich gesichert wird. Nachdem jede Sicherung abgeschlossen ist, wird eine Datei mit dem Namen BackupState.txt im Inneren jedes Ordners erstellt, die das Datum der Sicherung enthält.

Ein Beispiel dafür, wie dies aussehen könnte, finden Sie unten.

Sub-directories under C:\ARCHIVE_VOLUMES

Das folgende Skript führt drei Aktionen aus:

  • erhält die Liste aller Unterordner im Inneren von C:\ARCHIVE_VOLUMES
  • durchläuft jeden Ordner
  • erstellt eine Textdatei mit dem Namen BackupState.txt, die das aktuelle Datum und die Uhrzeit als Wert enthält

Das untenstehende Beispiel verwendet die foreach-Anweisung.

# Definieren Sie den TOP-Level-Ordner
$TOP_FOLDER = "C:\ARCHIVE_VOLUMES"

# Alle Unterordner rekursiv abrufen
$Child_Folders = Get-ChildItem -Path $TOP_FOLDER -Recurse | Where-Object { $_.PSIsContainer -eq $true }

# In jedem Unterordner eine Textdatei erstellen und das aktuelle Datum/Uhrzeit als Wert hinzufügen.
foreach ($foldername in $Child_Folders.FullName) {
   (get-date -Format G) | Out-File -FilePath "$($foldername)\BackupState.txt" -Force
}

Mit dem Get-ChildItem-Cmdlet 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 Screenshot unten zeigt die Ausgabe des Skripts, das alle gefundenen Dateien „BackupState.txt“ in jedem Unterverzeichnis anzeigt.

A text file is created in each sub-directory

Beispiel 2: Lesen des Inhalts jeder Textdatei in Unterverzeichnissen

Anschließend wird das Skript unten verwendet, um die Verwendung von PowerShell für jede Datei in einem Verzeichnis zu demonstrieren. Es liest jede erstellte Datei „BackupState.txt“ in Beispiel 1.

  • Rekursiv alle Dateien „BackupState.txt“ in jedem Unterverzeichnis finden.
  • Mit der Anweisung „foreach“ wird jede Textdatei gelesen, um den Wert „letzter Sicherungszeitpunkt“ zu erhalten.
  • Das Ergebnis wird auf dem Bildschirm angezeigt.
## Alle BackupState.txt-Dateien in C:\ARCHIVE_VOLUMES finden
$files = Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include 'BackupState.txt' | Select-Object DirectoryName,FullName

## Den Inhalt jeder Datei lesen
foreach ($file in $files) {
    Write-Output ("$($file.DirectoryName) last backup time - " + (Get-Content $file.FullName))
}

Nachdem das Skript in Ihrer PowerShell-Sitzung ausgeführt wurde, sollten Sie eine ähnliche Ausgabe wie auf dem Screenshot sehen. Dies zeigt, dass PowerShell Schleifen durch Dateien durchführt, den Inhalt liest und die Ausgabe anzeigt.

Using foreach to loop through files and read its contents.

Beispiel 3: Abrufen von Diensten und Starten mit dem Cmdlet „ForEach-Object“

Systemadministratoren müssen häufig den Status von Diensten abrufen und entweder einen manuellen oder automatischen Workflow auslösen, um fehlgeschlagene Dienste zu beheben. Schauen wir uns das Beispielskript mit dem Cmdlet „ForEach-Object“ an.

In diesem Beispiel führt das Skript unten folgende Schritte aus:

  • Eine Liste der Dienste abrufen, die für einen automatischen Start konfiguriert sind, aber derzeit nicht ausgeführt werden.
  • Als nächstes werden die Elemente in der Liste an das ForEach-Object-Cmdlet übergeben, um zu versuchen, jeden Dienst zu starten.
  • A message of either success or failed is displayed depending on the result of the Start-Service command.
## Eine Liste der automatischen Dienste abrufen, die angehalten sind.
$services = Get-Service | Where-Object {$.StartType -eq 'Automatic' -and $.Status -ne 'Running'}

## Jedes Dienstobjekt an die Pipeline übergeben und mit dem Foreach-Object-Cmdlet verarbeiten
$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 die Ausgabe wie auf dem untenstehenden Screenshot aus. Wie Sie sehen können, wurde jedem Dienst ein Startbefehl erteilt. Einige wurden erfolgreich gestartet, während andere fehlgeschlagen sind.

Using the ForEach-Object loop to start services

Beispiel 4: Lesen von Daten aus einer CSV-Datei mit der Methode ForEach()

Die Verwendung von Daten aus CSV-Dateien ist bei Systemadministratoren beliebt. Das Speichern von Datensätzen in einer CSV-Datei vereinfacht das Ausführen von Massenoperationen mit der Kombination aus Import-CSV und ForEach. Diese Kombination wird häufig für das Erstellen mehrerer Benutzer im Active Directory verwendet.

In diesem nächsten Beispiel wird angenommen, dass Sie eine CSV-Datei mit zwei Spalten haben – Vorname und Nachname. Diese CSV-Datei sollte dann mit den Namen der neuen Benutzer, die erstellt werden sollen, gefüllt werden. Die CSV-Datei würde ungefähr so aussehen:

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

Jetzt zum nächsten Skriptblock. Importieren Sie zunächst die CSV-Datei, indem Sie den Pfad zum Inhalt an das Import-CSV-Cmdlet übergeben. Verwenden Sie dann die foreach()-Methode, um durch die Namen zu iterieren und neue Benutzer im Active Directory zu erstellen.

# Importiere Liste von Vornamen und Nachnamen aus CSV-Datei
$newUsers = Import-Csv -Path .\Employees.csv

Add-Type -AssemblyName System.Web

# Verarbeite die Liste
$newUsers.foreach(
    {
        # Generiere ein zufälliges Passwort
        $password = [System.Web.Security.Membership]::GeneratePassword((Get-Random -Minimum 20 -Maximum 32), 3)
        $secPw = ConvertTo-SecureString -String $password -AsPlainText -Force

        # Formuliere einen Benutzernamen
        $userName = '{0}{1}' -f $_.FirstName.Substring(0, 1), $_.LastName

        # Baue neue Benutzerattribute
        $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
        }
    }
)

Wenn Sie dies ausführen, 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 Bezug darauf, wie sie verwendet wird und welche Variation der foreach-Schleife für eine bestimmte Aufgabe gewählt wird.

In diesem Artikel haben Sie die verschiedenen Arten von foreach-Schleifen in PowerShell kennengelernt und was bei der Entscheidung, welche verwendet werden soll, zu beachten ist. Sie haben auch die drei Arten von foreach-Schleifen in Aktion gesehen, die verschiedene Beispielszenarien verwenden.

Weiterführende Informationen

Source:
https://adamtheautomator.com/powershell-foreach/