| | 0

PowerShell: Skripte, Funktionen und Skriptblöcke (Einführung) – Part 14

4 Skripte, Funktionen und Skriptblöcke (Einführung)

Skripte sind die „Programme“, die in der PowerShell „Sprache“ geschrieben werden. Sie können beliebig komplex und fast beliebig lang sein. Skripte können beliebige Folgen von PowerShell Befehlen beinhalten, Variablen, Funktionen, Drives, etc. definieren. Sie können auch externe Parameter aufnehmen und verarbeiten, wodurch sie wesentlich an Flexibilität gewinnen. Im Normalfall laufen Skripte in einem eigenen Scope. Das bedeutet, dass alle Elemente wie Variablen, Funktionen, Drives, etc., welche innerhalb eines Skriptes, ohne eine explizite Scope-Angabe, erzeugt werden, nicht von dem Parent Scope aus verwendet werden können. Dieses Verhalten kann verändert werden, indem vor dem Skriptnamen (und Pfad) ein Punkt und ein Leerzeichen eingegeben werden. Das sogenannte „Dot Sourcing“ bewirkt, dass bei der Skriptausführung kein Skript Scope erzeugt wird, sondern das Skript im aktuellen Scope läuft. In diesem Fall sind alle im Skript definierten Elemente auch nach der Beendigung des Skriptes in dem aktuellen Scope verfügbar.
Funktionen sind den Skripten vom Aufbau her sehr ähnlich. Sie bilden eine abgeschlossene Einheit, welche aus mindestens einem PowerShell Befehl besteht und einen Namen trägt. Funktionen können Werte zurückgeben und somit auch innerhalb von Ausdrücken verwendet werden. Sie können ebenfalls externe Parameter aufnehmen und verarbeiten. Es gibt zwei grundsätzliche Unterschiede zwischen Skripten und Funktionen. Die Funktionen können nicht direkt in einer Datei gespeichert werden. Der „Ablageort“ ist immer das PSDrive Functions: Daher können Funktionen direkt auf der Kommandozeile verwendet werden, sie verhalten sich dann den Cmdlets ähnlich. Funktionen können, im Gegensatz zu Skripten, Objekte aus der Pipeline empfangen und welche an die Pipeline weiter geben. In den meisten Fällen werden Funktionen innerhalb von Skripten verwendet, wenn z.B. mehrfach die gleiche Aufgabe (nicht in einer Schleife) erledigt werden muss. Die Funktionen müssen innerhalb eines Skriptes definiert werden, bevor sie verwendet werden können. Das hat Einfluss auf den Aufbau und die Lesbarkeit der Skripte. Vor dem eigentlichen „Geschehen“ stehen immer die Funktionsdeklarationen. Für den einen ist das ein Vor-, für den anderen ein Nachteil.
Skriptblöcke haben wiederum mit Funktionen viel gemeinsam. Der Hauptunterschied ist, dass sie keine Namen tragen, sie sind sozusagen Funktionen ohne Namen (auch anonyme Funktionen genannt). Sie können aber auch externe Parameter aufnehmen und verarbeiten sowie Werte zurückgeben. Praktisch alles, was in geschweifte Klammern ({ }) eingeschlossen wird, ist ein Skriptblock. Skriptblöcke können auch als Parameter an Funktionen und Skripte übergeben werden, Pipelineelemente aufnehmen und verarbeiten sowie Elemente an die Pipeline weiter senden (siehe Kapitel 4.5).

Im Folgenden werden die wichtigsten Eigenschaften von Skripten, Funktionen und Skriptblöcken behandelt, sowie die wichtigsten Sprachelemente, welche in jeder der genannten Formen enthalten sein können, kurz besprochen. Es ist an dieser Stelle sinnvoll, sich die Syntax einer Funktion anzuschauen. Der Aufbau eines Skriptes und die Syntax eines Skriptblocks können davon abgeleitet werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Function [<Scope:>]<Name> [([Type]$Parameter1 [,[Type]$Parameter2] [,…])]
{
    Param
    (
        [Type]$Parameter1[,
        [Type]$Parameter2,]
        
    )
    Dynamicparam
{
<StatementList>
}
    Begin
{
        <StatementList>
}
Process
{
        <StatementList>
}
End
{
        <StatementList>
}
}

Um die Übersichtlichkeit zu behalten, wurde auf das Setzen von noch mehr eckigen Klammern für optionale Elemente verzichtet. Zwingend notwendig sind folgende Elemente der Funktionssyntax:

  • Das Schlüsselwort „Function“
  • Der Funktionsname
  • Ein Skriptblock (sinnvollerweise mit mindestens einer Anweisung)

Die vollständige Syntax reduziert sich dann zu der folgenden:

1
2
3
4
Function [<Scope:>]<Name>
{
}

 

