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.