about_ForEach

Letzte Aktualisierung: Mai 2014

Betrifft: Windows PowerShell 2.0, Windows PowerShell 3.0, Windows PowerShell 4.0, Windows PowerShell 5.0

THEMA

about_Foreach

KURZE BESCHREIBUNG

Beschreibt einen Programmiersprachenbefehl, mit dem Sie die einzelnen Elemente einer Datengruppe durchlaufen.

LANGE BESCHREIBUNG

Die Foreach-Anweisung (auch als Foreach-Schleife bezeichnet) ist ein Sprachkonstrukt zum schrittweisen Durchlaufen (Iterieren) einer Reihe von Werten in einer Datengruppe mit mehreren Elementen.

Die einfachste und am häufigsten durchlaufene Datengruppe ist ein Array. In einer Foreach-Schleife werden in der Regel ein oder mehrere Befehle an jedem Element eines Arrays ausgeführt.

Syntax

Das folgende Beispiel zeigt die Foreach-Syntax:

          foreach ($<item> in $<collection>){<statement list>}

Vereinfachte Syntax

Ab Windows PowerShell® 3.0 wurde die Syntax für programmiersprachenspezifische Schlüsselwörter wie Where und Foreach vereinfacht. Vergleichsoperatoren für die Elemente einer Datengruppe werden als Parameter behandelt. Sie können an den Membern einer Datengruppe eine Methode ausführen, ohne sie in einem Skriptblock einzuschließen oder die automatische Variable "$_." hinzuzufügen. Sehen Sie sich hierzu die folgenden beiden Beispiele an:

          dir cert:\ -Recurse | foreach GetKeyAlgorithm
          dir cert:\ -Recurse | foreach {$_.GetKeyAlgorithm()}

Beide Befehle funktionieren, jedoch gibt nur der erste Befehl Ergebnisse auch ohne einen Skriptblock oder die automatische Variable $_ zurück. Die Methode GetKeyAlgorithm wird als Parameter für Foreach behandelt. Der erste Befehl gibt die gleichen Ergebnisse zurück, jedoch ohne Fehler, da die vereinfachte Syntax nicht versucht, für Elemente, auf die das angegebene Argument nicht zutrifft, Ergebnisse zurückzugeben.

In diesem Beispiel wird die Get-Process-Eigenschaft Description als Parameterargument der Foreach-Anweisung übergeben. Das Ergebnis sind die Beschreibungen der aktiven Prozesse.

          Get-Process | ForEach Description

Foreach-Anweisung außerhalb einer Befehlspipeline

Der in Klammern stehende Teil der Foreach-Anweisung ist eine Variable und die zu iterierende Datengruppe. Windows PowerShell erstellt die Variable ($<item>) automatisch bei der Ausführung der Foreach-Schleife. Vor jeder Iteration der Schleife wird die Variable auf einen anderen Wert der Datengruppe gesetzt. Der Block nach der Foreach-Anweisung {<statement list>} enthält eine Reihe von Befehlen, die an jedem Element der Datengruppe ausgeführt werden.

Beispiele

Die Foreach-Schleife des folgenden Beispiels zeigt z. B. die Werte des Arrays $letterArray an.

        
          $letterArray = "a","b","c","d"
          foreach ($letter in $letterArray)
          {
              Write-Host $letter
          }

In diesem Beispiel wird das $letterArray-Array mit den Zeichenfolgewerten "a", "b", "c" und "d" erstellt und initialisiert. Bei der ersten Ausführung der Foreach-Anweisung wird die Variable $letter auf den Wert des ersten Elements von $letterArray ("a") gesetzt. Mithilfe des Cmdlets Write-Host zeigt die Anweisung dann den Buchstaben "a" an. Bei der nächsten Iteration der Schleife wird $letter auf "b" gesetzt usw. Nachdem die Foreach-Schleife schließlich den Buchstaben "d" angezeigt hat, beendet Windows PowerShell die Schleife.

Die gesamte Foreach-Anweisung muss in einer Zeile stehen, damit sie als Befehl an der Eingabeaufforderung von Windows PowerShell ausgeführt werden kann. In einer .ps1-Skriptdatei braucht die Foreach-Anweisung dagegen nicht in einer Zeile zu stehen.

Foreach-Anweisungen können auch mit Cmdlets verwendet werden, die eine Datengruppe mit Elementen zurückgeben. Im folgenden Beispiel iteriert die Foreach-Anweisung durch die vom Cmdlet Get-ChildItem zurückgegebene Elementliste.

          foreach ($file in Get-ChildItem)
          {
              Write-Host $file
          }

