Zum Hauptinhalt springen

Rückgabewerte aus Skriptblöcken

In vielen Programmier- und Skriptsprachen müssen Rückgabewerte einer Funktion explizit angegeben werden, z.B. mit dem Schlüsselwort Return. In Powershell ist das nicht notwendig – alle Ausgaben, die die Funktion generiert, werden aus dem Skriptblock in die sogenannte Standardausgabe geschrieben, und können von hier aus in einer Variablen aufgefangen oder ein die Pipeline weitergeleitet werden.

function Write-FromScriptblock
{
   'Dies ist eine Ausgabe aus dem Skriptblock'
}

 Rufen Sie die Funktion auf, so wird der String "Dies ist eine Ausgabe aus dem Skriptblock" in die Standardausgabe geschrieben.

Write-FromScriptblock
> Dies ist eine Ausgabe aus dem Skriptblock

 Wollen Sie die Ausgabe der Funktion weiterverarbeiten, weisen Sie sie einer Variablen zu:

$Textausgabe = Write-FromScriptblock

Genauso können Sie die Ausgabe natürlich auch an die Pipeline übergeben:

$Textausgabe | Set-Clipboard

Powershell unterstützt das Schlüsselwort Return ebenfalls, in den meisten Fällen ist es aber überflüssig, da jede Ausgabe aus dem Skriptblock außerhalb des Skriptblocks durch eine Variablenzuweisung wieder aufgefangen werden kann. Ein expliziter Return ist also nicht notwendig.

Tatsächlich ist der relevante Unterschied, dass andere Programmier- und Skriptsprachen Ausgaben ausschließlich dann aus der Funktion ausgeben, wenn Sie mit Return zurückgegeben werden. Powershell gibt grundsätzlich alle Ausgaben zurück. Das heißt auch, dass alle ungewollten Ausgaben in Powershell unterdrückt werden müssen, indem man Sie z.B. der Variablen $null zuweist.

Return hat allerdings noch eine Besonderheit, denn es beendet die Verarbeitung des Skriptblocks. Jeglicher Code, der in einem Skriptblock hinter einem Return steht, wird nicht mehr aufgerufen.

function Write-ReturnFromScriptblock
{
   Return 'Dies ist eine Ausgabe aus dem Skriptblock' 
   'Dieser Text wird nie ausgegegen'
}

Write-ReturnFromScriptblock

Werden aus einer Funktion mehrere Rückgabewerte geliefert, speichert Powershell diese in einem Array:

Function Write-ReturnArrayFromScriptblock
{
Param(
   [int]$Wert1, [int]$Wert2
)
   'Summe: ' + ( $Wert1 + $Wert2 ) 
   'Produkt: ' + ( $Wert1 * $Wert2 )
}

$Ergebnis = Write-ReturnArrayFromScriptblock -Wert1 10 -Wert2 20
$Ergebnis.gettype().Fullname
> System.Object[]
$Ergebnis
> Summe: 30
> Produkt: 200

 

Um zu testen, ob es sich bei dem Rückgabewert um ein Array handelt, kann man den Operator -is verwenden:

$ergebnis = Write-ReturnArrayFromScriptblock
$ergebnis –is [Array]

Wenn Sie mehrere Arrays aus einem Skriptblock zurückgeben, werden diese zu einem einzigen Array zusammengefasst:

function Return-Value
{
   $arr1 = 1,2,3,4
   $arr2 = 5,6,7
   $arr1
   $arr2
}

$Rueckgabe = Return-Value

$Rueckgabe enthält also nicht zwei Arrays, sondern nur eins. Um die beiden Arrays in der Rückgabe beizubehalten, müssen Sie die Arrays komma-separiert ausgeben:

function Return-Value
{
   $arr1 = 1,2,3,4
   $arr2 = 5,6,7
   $arr1,$arr2
}
$Rueckgabe = Return-Value

Um es an dieser Stellt noch einmal explizit zu erwähnen – diese Beispiele sollen Ihnen aufzeigen, wie Powershell mit Rückgaben aus Funktionen umgeht. Trotzdem gilt hier die grundsätzliche Regel: Geben Sie aus Funktionen Objekte zurück, keine einzelnen Strings, Zahlen oder Arrays. Es gibt nur wenig gute Gründe, Text aus einer Funktion zurückzugeben. Das gilt für Funktionen noch viel mehr als für Skripte!

Ausgabekanäle

Wenn Sie Rückgaben aus einer Funktion in die Konsole ausgeben wollen, ohne dass diese in der Standardausgabe landen, haben Sie eine Reihe von unterschiedlichen Cmdlets zur Verfügung: 

