3dmentat - Fotolia

PowerShell-Skripte mit der PowerShell Pipeline effizienter machen

Die PowerShell Pipeline ist ein äußerst hilfreiches Tool, um Skripte effizienter zu machen. Mit diesem Beispiel gelingt der Einstieg.

Eine der Eigenschaften, die die PowerShell einzigartig macht, ist die PowerShell Pipeline. Auch wenn sich das Pipeline-Konzept auch in anderen Shells findet, hat doch erst die PowerShell die traditionelle Funktionsweise so verbessert, dass über die Pipeline Objekte und nicht nur Text-Strings weitergegeben werden können.

Die PowerShell bietet hunderte Cmdlets, die die Pipeline unterstützen. Wenn man eigene Funktionen in einer bestimmten Art und Weise erstellt, können aber auch eigene Befehle mit Pipeline-Unterstützung angelegt werden. Die folgenden Beispiele sollen die Funktionsweise der PowerShell Pipeline verdeutlichen und Ideen geben, wie sich die Pipeline in eigenen Cmdlets und Skripten verwenden lässt.

In der alten Eingabeaufforderung von Windows erschöpft sich das Pipeline-Konzept darin, über STDOUT einen Output mit STDIN direkt als Input weiterzuleiten. Ein typisches Piping-Szenario wäre zum Beispiel, sich eine Dateiübersicht aus einem Ordner anzeigen zu lassen und durch das Ergebnis zu blättern. Das Durchblättern wird über den more-Befehl durchgeführt, der auch Pipeline-Input unterstützt. Wenn man also die Dateiübersicht gleich seitenweise angezeigt bekommen will, kann der Output von dir direkt an more: dir C:\ | more gesendet werden.

Allerdings unterstützen nicht alle Befehle Input in dieser Art und Weise, und anders als die PowerShell hat Microsoft die Eingabeaufforderung auch nicht dafür entwickelt, sehr viel mehr mit der Pipeline durchführen zu können. Die PowerShell dagegen bietet native Unterstützung der Pipeline. Wer zum Beispiel alle laufenden Prozesse eines Computers angezeigt bekommen will, der kann das Get-Process-Cmdlet verwenden (Abbildung 1).

Abbildung 1: Ergebnisse des Get-Process-Cmdlets.

Um dann lediglich den Namen der Prozesse zu sehen, kann das Ergebnis von Get-Process direkt an einen anderen Befehl weitergegeben werden: Select-Object (Abbildung 2). Mit der PowerShell Pipeline kann man diesen Prozess nahezu unbegrenzt fortführen (Abbildung 3).

Abbildung 2: Anzeige der Prozessnamen über die PowerShell Pipeline.

Mit ein wenig Mehraufwand kann die PowerShell Pipeline auch jenseits dieser eingebauten Kommandos in eigenen Funktionen verwendet werden. Hierzu müssen Administratoren die Attribute ValueFromPipeline und ValueFromPipelineByPropertyName verwenden. Werden diese beiden Attribute zusammen mit bestimmten Parametern in eigenen Funktionen verwendet, dann kann Input direkt von der Pipeline aufgenommen und über die Parameter-Werte innerhalb der Funktion verwendet werden. Auch wenn sich die Attribute an sich recht ähnlich sind, verhalten sie sich doch ein wenig unterschiedlich.

Abbildung 3: Der Output von Get-Process wird direkt an eine Datei weitergeleitet.

Mit dem ValueFromPipeline-Attribut kann erneut das Get-Process-Beispiel verwendet und zu einer angepassten Funktion erweitert werden, die den Output von Get-Process als Input aufnimmt. Eine einfache Erweiterung könnte dann folgendermaßen aussehen:

function Test-Process

{

                [CmdletBinding()]

                param

                (

                                [Parameter()]

                                [string]$ProcessName

                )

            

                if ((Get-Process -Name $ProcessName).Responding -eq $True)

                {

                                $true

                }

                else

                {

                                $false            

                }

}

Diese Funktion ermöglicht dem System die Spezifizierung eines Prozesses nach seinem Namen, überprüft ihn auf eine bestimmte Eigenschaft und gibt anschließend den Wert $true oder $false aus. Das funktioniert zwar, verdoppelt durch die Art der Darstellung aber auch den Aufwand. Eine andere Möglichkeit würde darin bestehen, die folgende Funktion für jeden Prozess auf dem Computer auszuführen:

Get-Process | ForEach-Object {Test-Process –ProcessName $_.Name}

Auch das ist aber noch nicht wirklich effizient, weil hierbei der Befehl Get-Process für jedes Objekt zwei Mal ausgeführt wird. Es wäre also wesentlich besser, einfach den Output von Get-Process direkt an Test-Process weiterzugeben, ohne hierfür das ForEach-Objekt nutzen zu müssen.

Stattdessen lässt sich eine Funktion mit Pipeline-Unterstützung erstellen, die jedes Objekt als Input akzeptiert, das Get-Process als Output erzeugt:

function Test-Process

{

                [CmdletBinding()]

                param

                (

                                [Parameter(ValueFromPipeline)]

                                [System.Diagnostics.Process]$Process

                )

                process

                {

                                if ($Process.Responding -eq $True)

                                {

                                                $true

                                }

                                else

                                {

                                                $false

                                }

                }

}

Zu guter Letzt wird der Output von Get-Process direkt weitergeleitet an Test-Process: Get-Process | Test-Process.

Hier gibt es ein paar kleine Änderungen. Zunächst wandert das Parameter-Attribut ValueFromPipeline in die Parameter-Deklaration der Parenthese. Das weist die PowerShell an, sich die ganzen Objekte anzusehen, die von der Pipeline kommen, und jedes Objekt mit diesem Parameter abzugleichen. Weiter ändert sich aber auch der Typ von String zu System.Diagnostics.Process. Diese Änderung ermöglicht den Abgleich des Parameter-Typs mit dem Typ des Output-Objekts von Get-Process, um so den Befehl erfolgreich ausführen zu können.

Abbildung 4: Über Get-Member in der PowerShell Pipeline wird der Typ des Objekt-Outputs von Get-Process wiedergegeben.

Um jedes Objekt aus der PowerShell Pipeline abarbeiten zu können, muss die Funktion einen Process-Block beinhalten. Ohne würde die Funktion einfach nach dem ersten Objekt aufhören zu arbeiten.

Nachdem der Befehl das gesamte System.Diagnostics.Process-Objekt statt lediglich den Prozessnamen akzeptiert, sind auch alle Eigenschaften erhältlich. PowerShell-Anwender können den entsprechenden Wert einfach durch $Process parameter auslesen, ohne hierfür erneut Get-Process bemühen zu müssen.

Folgen Sie SearchDataCenter.de auch auf Twitter, Google+, Xing und Facebook!

 

Erfahren Sie mehr über Serverbetriebssysteme