Zum Hauptinhalt springen

Die Standardvariable $Input

Anstatt die Pipeline-Elemente mit dem Process-Block zu verarbeiten, kann man auch auf die Standard-Variable $Input zurückgreifen, in der alle Elemente einer Pipeline gespeichert sind.

function Test-PipelineWithInputVariable
{
   param(
      [System.IO.FileInfo]$FileObject
   )

   If ($FileObject) 
   {
      $FileObject.FullName
   }
   Else 
   {
      Foreach ( $Element in $Input )
      {
         $Element.FullName
      }
   }
}

 Die Objekte werden alle in der Arraylist-Enumeration $Input gespeichert und können über eine Schleife verarbeitet werden. Am Aufruf der Funktion hat sich nichts geändert:

Get-Childitem C:\Windows\ -File | Test-PipelineWithInputVariable 
Test-PipelineWithInputVariable -FileObject C:\Windows\notepad.exe

Da die Objekte jetzt, anders als im Streamingmodus, erst in der Variablen $Input gesammelt werden müssen, ist diese Variante aber deutlich speicherintensiver, und der Code ist unleserlicher.

Mehr zu $Input finden Sie im Artikel $input gotchas.

Gültigkeitsbereiche (Scopes)

Eine Variable in Powershell hat immer eine vordefinierte Lebensdauer. Spätestens mit Schließen des Powershell-Prozesses werden alle Variablen entsorgt. Viele Variablen haben aber eine deutlich kürzere Existenz.

Die Gültigkeit einer Variablen wird normalerweise durch den Skriptblock definiert, in dem sie erzeugt wurde. Folgendes Beispiel verdeutlicht das:

$SkriptVariable = 'Global'
$SkriptVariable

function Test-Scope
{
   $SkriptVariable
   $SkriptVariable = 'Skript'
   $SkriptVariable
}

Test-Scope
$SkriptVariable
> Global # Ausgabe aus (1)
> Global # Ausgabe aus (2)
> Skript # Ausgabe aus (3)
> Global # Ausgabe aus (4)

In Zeile 1 wird eine Variable $Skriptvariable erzeugt und ausgegeben. Als nächstes wird die Funktion Test-Scope aufgerufen, die zuerst (in Zeile 6) $Skriptvariable ausgibt. Anschließend wird die Variable durch den String "Text" überschrieben und in Zeile 8 wieder ausgegeben. Die Ausgabe von $Skriptvariable ergibt nun "Skript" (Zeile 15). Nach Beendigung der Funktion wird der Inhalt von $Skriptvariable in Zeile 16 erneut ausgegeben. Der Inhalt ist nun wieder "Global" und nicht mehr "Skript". 

Ursache für dieses Verhalten ist, dass eine Variable immer nur in dem Skriptblock sichtbar ist, in dem sie deklariert wurde, sowie in Skriptblöcken, die aus diesem aufgerufen werden. $Skriptvariable ist im gesamten Skript gültig und kann in allen Sub-Skriptblöcken aufgelöst werden. Wenn in einem Sub-Skriptblock eine Variable gleichen Namens angelegt wird, wird die globale Variable nicht überschrieben, sondern im Skriptblock wird eine neue Variable angelegt, die die Skript-Variable quasi überlagert. Innerhalb des Skriptblocks hat die Skriptblock-lokale Variable nun Vorrang. Nach Beendigung des Skriptblocks wird die Skriptblock-Variable wieder freigegeben und aus dem Speicher entfernt. Nun wird wieder auf die Skriptvariable zurückgegriffen. Man spricht von Gültigkeitsbereichen (Scopes).

Um auf die globale Variable des Skripts zurückzugreifen, kann man den Gültigkeitsbereich mit einem Scope-Modifier explizit angeben:

$Skriptvariable = 'Skript'
function Test-Scope
{
   $SkriptVariable = 'Funktion'
   $Script:SkriptVariable
}
Test-Scope

In diesem Beispiel gibt die Funktion Skript zurück. 

Der Scope-Modifier wird, mit einem Doppelpunkt getrennt, vor den Variablennamen geschrieben. Powershell kennt vier Bezeichner für Gültigkeitsbereiche: Global, Script, Local und Private. Ab Powershell 3.0 kann man mit Hilfe des Bezeichners using: außerdem Variablen an Skriptblöcke übergeben, die per Invoke-Command auf einem anderen Rechner ausgeführt werden. Using: wird im Kapitel Powershell Remoting ausführlich behandelt.

 

Scope

 

 

Global:

