Die PowerShell-Pipeline ist eine der wichtigsten (und nützlichsten) Funktionen der PowerShell-Shell und der Skriptsprache. Sobald Sie die Grundlagen verstanden haben und wissen, wozu sie in der Lage ist, können Sie ihre Leistungsfähigkeit in Ihren eigenen Funktionen nutzen. In diesem Tutorial werden Sie genau das tun!
Die PowerShell-Pipeline ermöglicht es Ihnen, Befehle zu einer einzigen „Pipeline“ zusammenzufügen, was den Code vereinfacht, parallele Verarbeitung ermöglicht und vieles mehr. Wenn Sie bereit sind, mehr über die Pipeline zu erfahren und Ihre eigenen Funktionen zu erstellen, um die Pipeline zu nutzen, dann legen wir los!
Voraussetzungen
In diesem Beitrag handelt es sich um ein Tutorial mit praktischen Demonstrationen. Wenn Sie mitmachen möchten, benötigen Sie PowerShell v3+. In diesem Tutorial wird Windows PowerShell v5.1 verwendet.Verständnis der PowerShell-Pipeline
Die meisten PowerShell-Befehle erhalten Eingaben über einen Parameter. Der Befehl erhält eine Eingabe in Form eines Objekts und führt dann eine Aktion damit aus. Optional gibt er dann ein Ergebnisobjekt aus.
Befehle in einer Pipeline verhalten sich wie menschliche Läufer in einem Staffellauf. Jeder Läufer im Rennen, außer dem ersten und dem letzten, nimmt den Staffelstab (Objekte) von seinem Vorgänger entgegen und gibt ihn an den nächsten weiter.
Zum Beispiel hat das Stop-Service
-Cmdlet einen Parameter namens InputObject
. Dieser Parameter ermöglicht es Ihnen, ein bestimmtes Objekt an Stop-Service
zu übergeben, das den Windows-Dienst darstellt, den Sie stoppen möchten.
Um den Parameter InputObject
zu verwenden, könnten Sie das Dienstobjekt über Get-Service
abrufen und dann das Objekt an den Parameter InputObject
übergeben, wie unten gezeigt. Diese Methode, Eingaben an das Stop-Service
-Cmdlet über den Parameter InputObject
zu übergeben, funktioniert hervorragend und erledigt den Job.
Diese Methode der Eingabe für das Stop-Service
-Befehl erfordert zwei unterschiedliche Schritte. PowerShell muss zuerst Get-Service
ausführen, die Ausgabe in einer Variablen speichern und dann diesen Wert über den Parameter InputObject
an Stop-Service
übergeben.
Jetzt, vergleichen Sie das obige Snippet mit dem untenstehenden Snippet, das dasselbe tut. Es ist viel einfacher, weil Sie keine $services
-Variable erstellen oder überhaupt den InputObject
-Parameter verwenden müssen. Stattdessen „weiß“ PowerShell, dass Sie beabsichtigen, den InputObject
-Parameter zu verwenden. Es macht dies über ein Konzept namens Parameterbindung.
Sie haben jetzt Befehle mit dem |
-Operator „verkettet“. Sie haben eine Pipeline erstellt.
Aber Sie müssen nicht nur zwei Befehle verwenden, um eine Pipeline zu erstellen; Sie können so viele Befehle miteinander verketten, wie Sie möchten (wenn die Befehlsparameter dies unterstützen). Zum Beispiel der folgende Code-Abschnitt:
- Übergibt alle Objekte, die der
Get-Service
-Cmdlet zurückgibt, an dasWhere-Object
-Cmdlet. - Das
Where-Object
-Cmdlet betrachtet dann dieStatus
-Eigenschaft jedes Objekts und gibt nur diejenigen Objekte mit dem WertRunning
zurück. - Dann werden diese Objekte jeweils an
Select-Object
gesendet, das nur dieName
– undDisplayName
-Eigenschaften der Objekte zurückgibt. - Da kein anderes Cmdlet die von
Select-Object
ausgegebenen Objekte akzeptiert, gibt der Befehl die Objekte direkt an die Konsole zurück.
Die
Where-Object
– undSelect-Object
-Cmdlets wissen, wie sie die Pipelineeingabe durch ein Konzept namens Parameterbindung verarbeiten können, das im nächsten Abschnitt erläutert wird.
Für weitere Informationen zu Pipelines führen Sie den Befehl
Get-Help about_pipelines
aus.
Pipeline-Parameterbindung
Auf den ersten Blick mag die Pipeline trivial erscheinen. Immerhin gibt sie einfach Objekte von einem Befehl an einen anderen weiter. Aber in Wirklichkeit ist die Pipeline viel komplizierter. Befehle akzeptieren nur Eingaben über Parameter. Die Pipeline muss irgendwie herausfinden, welchen Parameter sie verwenden soll, auch wenn Sie ihn nicht explizit definieren.
Die Aufgabe, herauszufinden, welcher Parameter zu verwenden ist, wenn eine Befehlseingabe über die Pipeline erfolgt, wird als Parameterbindung bezeichnet. Um ein Objekt erfolgreich an einen Parameter zu binden, müssen die Parameter des eingehenden Befehls dies unterstützen. Befehlsparameter unterstützen die Parameterbindung über die Pipeline auf zwei Arten: ByValue
und/oder ByPropertyName
.
ByValue
Der Befehlsparameter akzeptiert das gesamte eingehende Objekt als Parameterwert. Ein ByValue
-Parameter sucht nach einem Objekt eines bestimmten Typs in den eingehenden Objekten. Wenn dieser Objekttyp übereinstimmt, geht PowerShell davon aus, dass das Objekt an diesen Parameter gebunden werden soll, und akzeptiert es.
Der
Get-ChildItem
-Cmdlet hat einen Parameter namensPath
, der einen Zeichenfolgenobjekttyp akzeptiert und die Pipeline-Eingabe überByValue
ermöglicht. Daher gibt beispielsweise'C:\Windows' | Get-ChildItem
alle Dateien im Verzeichnis C:\Windows zurück, daC:\Windows
eine Zeichenfolge ist.
ByPropertyName
Der Befehlsparameter akzeptiert kein gesamtes Objekt, sondern eine einzelne Eigenschaft dieses Objekts. Dies geschieht nicht durch Betrachten des Objekttyps, sondern des Eigenschaftsnamens.
Der
Get-Process
-Befehl verfügt über einenName
-Parameter, der so eingerichtet ist, dass er die Eingabe über die Pipeline mitByPropertyName
akzeptiert. Wenn Sie demGet-Process
-Befehl also ein Objekt mit einemName
-Eigenschaft über die Pipeline übergeben, wie z. B.[pscustomobject]@{Name='firefox'} | Get-Process
, gleicht PowerShell dieName
-Eigenschaft des eingehenden Objekts mit demName
-Parameter ab und verwendet diesen Wert.
Entdeckung von Befehlsparametern, die die Pipeline unterstützen
Wie bereits erwähnt, unterstützt nicht jeder Befehl die Eingabe über die Pipeline. Der Befehlsautor muss diese Funktionalität während der Entwicklung erstellen. Der Befehl muss mindestens einen Parameter haben, der die Pipeline unterstützt, und zwar mit ByValue
oder ByPropertyName
.
Wie erfahren Sie, welche Befehle und ihre Parameter die Eingabe über die Pipeline unterstützen? Sie könnten es einfach durch Ausprobieren herausfinden, aber es gibt einen besseren Weg mit dem PowerShell-Hilfesystem unter Verwendung des Get-Help
-Befehls.
Zum Beispiel werfen Sie einen Blick auf den Path
-Parameter des Get-ChildItem
-Befehls unten. Sie können sehen, dass er beide Arten der Pipeline-Eingabe unterstützt.