Cmdlet

Kanal

Funktion

Write-Host

-/6

Schreibt direkt in die Konsole. Ab PSH 5 wird Kanal 6 verwendet

Write-Output

1

Schreibt in die Standardausgabe

Write-Error

2

Schreibt in den Fehlerdatenstrom

Write-Warning

3

Schreibt in den Warnungs-Datenstrom

Write-Verbose

4

Schreibt in den ausführlichen Ausgabestrom

Write-Debug

5

Schreibt in den Debug-Datenstrom

Write-Information

6

Schreibt in den Information-Datenstrom. Erst ab PSH 5 verfügbar.

Nur Write-Output schreibt dabei in die Standardausgabe (Kanal 1). Dadurch ist es möglich, z.B. in der Pipeline eine Fehlerausgabe zu erzeugen, ohne dass diese hinterher in der Ausgabevariable aufgefangen wird oder sogar die Verarbeitung der Pipeline unterbricht.

Das kann man sehr gut sehen, wenn man Get-Childitem auf den Windows-Ordner ausführt:

$Folder = Get-ChildItem -Path C:\Windows\System32 -Recurse -Directory

Die Ordner-Objekte werden alle in der Variablen $Folder gespeichert, aber die Fehler werden in der Konsole ausgegeben und nicht in $Folder gespeichert.

Direkt in den Fehlerdatenstrom können Sie mit dem Cmdlet Write-Error schreiben:

Write-Error -Exception 'Ein böser Benutzer hat die Tastatur verschmutzt'
> Ein böser Benutzer hat die Tastatur verschmutzt
> + CategoryInfo : NotSpecified: (:) [Write-Error], Exception
> + FullyQualifiedErrorId : System.Exception

Jedes der Write-Cmdlets schreibt in einen anderen Kanal. Stellen Sie sich die einzelnen Kanäle einfach wie eine mehrspurige Autobahn vor, in der jedes Objekte immer auf seiner Spur bleibt. Einige Kanäle werden am Ende der Verarbeitung direkt in die Konsole (das Powershell-Fenster) ausgegeben, manche werden unterdrückt, wenn sie nicht in eine sichtbare Ausgabe gezwungen werden. Man kann einen Kanal auch ausleiten. Das geschieht durch den Umleitungsoperator >.

Get-ChildItem -Path C:\Windows\System32 -Recurse -Directory 2> c:\Temp\Error.log

Die Fehlerausgabe wird jetzt nicht mehr in die Konsole ausgegeben, sondern in die Datei Error.log. Die Zahl zwei ist der Kanal für Fehlermeldungen. Achten Sie darauf, dass er ohne Leerzeichen direkt vor dem Umleitungsoperator stehen muss.

Es ist auch möglich, einen "Spurwechsel" zu erzwingen:

$files = Get-ChildItem -Path C:\Windows\System32 -Recurse -Directory 2>&1

In diesem Fall wird der Ausgabestrom 2 in den Ausgabestrom 1 umgeleitet ("addiert", deshalb das &). Die Fehlermeldungen werden jetzt auch in die Standardausgabe geschrieben und landen damit ebenfalls in der Variablen $files.

Manche Datenströme werden standardmäßig nicht in die Konsole weitergeleitet und sind daher nicht sichtbar, wie z.B. die Ausgabe von Write-Verbose.

Write-Verbose -Message 'Ein Blumenkohl im Ohr ist auch ein schöner Flor'

Sie müssen in der Konsole erst die Ausgabe von ausführlichen Meldungen aktivieren. Der Powershell-Host steuert dabei über die Standardvariable $VerbosePreference, ob ausführliche Meldungen angezeigt werden oder nicht. Setzen Sie die Variable vom Standardwert "SilentlyContinue" auf den Wert "Continue", werden alle ausführlichen Meldungen ausgegeben. Achten Sie darauf, dass Continue als String in Anführungszeichen angegeben ist. Lassen Sie die Anführungszeichen weg, gibt es keinen Fehler, aber der Standardwert wird nicht umgestellt.

$VerbosePreference="Continue"
Write-Verbose -Message 'Ein Blumenkohl im Ohr ist auch ein schöner Flor'
> Ein Blumenkohl im Ohr ist auch ein schöner Flor

Um alle Ausgaben von Write-Host aus einer Funktion oder einem Cmdlet unabhängig vom Status der VerbosePreference-Variable anzuzeigen, können Sie den Parameter -Verbose verwenden, der an jedem Cmdlet und jeder erweiterten Funktion verfügbar ist.