Dieses Beispiel können Sie durch eine If-Anweisung erweitern, mit der Sie die zurückgegebenen Ergebnisse einschränken. Im folgenden Beispiel führt die Foreach-Anweisung den gleichen iterierenden Vorgang wie im vorherigen Beispiel durch, allerdings begrenzt eine If-Anweisung hier die Ergebnisse auf Dateien mit mehr als 100 Kilobyte (KB):

          foreach ($file in Get-ChildItem)
          {
              if ($file.length -gt 100KB) 
              {
                  Write-Host $file
              }
          }

In diesem Beispiel verwendet die Foreach-Schleife eine Eigenschaft der Variablen $file zur Durchführung einer Vergleichsoperation ($file.length -gt 100KB). Die Variable $file enthält alle Eigenschaften des durch das Cmdlet Get-ChildItem zurückgegebenen Objekts. Daher können Sie mehr als nur den Dateinamen zurückgeben. Im nächsten Beispiel gibt Windows PowerShell die Länge und den Zeitpunkt des letzten Zugriffs innerhalb der Anweisungsliste zurück:

          foreach ($file in Get-ChildItem)
          {
              if ($file.length -gt 100KB) 
              {
                  Write-Host $file
                  Write-Host $file.length
                  Write-Host $file.lastaccesstime
              }
          }

Dieses Beispiel beschränkt Sie nicht auf die Ausführung eines einzelnen Befehls innerhalb einer Anweisungsliste.

