Zum Hauptinhalt springen

Funktionen Pipelinefähig machen

Ein Skriptblock besteht eigentlich aus drei Elementen, nämlich einem Begin-Block, einem Process-Block und einem End-Block.

function Test-PipeLine
{
Param()

Begin {
   # Dieser Block wird einmal beim Start der Pipeline vearbeitet
}

Process {
   # Dieser Block wird für jedes Element der Pipeline aufgerufen
}

End {
   # Dieser Block wird nach Verarbeitung der Pipeline einmal gestartet 
   # Werden keine Blöcke angegeben, ist dies der Default-Block
}
}

Wenn beim Erstellen einer Funktion kein Block angegeben wird, wird der Code des Skriptblocks automatisch als End-Block ausgeführt. Um Daten in der Pipeline verarbeiten zu können, muss der Process-Block vorhanden sein. Prinzipiell sind alle Blöcke optional. Eine Funktion kann also aus nur einem Pro-cess-Block bestehen.

  • Der Begin-Block wird vor dem Start des Process-Blocks einmalig ausgeführt und kann z.B. dazu verwendet werden, Variablen zu initialisieren.
  • Der Process-Block wird für jedes Objekt in der Pipeline genau einmal ausgeführt.
  • Nach der Verarbeitung im Process-Block wird der End-Block einmal aufgerufen. Er kann z.B. dazu verwendet werden, Daten auszugeben oder temporäre Daten wieder aufzuräumen.

Das aktuell in der Pipeline befindliche Objekt kann über die anonyme Variable $_ referenziert werden. Deutlich komfortabler ist es aber, die Übergabe über den Parameter-Block durchzuführen. Dadurch ist die Funktion auch ohne Pipeline-Input lauffähig (also bei direktem Aufruf), und die Variablen erhalten lesbare Namen. Für die Übergabe aus der Pipeline werden drei Parameter-Attribute zur Verfügung gestellt: ValueFromPipeline, ValueFromPipelineByPropertyname und ValueFromRemainingArguments.

Ist das Attribut ValueFromPipeline gesetzt, versucht Powershell den Parameter mit dem aktuellen Ob-jekt aus der Pipeline zu befüllen. Zuerst werden dabei Objekte gleichen Typs in den Parameter übernommen. Funktioniert das nicht, da der Parameter und das Pipelineobjekt unterschiedlichen Typs sind, versucht Powershell, das Objekt aus der Pipeline zu konvertieren. Klappt auch das nicht, wird ein Fehler ausgegeben, und das nächste Objekt verarbeitet.

function Test-ParameterFromPipeline
{
param(
   [Parameter(ValueFromPipeline=$true)] 
   [System.IO.FileInfo]$FileObject
)

Process {
   $FileObject.FullName
}
}

Das Attribut ValueFromPipelineByPropertyName ist selektiver und übernimmt nur die Eigenschaft aus dem Pipelineobjekt, die dem Namen der Parameter-Variable entspricht. Um den Parameternamen unabhängig vom Eigenschaftsnamen vergeben zu können, kann man noch einen oder mehrere Parameter-Aliase vergeben. 

function Test-ParameterValueFromPipeline
{
param(
   [Parameter(ValueFromPipelineByPropertyName=$true)] 
   Alias('Fullname','FilePath')] # (1)
   [string]$Path
)

Process {
   $Path
}
}

Get-Item -Path C:\Windows | 
   Test-ParameterValueFromPipeline 
> C:\Windows

Test-ParameterValueFromPipeline -Path C:\windows\notepad.exe
> c:\Windows\notepad.exe

In  diesem Beispiel kann man sehen, dass nur die Eigenschaft Fullname aus dem Pipelineobjekt übernommen wird. Der Eigenschaftenname wird in Zeile 5 als Alias angegeben, aber wenn das Kommando direkt an einem Einzelobjekt aufgerufen wird, ist der Name des Parameters nicht Fullname sondern Path.

Man kann einem Parameter auch mehrere Attribute mitgeben. Der Parameter kann natürlich nur einen Wert übernehmen. Powershell versucht in folgender Reihenfolge, die Parameter zu binden:

1. ValueFromPipeline mit gleichem Datentyp

2. ValueFromPipelineByPropertyName mit gleichem Datentyp

3. ValueFromPipeline mit Typkonvertierung

4. ValueFromPipelineByPropertyName mit Konvertierung

Wie man sieht, versucht Powershell zuerst, das Pipelineobjekt an den Parameter zu binden, was aber nur funktioniert, wenn der Datentyp des Objekts aus der Pipeline und der Datentyp des Parameters übereinstimmen. Tun sie das nicht, versucht Powershell, eine Eigenschaft gleichen Namens zu binden, bei der der Datentyp übereinstimmt. Klappt auch das nicht, versucht Powershell eine Typkonvertierung. Würde im obigen Beispiel kein Alias angegeben sein, könnte Powershell keine Eigenschaft des gepipten Fileinfo-Objekts in den Parameter übernehmen, da es keine Property Path im Fileinfo-Objekt gibt, und würde das Fileinfo-Objekt zu String konvertieren. Klappt auch das nicht, sucht Powershell eine Eigenschaft gleichen Namens wie der Parameter im Objekt, der nicht den gleichen Datentyp hat, und versucht diesen zu Konvertieren. Ist auch das nicht möglich, weil die Datentypen inkompatibel sind, gibt Powershell einen Fehler aus.

Eine ausführliche Beschreibung zur Bindungsreihenfolge finden Sie im Artikel Building PowerShell Functions That Support the Pipeline des MCP-Magazins. 

 
function Test-ParameterValueFromPipeline
{
param(
   [Parameter(ValueFromPipelineByPropertyName=$true)] 
   Alias('Fullname','FilePath')] # (1)
   [string]$Path
)

Process {
   $Path
}
}

Get-ChildItem -Path C:\Windows | 
   Test-ParameterValueFromPipeline 

Test-ParameterValueFromPipeline -Path C:\windows\notepad.exe