Der Gültigkeitsbereich des Powershell-Prozesses. Alle automatischen- und Einstellungs-Variablen liegen hier, sowie alle Variablen, die in diesem Prozess erstellt wurden.

 

Script:

Der Gültigkeitsbereich eines Skriptes. Er wird erstellt, wenn ein Skript gestartet wird. Innerhalb eines Moduls wird der Script:-Scope zum Modul-Scope, ist also in allen Scripten des Moduls gültig.

 

Local:

Der lokale Gültigkeitsbereich bezieht sich immer auf den Scriptblock, indem local: aufgerufen wird. Wenn eine Variable in der Konsole als $local:test definiert wird, ist sie identisch mit $global:test.

Private:

Der private Gültigkeitsbereich ist außerhalb seines Scriptblocks nicht sichtbar, wird also auch nicht vererbt.

 

Using:

Erlaubt es, auf Variablen aus dem äußeren Skriptblock innerhalb eines Sub-Scriptblocks zuzugreifen, der Remote (in einem anderen Powershell-Prozess) ausgeführt wird.

 

Man kann auf die Gültigkeitsbereiche auch durch die Nummer der Schachtelungstiefe zugreifen, wobei der aktuelle (lokale) Gültigkeitsbereich die Nummer 0 hat, 1 ist der darüberliegende Gültigkeitsbereich, usw.

Um sich nur die Variablen eines Gültigkeitsbereichs anzeigen zu lassen, kann man Get-Variable mit dem Parameter -Scope aufrufen:

Get-Variable -Scope local

Funktionen

Eine Spezialform einer Variablen, die Skriptblöcke direkt starten kann, ist eine Funktion. Syntaktisch unterscheidet eine Funktion sich in drei Dingen von einer Variablen: Ihr Name wird nicht mit $ eingeführt, sondern ist reiner Text, für die Deklaration wird das Schlüsselwort Function verwendet und Sie benötigen kein Gleichheitszeichen, um den Scriptblock zuzuweisen. Man könnte auch sagen, dass Sie dem Skriptblock einen Namen geben, über den Sie ihn direkt starten können.

Function Start-Scriptblock
{
   Get-Command
}

Führen Sie diesen Code aus, wird Get-Command immer noch nicht gestartet. Aber nun wird der Skriptblock im Speicher abgelegt. Von dort kann er jederzeit aufgerufen werden, indem man den Funktionsnamen aufruft.

Start-Scriptblock

Funktionen sind ein sehr faszinierendes Konstrukt, weil sie Skripte, die selbst nichts weiter sind als Skriptblöcke, die man aus einer Datei heraus startet, fast überflüssig machen können.

Skripte sind selbst Skriptblöcke, und das bedeutet, dass alles, was in einem Skript möglich ist, auch in einer Funktion möglich ist. Parameter können in Funktionen also genauso genutzt werden wie in Skripten. Alles, was Sie machen müssen, um aus einem Skript eine Funktion zu machen ist , das Skript in geschweifte Klammern einzufassen und mit Function <Name> einen Funktionsnamen zu vergeben. Nehmen wir hierzu noch einmal das Skript her, dass wir als Beispiel für Parametersets verwendet haben und wandeln es in eine Funktion um.

function Out-Command
{
<#
.Synopsis
Kann alle Powershell-Kommandos als Gridview oder CSV-Datei ausgeben

.Example
Out-Command -Path c:\temp\commands.csv
#>
param(
   # Ausgabe als Gridview 
   [Parameter(Mandatory=$true, ParameterSetName='Grid')] 
   [switch]$Grid,

   # Ausgabe als CSV-Datei in den angegebenen Pfad 
   Parameter(Mandatory=$true, ParameterSetName='CSV')]
   [string]$Path
)

   If ( $PSCmdlet.ParameterSetName -eq 'Grid' )
   {
      Get-command | Out-GridView
   }
   Else
   {
      Get-Command | Export-CSV -Path $Path
   }
}

Im Beispiel habe ich noch eine Kommentarbasierte Hilfe hinzugefügt um zu zeigen, dass Sie auch Skriptdokumentationen nahtlos in Get-Help integrieren können.

Wenn Sie diesen Skriptblock einmal ausführen, können Sie das Kommando Out-Command beliebig oft starten, es verhält sich wie ein Cmdlet und ist für einen Nutzer von einem echten Powershell-Cmdlet praktisch nicht zu unterscheiden. Man spricht daher auch von einer erweiterten Funktion (Advanced Function).

WhatIf und Confirm