Sie können auch eine Variable außerhalb einer Foreach-Schleife verwenden und diese innerhalb der Schleife inkrementieren. Im folgenden Beispiel zählen nur Dateien mit einer Größe von mehr als 100 KB:

          $i = 0
          foreach ($file in Get-ChildItem)
          {
              if ($file.length -gt 100KB) 
              {
                  Write-Host $file "file size:" ($file.length / 
          1024).ToString("F0") KB
                  $i = $i + 1
              }
          }
          if ($i -ne 0)
          {
              Write-Host
              Write-Host $i " file(s) over 100 KB in the current 
          directory."}
          else 
          {
              Write-Host "No files greater than 100 KB in the current 
          directory."
          }

Im vorherigen Beispiel ist die Variable $i außerhalb der Schleife auf 0 gesetzt; innerhalb der Schleife wird die Variable für jede Datei mit mehr als 100 KB inkrementiert. Nach Beendigung der Schleife wertet eine If-Anweisung den Wert von $i aus, um die Anzahl der Dateien über 100 KB zurückzugeben. Eventuell wird auch die Meldung zurückgegeben, dass keine Dateien mit mehr als 100 KB gefunden wurden.

Das vorherige Beispiel veranschaulicht auch, wie die Dateilängenergebnisse formatiert werden:

          ($file.length / 1024).ToString("F0")

Der Wert wird durch 1.024 geteilt, um das Ergebnis in Kilobyte (nicht in Byte) anzuzeigen; dieser Wert wird dann mit dem Bezeichner für das Festkommaformat formatiert, damit das Ergebnis ohne Dezimalwerte zurückgegeben wird. Die 0 für den Formatbezeichner legt fest, dass keine Dezimalstellen angezeigt werden.

Foreach-Anweisung innerhalb einer Befehlspipeline

Wenn sich Foreach in einer Befehlspipeline befindet, verwendet Windows PowerShell den Foreach-Alias, der den Befehl ForEach-Object aufruft. Bei Verwendung des Foreach-Alias in einer Befehlspipeline fügen Sie nicht wie bei einer Foreach-Anweisung die Syntax ($<item> in $<collection>) ein. Die durch diese Syntax bereitgestellten Informationen werden in der Pipeline durch den vorangegangenen Befehl bereitgestellt. Die Syntax des Foreach-Alias in einer Befehlspipeline sieht wie folgt aus:

          <command> | foreach {<command_block>}

Die Foreach-Schleife des folgenden Befehls zeigt beispielsweise Prozesse an, deren Arbeitssatz (Speicherauslastung) größer als 20 Megabyte (MB) ist.

Der Get-Process-Befehl ruft alle Prozesse auf dem Computer ab. Der Foreach-Alias führt die Befehle des Skriptblocks der Reihe nach für alle Prozesse aus.

Die If-Anweisung wählt Prozesse mit einem Arbeitssatz größer als 20 MB aus. Das Cmdlet Write-Host schreibt den Namen des Prozesses gefolgt von einem Doppelpunkt. Danach dividiert der Befehl den Wert des Arbeitssatzes, der in Byte angegeben ist, durch 1 MB, um den Wert des Arbeitssatzes in Megabyte anzugeben. Anschließend konvertiert er den Double-Wert des Ergebnisses in eine Zeichenfolge. Schließlich zeigt er den Wert als eine Festkommazahl mit 0 Dezimalstellen (F0), einem Leerzeichen als Trennzeichen (" ") und der Angabe "MB" an.

          Write-Host "Processes with working sets greater than 20 MB."
          Get-Process | foreach { 
             if ($_.WS -gt 20MB)
             { Write-Host $_.name ": " ($_.WS/1MB).ToString("F0") MB -Separator ""}
          }

Der Foreach-Alias unterstützt auch einleitende, mittlere und abschließende Befehlsblöcke. Einleitende und abschließende Befehlsblöcke werden nur einmal ausgeführt, mittlere Befehlsblöcke hingegen bei jeder Iteration der Schleife durch eine Datengruppe oder ein Array.

Die Syntax des Foreach-Alias in einer Befehlspipeline mit einem einleitenden, einem mittleren und einem abschließenden Befehlsblocksatz lautet wie folgt:

          <command> | foreach {<beginning command_block>}{<middle 
          command_block>}{<ending command_block>}

Das folgende Beispiel veranschaulicht die Verwendung von einleitenden, mittleren und abschließenden Befehlsblöcken.

          Get-ChildItem | foreach {
          $fileCount = $directoryCount = 0}{
          if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}{
          "$directoryCount directories and $fileCount files"}

Der einleitende Befehlsblock erstellt zwei Variablen und initialisiert sie mit 0:

          {$fileCount = $directoryCount = 0}

Der mittlere Befehlsblock ermittelt für jedes von Get-ChildItem zurückgegebene Element, ob es sich um ein Verzeichnis oder um eine Datei handelt:

          {if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}

Wenn das zurückgegebene Element ein Verzeichnis ist, wird die Variable $directoryCount um 1 erhöht. Wenn das Element kein Verzeichnis ist, wird die Variable $fileCount um 1 erhöht. Der abschließende Befehlsblock beginnt erst, wenn der mittlere Block die Schleife abgeschlossen hat. Er gibt dann die Ergebnisse des Vorgangs zurück:

          {"$directoryCount directories and $fileCount files"}

Dieses Beispiel für eine Blockstruktur mit dem Pipeline-Operator können Sie wie folgt auch ändern, um alle Dateien zu suchen, die größer als 100 KB sind:

          Get-ChildItem | foreach{
              $i = 0}{
              if ($_.length -gt 100KB)
              {
                  Write-Host $_.name "file size:" ($_.length / 
          1024).ToString("F0") KB
                  $i++
              }
              }{
              if ($i -ne 0)
              {
                  Write-Host
                  Write-Host "$i file(s) over 100 KB in the current 
          directory."
              }
              else 
              {
              Write-Host "No files greater than 100 KB in the current 
          directory."}
              }

Das folgende Beispiel, eine Funktion, die die in Skripts und Skriptmodulen verwendeten Funktionen zurückgibt, veranschaulicht die Verwendung der Methode MoveNext (die ähnlich funktioniert wie "skip X" in einer For-Schleife) mit der Eigenschaft Current der Variablen $foreach in einem Foreach-Skriptblock, selbst wenn der Funktionsname durch ungewöhnliche Funktionsdefinitionen (bzw. durch Funktionsdefinitionen mit uneinheitlichen Abständen) deklariert wird. Das Beispiel funktioniert auch, wenn die in einem Skript oder Skriptmodul verwendeten Funktionen Kommentare enthalten.

         function Get-FunctionPosition {
             [CmdletBinding()]
             [OutputType('FunctionPosition')]
             param(
                 [Parameter(Position=0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
                 [ValidateNotNullOrEmpty()]
                 [Alias('PSPath')]
                 [System.String[]]
                 $Path
             )
             process {
                 try {
                     $filesToProcess = if ($_ -is [System.IO.FileSystemInfo]) {
                         $_
                     } else {
                         Get-Item -Path $Path
                     }
                     foreach ($item in $filesToProcess) {
                         if ($item.PSIsContainer -or $item.Extension -notin @('.ps1','.psm1')) {
                             continue
                         }
                         $tokens = $errors = $null
                         $ast = [System.Management.Automation.Language.Parser]::ParseFile($item.FullName,([REF]$tokens),([REF]$errors))
                         if ($errors) {
                             Write-Warning "File '$($item.FullName)' has $($errors.Count) parser errors."
                         }
                         :tokenLoop foreach ($token in $tokens) {
                             if ($token.Kind -ne 'Function') {
                                 continue
                             }
                             $position = $token.Extent.StartLineNumber
                             do {
                                 if (-not $foreach.MoveNext()) {
                                     break tokenLoop
                                 }
                                 $token = $foreach.Current
                             } until ($token.Kind -in @('Generic','Identifier'))
                             $functionPosition = [pscustomobject]@{
                                       Name = $token.Text
                                 LineNumber = $position
                                       Path = $item.FullName
                             }
                             Add-Member -InputObject $functionPosition -TypeName FunctionPosition -PassThru
                         }
                     }
                 } catch {
                     throw
                 }
             }
         }

SIEHE AUCH

about_Automatic_Variables

about_If

Foreach-Objekt