Sobald Sie wissen, welche Befehlsparameter die Pipeline-Eingabe unterstützen, können Sie diese Funktionalität nutzen, wie unten gezeigt.
Aber andererseits unterstützt der DisplayName
-Parameter von Get-Service
keine Pipeline-Eingabe.

Erstellen Ihrer eigenen Pipeline-Funktion
Auch wenn die Standard-PowerShell-Cmdlets die Pipeline-Eingabe unterstützen, bedeutet das nicht, dass Sie diese Funktionalität nicht nutzen können. Glücklicherweise können Sie auch Funktionen erstellen, die Pipeline-Eingabe akzeptieren.
Um dies zu demonstrieren, beginnen wir mit einer vorhandenen Funktion namens Get-ConnectionStatus
.
- Diese Funktion hat einen einzigen Parameter (der keine Pipeline-Eingabe akzeptiert) namens
ComputerName
, der es Ihnen ermöglicht, einen oder mehrere Strings an ihn zu übergeben. Sie können erkennen, dass derComputerName
-Parameter keine Pipeline-Eingabe akzeptiert, da er nicht als Parameterattribut definiert ist ([Parameter()]
). - Dann liest die Funktion jeden dieser Strings und führt das
Test-Connection
-Cmdlet gegen jeden aus. - Für jeden übergebenen Computernamen gibt es dann ein Objekt mit einer
ComputerName
und einerStatus
-Eigenschaft zurück.
Dann rufen Sie die Funktion Get-ConnectionStatus
auf, indem Sie den ComputerName
-Parameter mit einem oder mehreren Hostnamen oder IP-Adressen wie unten übergeben.
Schritt 1: Pipeline-Eingabe ermöglichen
Um diese Funktion dazu zu bringen, Pipeline-Eingaben zu akzeptieren, müssen Sie zuerst das entsprechende Parameterattribut definieren. Dieses Attribut kann entweder ValueFromPipeline
sein, um Pipeline-Eingaben nach Wert zu akzeptieren, oder ValueFromPipelineByPropertyName
, um Pipeline-Eingaben nach Eigenschaftsnamen zu akzeptieren.
Fügen Sie für dieses Beispiel das Parameterattribut ValueFromPipeline
innerhalb der Klammern der [Parameter()]
-Definition wie unten gezeigt hinzu.
An diesem Punkt ist das im Grunde alles, was Sie tun müssen. Die Funktion Get-ConnectionStatus
wird jetzt jedes übergebene Zeichenobjekt an den ComputerName
-Parameter binden. Aber auch wenn die Parameterbindung stattfindet, bedeutet das nicht, dass die Funktion etwas Sinnvolles damit macht.
Schritt 2: Hinzufügen eines Prozessblocks
Wenn Sie möchten, dass PowerShell alle Objekte verarbeitet, die aus der Pipeline kommen, müssen Sie als nächstes einen Process
-Block hinzufügen. Dieser Block teilt PowerShell mit, jeden aus der Pipeline kommenden Gegenstand zu verarbeiten.
Ohne einen
Process
-Block verarbeitet PowerShell nur das erste Objekt aus der Pipeline. DerProcess
-Block sagt PowerShell, die Verarbeitung der Objekte fortzusetzen.
Fügen Sie einen Process
-Block wie unten gezeigt hinzu, indem Sie die gesamte Funktionalität der Funktion einschließen.
Senden Sie immer Ausgaben innerhalb des
Process
-Blocks. Das Senden von Ausgaben innerhalb desProcess
-Blocks „streamt“ die Objekte in die Pipeline und ermöglicht es anderen Befehlen, diese Objekte aus der Pipeline zu akzeptieren.
Übergeben von Objekten an die PowerShell-Pipeline
Nachdem Sie einen Process
-Block in der oben definierten Funktion haben, können Sie die Funktion aufrufen und Werte an den ComputerName
-Parameter über die Pipeline übergeben, wie unten gezeigt.
Zu diesem Zeitpunkt können Sie die eigentliche Stärke der Pipeline nutzen und beginnen, weitere Befehle in die Mischung aufzunehmen. Zum Beispiel haben Sie möglicherweise eine Textdatei, C:\Test\computers.txt, mit einer Zeile von IP-Adressen, die durch einen Zeilenumbruch getrennt sind, wie unten gezeigt.
Sie könnten dann den Get-Content
-Befehl verwenden, um jede dieser IP-Adressen in der Textdatei zu lesen und sie direkt an die Get-ConnectionStatus
-Funktion zu übergeben.
Wenn Sie dieses Setup einen Schritt weiter bringen, könnten Sie die Objekte, die Get-ConnectionStatus
zurückgibt, direkt an den ForEach-Object
-Befehl übergeben.
Der folgende Code:
- Liest alle Computernamen in der Textdatei und übergibt sie an die Funktion
Get-ConnectionStatus
. - Die
Get-ConnectionStatus
-Funktion verarbeitet jeden Computernamen und gibt ein Objekt mit den EigenschaftenComputerName
undStatus
zurück. Get-ConnectionStatus
übergibt dann jedes Objekt an dasForEach-Object
-Cmdlet, das dann einen einzelnen String zurückgibt, der als Cyan formatiert ist und einen menschenlesbaren Status enthält.
Wenn die Pipeline-Eingabe nicht für den
ComputerName
-Parameter aktiviert war oder wennGet-ConnectionStatus
kein Objekt innerhalb desProcess
-Blocks zurückgegeben hat, würde PowerShell keinen Status an die Konsole zurückgeben, bis alle Objekte (IP-Adressen) verarbeitet sind.
Pipeline-Bindung nach Eigenschaftsnamen
Bisher ist das Get-ConnectionStatus
-Cmdlet so eingerichtet, dass es die Pipeline-Eingabe nach Wert akzeptiert (ValueFromPipeline
), indem es ein Array von Zeichenfolgen wie '127.0.0.1', '192.168.1.100'
akzeptiert. Würde diese Funktion auch wie erwartet funktionieren, wenn die Eingabe aus einer CSV-Datei anstelle einer Textdatei mit IP-Adressen empfangen würde?
Vielleicht haben Sie eine CSV-Datei, die wie folgt aussieht, unter C:\Test\pc-list.csv.
Beachten Sie, dass das
ComputerName
-Feld in der CSV-Datei denselben Namen wie derComputerName
-Parameter vonGet-ConnnectionStatus
hat.
Wenn Sie versuchen würden, die CSV zu importieren und sie über die Pipeline an Get-ConnectionStatus
zu übergeben, würde die Funktion ein unerwartetes Ergebnis in der ComputerName
-Spalte zurückgeben.
Können Sie erraten, was schief gelaufen ist? Immerhin stimmt der Parametername überein, also warum hat die PowerShell-Pipeline die Ausgabe, die Import-CSV
zurückgab, nicht an den ComputerName
-Parameter von Get-ConnectionStatus
gebunden? Weil Sie das Parameterattribut ValueFromPipelineByPropertyName
benötigen.
Zum jetzigen Zeitpunkt hat der Parameter ComputerName
der Funktion eine Parameterdefinition, die wie folgt aussieht: [Parameter(ValueFromPipeline)]
. Daher müssen Sie ValueFromPipelineByPropertyName
hinzufügen, um den ComputerName
-Parameter für die Eingabe ByPropertyName zu unterstützen, wie unten gezeigt.
Sobald Sie die Pipeline-Unterstützung ByPropertyName hatten, teilen Sie PowerShell mit, dass es beginnen soll, die Objekteigenschaftsnamen und den Objekttyp zu betrachten. Nach dieser Änderung sollten Sie die erwartete Ausgabe sehen.
Zusammenfassung
In diesem Tutorial haben Sie gelernt, wie die PowerShell-Pipeline funktioniert, wie sie Parameter bindet, und sogar, wie Sie Ihre eigene Funktion erstellen können, die die PowerShell-Pipeline unterstützt.
Auch wenn Funktionen ohne die Pipeline funktionieren, übertragen sie keine Objekte von einem Befehl zum anderen und vereinfachen den Code nicht.
Können Sie sich an eine Funktion erinnern, die Sie geschrieben haben oder vielleicht schreiben werden, die von der Vorbereitung für die Pipeline profitieren kann?