WhatIf und Confirm sind Standard-Parameter, die oft bei Cmdlets, die Änderungen an Daten durchführen (Set-*, Remove-*), verfügbar sind. Rufen Sie ein Cmdlet mit -Whatif auf, wird statt der eigentlichen Funktion nur eine "Was wäre wenn"-Meldung ausgegeben:

PS C:\Windows> Get-ChildItem -File | Remove-Item -WhatIf
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\bfsvc.e-xe".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\bootstat. dat".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\comsetup. log".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\ctfile. rfc".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\diagerr. xml".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\diagwrn. xml".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\DirectX. log".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\DPINST. LOG".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\DtcIn-stall.log".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\Educatio-n.xml".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\Enterpri-se.xml".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\explorer. exe".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\gdrv. sys".
> WhatIf: Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\gdrv2. sys".

Geben Sie -Confirm beim Aufruf eines Kommandos an, wird das Kommando ganz normal ausgeführt, Powershell fordert aber vor der Ausführung eine Bestätigung an.

Get-ChildItem -File | Remove-Item -Confirm
> Bestätigung
> Möchten Sie diese Aktion wirklich ausführen?
> Ausführen des Vorgangs "Datei entfernen" für das Ziel "C:\Windows\system32\0".
> [J] Ja [A] Ja, alle [N] Nein [K] Nein, keine [H] Anhalten [?] Hilfe (Standard ist "J"):

Sie können -whatif und -confirm für Ihre eigenen Funktionen verwenden. Binden Sie dazu zuerst das [CmdletBinding]-Attribut mit dem Argument SupportsShouldProcess=$true ein. Das [CmdletBinding]-Attribut muss direkt vor dem Parameter-Block stehen.

function Remove-Windows
{
   [CmdletBinding(SupportsShouldProcess=$true)] 
   param()
   
   remove-item -Path F:\Windows\*.*
}

Wenn Sie SupportsShouldprocess aktivieren, erlaubt Ihre Funktion sofort den Einsatz von -whatif und -confirm:

 image

Allerdings setzen Sie mit diesem Parameter nur für den Ablauf der Funktion die Standardvariable $WhatIfPreference bzw. $Confirmpreference auf $true, was dazu führt, dass alle Kommandos in Ihrer Funktion so ausgeführt werden, als hätten Sie die Parameter beim Aufruf explizit angegeben. Sie können über die Methode ShouldProcess(), die über $PSCmdlet verfügbar gemacht wird, aber Ihre eigene Prüfung implementieren. Die Methode Shouldprocess() arbeitet im Prinzip wie die vielen Test-Cmdlets. Sie prüft, ob -WhatIf oder -Confirm gesetzt wurden, und gibt $true zurück, wenn sie NICHT gesetzt sind, und $false, wenn sie gesetzt sind. Schauen Sie sich dazu folgendes Skript an:

function Set-VMDefault
{
[CmdletBinding(SupportsShouldProcess=$true)] 
param(
   [Parameter(ValueFromPipeline)]
   $VmName
)

   process {
      If ( $PSCmdlet.ShouldProcess($VmName.VMName) ) {
         Set-VM -VMName $VmName -CheckpointType Production - ProcessorCount 2
      }
   }
}

Die Funktion Set-VMDefault setzt für eine oder mehrere virtuelle Hyper-V Maschinen den Typ des Snapshots und die Anzahl der virtuellen CPUs. Da die Funktion Pipeline-Eingaben akzeptiert, sollte sie auch Whatif und -confirm unterstützen. Der Befehl zum Ändern der virtuellen Maschine wird daher in ein if-Statement eingepackt:

If ( $PSCmdlet.ShouldProcess( $VmName.VMName ))
{
   Set-VM -VMName $VmName -CheckpointType Production -ProcessorCount 2
}

Der Skriptblock des if wird nur ausgeführt, wenn $PSCmdlet.Shouldprocess() wahr zurückgibt, wenn also -Whatif und -Confirm NICHT gesetzt sind. Ansonsten gibt Shouldprocess() $false zurück und schreibt eine Ausgabe direkt in den Host zurück:

"DC1","DC2" | Set-VMDefault -WhatIf

> WhatIf: Ausführen des Vorgangs "Set-VMDefault" für das Ziel "DC1". 
> WhatIf: Ausführen des Vorgangs "Set-VMDefault" für das Ziel "DC2".