function Out-Verbose
{
[Cmdletbinding()] 
param()
   Out-Verbose -Message 'Ein Blumenkohl in Ohr ist auch ein schöner Flor'
}

Out-Verbose -Verbose
> Ein Blumenkohl in Ohr ist auch ein schöner Flor

Die Funktion Out-Verbose verwendet das Cmdlet Write-Verbose, um eine Ausführliche (Verbose) Information auszugeben. Die Ausgabe wird aber nur dann in die Ausgabe geschrieben, wenn sie explizit mit dem Parameter Verbose angefordert wird (oder die Standardvariable $VerbosePreference global auf "Continue gesetzt ist"). Alle Funktionen bekommen diesen Parameter automatisch aktiviert, allerdings muss man dafür eine erweiterte Funktion (Advanced Function) erstellen. Die einfachste Möglichkeit, eine Advanced Functions zu bekommen ist, einen Parameter mit einem Parameter-Attribut zu definieren. Werden keine Parameter benötigt, kann man einen leeren Parameter-Block anlegen und das Attribut [Cmdletbinding()] über den Parameter-Block schreiben. 

In der Powershell-Konsole wird über eine Reihe von Einstellungsvariablen gesteuert, welche Ausgabeströme angezeigt werden, und welche nicht:

Tabelle 1 - Preference-Variablen steuern das Ausgabeverhalten

Variable

Standard-Wert

ErrorActionPreference

Continue

WarningPreference

Continue

VerbosePreference

SilentlyContinue

DebugPreference

SilentlyContinue

Die möglichen Einstellungen sind durch Actionpreferences festgelegt. Die Actionpreference stellt die Standardkonfiguration für ein Kommando dar, wenn beim Aufruf kein anderes Verhalten durch einen der Standard-Parameter festgelegt wird. Die möglichen Werte für Actionpreference und deren Bedeutung finden Sie in der folgenden Tabelle:

Tabelle 2 - ActionPreference-Einstellungen 

Wert

Bedeutung für das Kommando

für

Continue

Gib die Ausgaben in deinen Datenstrom aus und

übergib an das nächste Kommando

Debug, Error,

Verbose, Warning

SilentlyContinue

Unterdrücke die Ausgaben und übergib an das nächste

Kommando

Debug, Error,

Verbose, Warning

Stop

Gib die Ausgabe in deinen Datenstrom aus und beende

die Ausführung

Debug, Error,

Verbose, Warning

Inquire

Zeig eine Ausgabe an und frag, ob fortgefahren werden

soll

Debug, Error,

Verbose, Warning

Ignore

Unterdrücke die Ausgabe und übergib an das nächste Kommando. Fehler werden nicht in der

Standardvariable $Error gespeichert.

Error

Suspend

Nur in Workflows – Halte nach einem terminierenden

Fehler die Ausführung zur Diagnose an. Nach der Untersuchung kann der Workflow fortgesetzt werden.

Error

Mehr Informationen zu den Actionpreferences finden Sie in der Powershell-Hilfe für Standardparameter:

get-help about_commonparameters

Grundsätzlich können Sie Write-Verbose verwenden, um Ausgaben nur dann anzuzeigen, wenn der Benutzer sie mit dem Parameter -Verbose explizit anfordert. Write-Warning und Write-Error verwenden Sie, um Warnungen und Fehler auszugeben, die die Weiterverarbeitung des Skriptes nicht verhindern, aber dem Benutzer sichtbar gemacht werden sollen, wie z.B. ein Zugriffsfehler auf einen Ordner. Im Gegensatz zu ausführlichen Meldungen werden Warnungen und Fehler standardmäßig angezeigt.

Write-Debug verwenden Sie, wenn Sie Ausgaben erzeugen wollen, die Sie zum Debugging (also zur Fehlerbehandlung) aus dem Skript ausgeben möchten. Wird ein Skriptblock mit dem Standardparameter -Debug aufgerufen, werden aber nicht nur die Debug-Meldungen ausgegeben, sondern es wird auch nach jedem Befehl zur Bestätigung aufgefordert.

Mehr zur Fehlerausgabe mit Write-Errorfinden Sie auch im Abschnitt Debugging und Fehlerbehandlung ab S. 90.

 
function Out-Verbose
{
[Cmdletbinding()] 
param()
   Out-Verbose -Message 'Ein Blumenkohl in Ohr ist auch ein schöner Flor'
}

Out-Verbose -Verbose
> Ein Blumenkohl in Ohr ist auch ein schöner Flor