Demnach sind die Schlüsselworte Param, Dynamicparam, Begin, Process und End optional. Die ersten beiden werden im Kapitel 4.1.4 besprochen. Die letzten drei haben etwas mit der Verarbeitung innerhalb der Pipeline zu tun. Wenn Objekte über die Pipeline an eine Funktion übergeben werden, wird zunächst einmalig alles innerhalb des Blocks Begin abgearbeitet. Danach werden Objekte von der Pipeline empfangen und für jedes Objekt wird der Inhalt des Blocks Process abgearbeitet, dabei wird jedes Mal die Variable $_ mit dem aktuellen Objekt belegt. Anschließend wird einmalig der Block End ausgeführt. Enthält die Funktion weder Begin, Process noch End Blöcke, wird der Skriptblock innerhalb der Funktion so ausgeführt, als wäre das der „End“ Block.
Bei einem Skript entfällt das Schlüsselwort Function und die Blöcke Begin, Process und End.
Wie vorhin erwähnt, sind Skriptblöcke Funktionen ohne Namen. Umgekehrt gilt auch: Funktionen sind Skriptblöcke mit Namen. Dies verdient eine nähere Betrachtung. Die Funktionen werden in dem PSDrive Function: gespeichert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1.  Get-ChildItem function:
CommandType     Name                            Definition
-----------     ----                            ----------
Function        A:                              Set-Location A:
Function        B:                              Set-Location B:
Function        C:                              Set-Location C:
Function        cd..                            Set-Location ..
Function        cd\                             Set-Location \
Function        Clear-Host                      $space = New-Object System.
Function        D:                              Set-Location D:
Function        Disable-PSRemoting              ...
Function        E:                              Set-Location E:
Function        F:                              Set-Location F:
Function        G:                              Set-Location G:
Function        Get-Verb                        ...
Function        gh                              param($HelpTopic)...
Function        H:                              Set-Location H:
Function        help                            ...
Function        I:                              Set-Location I:
Function        ImportSystemModules             ...
Function        J:                              Set-Location J:
Function        K:                              Set-Location K:
Function        L:                              Set-Location L:
Function        M:                              Set-Location M:
Function        mkdir                           ...
Function        more                            param([string[]]$paths)...
Function        N:                              Set-Location N:
Function        O:                              Set-Location O:
Function        P:                              Set-Location P:
Function        prompt                          $(if (test-path variable:/P
Function        Q:                              Set-Location Q:
Function        R:                              Set-Location R:
Function        S:                              Set-Location S:
Function        T:                              Set-Location T:
Function        TabExpansion                    ...
Function        U:                              Set-Location U:
Function        V:                              Set-Location V:
Function        W:                              Set-Location W:
Function        X:                              Set-Location X:
Function        Y:                              Set-Location Y:
Function        Z:                              Set-Location Z:

 

Wird eine neue Funktion definiert, wird sie auch auf Function: gespeichert:

1
2
3
4
5
6
7
8
9
10
11
12
1.  function Get-Factorial ([int32]$InputNumber=1)
2.  >> {
3.  >> $i=1
4.  >> 1..$InputNumber | foreach {$i*=$_}
5.  >> return $i
6.  >> }
7.  >>
8.  Get-Childitem function:Get-Factorial
CommandType     Name                                     Definition
-----------     ----                             ----------
Function        Get-Factorial                            param([int32]$InputNumber=1)...

 

Wie sieht es mit weiteren Details zu Get-Factorial aus?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1.  (Get-ChildItem Function:Get-Factorial).gettype()
IsPublic IsSerial Name                  BaseType
-------- -------- ----                  --------
True     False    FunctionInfo          System.Management.Automation.CommandInfo
2.  (Get-ChildItem Function:Get-Factorial).scriptblock
param([int32]$InputNumber=1)
$i=1
1..$InputNumber | foreach {$i*=$_}
return $i
3.  (Get-ChildItem Function:Get-Factorial) | get-member
   TypeName: System.Management.Automation.FunctionInfo
Name                MemberType     Definition
----                ----------     ----------
Equals              Method         bool Equals(System.Object obj)
GetHashCode         Method         int GetHashCode()
GetType             Method         type GetType()
ToString            Method         string ToString()
PSDrive             NoteProperty   System.Management.Automation.PSDriveInfo
PSIsContainer       NoteProperty   System.Boolean PSIsContainer=False
PSPath              NoteProperty   System.String PSPath=Microsoft.PowerShel
PSProvider          NoteProperty   System.Management.Automation.ProviderInf
CmdletBinding       Property       System.Boolean CmdletBinding {get;}
CommandType         Property       System.Management.Automation.CommandType
DefaultParameterSet Property       System.String DefaultParameterSet {get;}
Definition          Property       System.String Definition {get;}
Description         Property       System.String Description {get;set;}
Module              Property       System.Management.Automation.PSModuleInf
ModuleName          Property       System.String ModuleName {get;}
Name                Property       System.String Name {get;}
Options             Property       System.Management.Automation.ScopedItemO
OutputType          Property       System.Collections.ObjectModel.ReadOnlyC
Parameters          Property       System.Collections.Generic.Dictionary`2[
ParameterSets       Property       System.Collections.ObjectModel.ReadOnlyC
ScriptBlock         Property       System.Management.Automation.ScriptBlock
Visibility          Property       System.Management.Automation.SessionStat
HelpUri             ScriptProperty System.Object HelpUri {get=try...

 

Die Funktion ist vom Typ System.Management.Automation.FunctionInfo und hat eine Eigenschaft mit dem Namen „ScriptBlock“! Das bedeutet, Funktionen sind Skriptblöcke und werden referenziert durch Einträge auf dem PSDrive Function:

 

Zurück zu Part 13 – Die Preference und Error-Variable, Fehlerbehandlung

Weiter zu Part 15 – Skripte, Funktionen und Skriptblöcke (Parameter und Argumente)

Eine Übersicht aller Artikel dieser Windows PowerShell Blogserie findet ihr hier.