Die Textausgabe besteht aus dem Standardtext "WhatIf: Ausführen des Vorgangs {0} für das Ziel {1}". In Position {0} wird der Name der Funktion eingefügt, der ausgeführt wird, und in {1} der Wert, der ShouldProcess() als Parameter in runden Klammern übergeben wurde – in diesem Fall also der Name der virtuellen Maschine. Die gleiche Ausgabe wird bei -Confirm angezeigt, allerdings mit einer Abfrage, ob die Aktion wirklich ausgeführt werden soll.

 Die Methode Shouldprocess() verfügt noch über eine Reihe von Parametern, um die Ausgabe anzupassen. Die Überladungen (=Parameter, die übergeben werden können) sehen so aus: 

OverloadDefinitions
-------------------
bool ShouldProcess(string target)
bool ShouldProcess(string target, string action)
bool ShouldProcess(string verboseDescription, string verboseWarning, string caption)
bool ShouldProcess(string verboseDescription, string verboseWarning, string caption,
[ref] System.Management.Automation.ShouldProcessReason shouldProcessReason)

 Die erste Überladung wurde im Beispiel bereits angewendet. Der Target-String definiert, was in der Ausgabe für das Ziel angegeben werden soll:

 ( $PSCmdlet.ShouldProcess( $VmName ))

Sie können den Namen der Funktion aber auch durch eine eigene Aktion ersetzen:

( $PSCmdlet.ShouldProcess($VmName,'Mache kaputt'))

Die Ausgabe lautet dann: 

"DC1","DC2" | Set-VMDefault -WhatIf

> WhatIf: Ausführen des Vorgangs "Mache kaputt" für das Ziel "DC1". 
> WhatIf: Ausführen des Vorgangs "Mache kaputt" für das Ziel "DC2".

Die dritte Überladung erlaubt es Ihnen, eine komplett eigene Meldung auszugeben. Sie wird im Parameter VerboseDescription übergeben. Der Parameter VerboseWarning wird verwendet, wenn Sie die Funktion statt mit -WhatIf mit -Confirm aufrufen. Der dritte Parameter Caption gibt einfach nur den Namen des Confirm-Fensters an:

 

function Set-VMDefault
{
[CmdletBinding(SupportsShouldProcess=$true)] 
param(
   [Parameter(ValueFromPipeline)]
   $VmName
)
   process {
      If ( $PSCmdlet.ShouldProcess("Mache kaputt","Bitte bestätigen","Don´t do it!" )
      {
         Set-VM -VMName $VmName -CheckpointType Production -ProcessorCount 2
      }
   }
}

-Confirm fordert jetzt eine Bestätigung mit Ihrem individuellen Text.

image

-WhatIf verwendet den Text, der im ersten Parameter VerboseDescription übergeben wurde.

"DC1","DC2" | Set-VMDefault -WhatIf
> WhatIf: Etwas Schlimmes passiert WhatIf: Etwas Schlimmes passiert

Die letzte Überladung erlaubt Ihnen, den Grund für das Ausführen von Shouldprocess() zurückzugeben. Die Rückgabe erfolgt byReference (erkennbar am [ref] vor dem Parametertyp in der Überladung), was bedeutet, dass die Daten nur einer bestehenden Variablen zugewiesen werden können ("referenziert").

$Reason = New-Object -TypeName System.Management.Automation.ShouldProcessReason 
If ( $PSCmdlet.ShouldProcess($VmName,"Mache kaputt",'bla',[ref]$Reason) )
{
   Set-VM -VMName $VmName -CheckpointType Production -ProcessorCount 2
}
$Reason 

In der ersten Zeile wird eine neue Enumeration vom Typ [System.Management.Automation.ShouldProcessReason] erzeugt. Enumerationen sind im Prinzip vordefinierte Arrays, die eine Liste von festgelegten Werten erhalten. Die Enumeration ShouldProcessReason kennt tatsächlich zwei Werte: None und WhatIf. Dies sind auch die beiden möglichen Rückgabewerte, die Sie aus dem Parameter shouldpro-cessreason entnehmen können.

In der Shouldprocess()-Methode übergeben Sie die Variable $Reason referentiell (erkennbar an der [ref]-Anweisung). Referentiell bedeutet, dass Shouldprocess() kein neues Objekt für die Rückgabe erzeugt, sondern das von Ihnen übergebene Objekt verwendet, um den Rückgabegrund zu speichern. Wenn Sie nach Ausführung von Shouldprocess() die Variable $Reason abfragen, gibt sie "WhatIf" zurück, wenn die Funktion mit dem Parameter -Whatif aufgerufen wurde, und sonst None.

Eine noch ausführlichere Beschreibung von Whatif und Confirm finden Sie im Artikel Powershell: Everything you wanted to know about ShouldProcess.

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