Funktionen in PowerShell: Parameter, Datentypen, Rückgabewerte


    Tags:

    Funktion in PowerShellWie die meisten Programmier­sprachen kann auch PowerShell mehrere Statements zu einer Funktion zusammenfassen. Sie vereinfacht die Wiederverwendung von Code und hilft bei der Strukturierung von Scripts. Auch wenn Funktionen in PowerShell für die Benutzer von VBScript oder PHP auf den ersten Blick vertraut aussehen, so gibt es doch einige gravierende Unterschiede.

    Unabhängig davon, ob eine Funktion nur ein einzelnes Statement oder eine komplexe Abfolge von Anweisungen umfasst, lässt sich der in ihr enthaltene Code einfach durch den Aufruf des Funktionsnamens inklusive der eventuell erforderlichen Parameter ausführen. Das vermeidet redundante Script-Blöcke und erleichtert die Pflege des Codes.

    Kein Zwang zur Verwendung von Funktionen

    Allerdings ist die Verwendung von Funktionen in PowerShell nicht zwingend, und darin unterscheidet sie sich von den meisten kompilierten Sprachen, beispielsweise von C. So darf ein großer Teil der Anweisungen direkt im Script stehen, ohne dass sie in irgendeiner Form gekapselt werden.

    Dabei fällt auch auf, dass man es in PowerShell zwar durchgängig mit Objekten zu tun hat, aber Funktionen nicht als Methoden innerhalb von Klassen definiert werden, sondern eigenständig wie in prozeduralen Sprachen.

    Funktion als Scope für Variablen

    Wie man von einer Funktion erwarten kann, definiert sie auch in PowerShell den Geltungsbereich von Variablen. Solche, die innerhalb eines function-Blocks definiert werden, sind außerhalb nicht vorhanden und ihr Wert damit nicht verfügbar. Dennoch sollte man keine gleichnamigen lokalen und globalen Variablen in einem Script verwenden, um unerwünschten Nebeneffekten vorzubeugen.

    Gültigkeitsbereiche betreffen auch Funktionen selbst, so dass sie nicht immer und überall verfügbar sind. Wenn man sie in einem Script definiert, dann sind sie standardmäßig nur dort bekannt und beispielsweise nicht am PowerShell-Prompt. Dies kann man ändern, indem man bei der Definition dem Namen der Funktionen einen Bezeichner für den Scope voranstellt, etwa

    function global:Get-UserInput

    Dieses Beispiel erweitert den Geltungsbereich der Funktion Get-UserInput auf global, so dass sie auch außerhalb des Scripts zugänglich ist, in dem sie definiert wurde.

    Funktionen im Profil laden

    Definiert man eine Funktion auf der Kommandozeile, um sich für längere Kommandos künftig Tipparbeit zu ersparen, dann existiert sie nur während dieser Sitzung. Möchte man, dass sie auch künftig in Shells zur Verfügung steht, dann muss man sie ebenso wie Aliase in das PowerShell-Profil aufnehmen.

    Während man bei der interaktiven Verwendung von PowerShell eher einfache Funktionen definiert, die aus bloß einer einzelnen Anweisung bestehen können, nehmen sie in Scripts oft erhebliche Komplexität an.

    Syntax für Funktionen

    Unabhängig vom Kontext und vom Umfang beschreibt die PowerShell-Hilfe die allgemeine Form von Funktionen folgendermaßen:

    function [<scope:>]<name> [([type]$parameter1[,[type]$parameter2])]{

    param([type]$parameter1 [,[type]$parameter2])
    dynamicparam {<statement list>}

    begin {<statement list>}
    process {<statement list>}
    end {<statement list>}

    }

    Die Definition beginnt mit dem Schlüsselwort function, wobei die Deklaration eines Datentyps für den Rückgabewert nicht vorgesehen ist. Dem Namen kann man wie beschrieben einen Wert für den Geltungsbereich der Funktion voranstellen. Die Bezeichnung der Funktion sollte sich an der PowerShell-Konvention Verb-Noun orientierten, der alle mitgelieferten Cmdlets folgen (zum Beispiel Get-Process).

    Definition von Parametern

    Ein wesentliches Feature von Funktionen besteht darin, dass sie die enthaltenen Anweisungen nicht stur mit konstanten Daten ausführen müssen, sondern dass man ihnen wechselnde Werte als Argumente übergeben kann.

    PowerShell geht hier in seinen Möglichkeiten deutlich über die meisten anderen Sprachen hinaus, was aber auch einigen Lernaufwand bedeutet. Ungewöhnlich ist schon die Auswahl zwischen zwei verschiedenen Notationen, um Parameter zu definieren.

    Zum einen kann man die Liste der Parameter direkt hinter dem Funktionsnamen deklarieren, so wie dies in den meisten Programmiersprachen üblich ist. Alternativ besteht die Möglichkeit, sie am Anfang des Funktionskörpers mit Hilfe des Schlüsselworts param festzulegen.

    Grundsätzlich ist es Geschmacksache, für welche Variante man sich entscheidet. Wenn man allerdings mehrere der zahlreichen Attribute nutzen und Vorgabewerte bestimmen möchte, dann empfiehlt sich aus Gründen der Übersichtlichkeit die Definition in einem param-Block.

    Benannte Parameter

    Microsofts Cmdlets verwenden überwiegend benannte Parameter, so dass man dieser Konvention auch für eigene Funktionen folgen sollte. Bei ihrer Deklaration legt man den Namen eines Parameters als Variablen fest, auf deren Wert man dann innerhalb der Funktion zugreifen kann:

    function Start-App($AppName){

    $AppID = Get-StartApps | where Name -like $AppName
    Start-Process "explorer.exe" -ArgumentList ("shell:AppsFolder\" + $AppID.AppID)

    }

    Dieses Beispiel packt die Anweisungen zum Start einer App unter Windows 8.x in eine Funktion, so dass man ihr nur mehr den Namen der Anwendung (oder einen Teil davon) übergeben muss, um sie auszuführen. Zu diesem Zweck würde man sie nach dem Muster

    Start-App -AppName "editor"

    aufrufen, um etwa Notepad zu starten.

    Typisierung und Vorbelegung von Parametern

    Die Definition des einzigen Parameters verzichtet in diesem Beispiel auf zusätzliche Attribute. Aber man könnte etwa durch die Spezifikation eines Datentyps erzwingen, dass nur ein Wert vom Typ String akzeptiert wird:

    function Start-App([String] $AppName){

    <Anweisungen>

    }

    Ein gängiges Anliegen besteht darin, den Aufruf einer Funktion abzufangen, wenn er ohne die definierten Parameter erfolgt. Dafür bieten sich zwei Strategien an: Entweder man setzt einen Vorgabewert für ein Argument oder man schreibt die Verwendung eines bestimmten Parameters vor. Einen Default-Wert legt man durch eine Zuweisung in der Deklaration fest:

    function Start-App([String] $AppName = "Editor"){

    <Anweisungen>

    }

    Fehlt hier beim Aufruf von Start-App die Angabe von -AppName, dann führt dies automatisch zum Start von Notepad. Möchte man verhindern, dass ein Benutzer die Funktion ohne den Parameter AppName aufruft, dann kann man dies durch das Attribut Mandatory tun:

    function Start-App{

    Param(
    [parameter(Mandatory=$true)]
    [String]
    $AppName
    )

    <Anweisungen>
    }

    Dieses Beispiel verwendet die Notation, die den Parameter mit Hilfe von Param innerhalb der Funktion definiert. Es legt fest, dass AppName angegeben werden muss, andernfalls wird der Benutzer nach dem Start der Funktion zu dessen Eingabe aufgefordert. Zusätzlich erwartet die Funktion einen Wert vom Typ String.

    Validierung der Parameterwerte

    Neben Mandatory bietet PowerShell eine ganze Reihe von Attributen, um die angegebenen Parameter zu validieren. Dazu zählen unter anderem:

    • AllowNull
    • AllowEmptyString
    • ValidateCount (Prüfung auf Minimal- und Maximalwert)
    • ValidateLength (prüft minimale und maximale Zahl an Zeichen)
    • ValidatePattern (Prüfung mittels RegEx)

    Eine vollständige Übersicht über die Validierungsoptionen findet sich in der Hilfe unter dem Stichwort about_Functions_Advanced_Parameters.

    Positionsparameter

    Benannte Parameter erleichtern aufgrund der sprechenden Namen den Aufruf von Funktionen. Dazu trägt auch bei, dass die Reihenfolge der Argumente keine Rolle spielt. Möchte man bei benannten Parametern aber auf die Verwendung der Parameternamen verzichten, dann muss man sie genau in der Reihenfolge übergeben, in der sie deklariert wurden. Alternativ kann man die Position eines Arguments bei der Definition einer Funktion erzwingen:

    MyFunc{
    Param(
    [parameter(Position=1)]
    $Address,
    [parameter(Position=0)]
    $ComputerName

    )

    <Anweisungen>

    }

    In diesem Beispiel müsste der Parameter ComputerName immer als erster angegeben werden, wenn man ihn ohne Bezeichner aufruft, also nach dem Muster

    MyFunc "Mein PC" "192.168.10.11"

    Werte der Parameter aus $args entnehmen

    Die simpelste Form positionaler Parameter besteht darin, dass man diese gar nicht definiert und innerhalb der Funktion einfach die übergebenen Werte aus dem Array $args entnimmt. Folgendes Beispiel ruft Test-Connection erst mit dem Hostname und dann mit der IP-Adresse auf:

    function MyPing{

    $CompName = $args[0]
    $IP = $args[1]

    Test-Connection $CompName
    Test-Connection $IP

    }

    Hier findet keinerlei Validierung der übergebenen Werte statt, für das erste Argument (repräsentiert durch $args[0]) erwartet die Funktion MyPing den Namen des Computers und für das zweite ($args[1])dessen IP-Adresse.

    Daten über Pipe an Funktionen übergeben

    Werte müssen nicht unbedingt statisch via Parameter an Funktionen übergeben werden, vielmehr akzeptieren sie auch die Eingabe über die Pipeline. Typischerweise leitet man auf diese Art die Ausgabe eines Cmdlets weiter oder man liest den Inhalt einer Datei aus reicht diesen als Input durch.

    Im einfachsten Fall verzichtet man auch hier auf die Deklaration von Parametern und entnimmt die Daten aus der Pipeline, indem man über alle Objekte der vordefinierten Variablen $input iteriert:

    function Start-App([String] $AppName){

    foreach($App in $input){
    if($App.Name -like $AppName){
    Start-Process "explorer.exe" -ArgumentList ("shell:AppsFolder\" + $App.AppID)
    }

    }

    }

    Bei diesem Beispiel handelt es sich um eine Variante von Start-App, allerdings mit dem Unterschied, dass Get-StartApps nicht in der Funktion aufgerufen wird, sondern seine Ausgabe via Pipeline von außen übergibt. Ein Aufruf würde dann etwa so aussehen:

    Get-StartApps | Start-App "Paint*"

    Seit PowerShell 3.0 stehen weitergehende Möglichkeiten zur Verfügung, um den Input aus einer Pipe zu verarbeiten. Dazu gehören eigene Attribute in der Deklaration von Parametern sowie die Unterteilung des Funktionskörpers in die Blöcke begin, process und end.

    Rückgabewerte

    Wer bereits Erfahrung mit anderen Programmiersprachen hat, ist mit dem Konzept vertraut, dass eine Funktion einen Wert an den Aufrufer zurückgibt. In Javascript oder PHP erfolgt dies über das Schlüsselwort return, in VBScript verwendet man dafür eine Variable mit dem gleichen Namen wie jener der Funktion.

    Auch PowerShell kennt die Anweisung return, aber sie folgt einer anderen Logik. Grundsätzlich dient sie dazu, die Ausführung einer Funktion oder eines Code-Abschnitts zu beenden und die Kontrolle an den übergeordneten Block zu übertragen.

    Ergänzt man das return-Statement um einen Parameter, dann gelangt dessen Wert tatsächlich zurück zum Aufrufer der Funktion. Das gilt jedoch auch für alle anderen Anweisungen, die eine Ausgabe erzeugen, so dass sich diese zusammen mit dem Parameter von return in einer Variablen finden:

    function rTest{

    $a = 5
    $b = "Das ist ein Test"
    $a
    return $b
    }

    Ruft man die Funktion rTest über die Anweisung

    $r = rTest

    auf, dann würden sowohl die Werte von $a als auch von $b als separate Elemente an das Array $r zugewiesen. Es zeigt sich also, dass return nicht erforderlich ist, um aus einer Funktion Werte zurückzugeben. Im obigen Beispiel wäre das Ergebnis gleich, wenn $b vor einem leeren return stünde.

    Keine Kommentare