Tags: PowerShell, Scripts
Wie in den meisten fortgeschrittenen Script- und Programmiersprachen hängt die Verfügbarkeit von Variablen auch bei PowerShell vom jeweiligen Kontext ab. Eine unglückliche Wahl von Variablennamen kann daher im Zusammenspiel mit verschachtelten Gültigkeitsbereichen zu verwirrenden Ergebnissen führen.
Das Konzept der Gültigkeitsbereiche trifft in PowerShell nicht nur auf Variablen zu, sondern auch auf Aliase, Funktionen und PowerShell Drives (wie etwa das Active-Directory-Laufwerk). Wenn man es jedoch im Zusammenhang mit Variablen verstanden hat, dann kann man es leicht auf die anderen Sprachelemente übertragen.
Hierarchische Organisation
Wenn man einer Variablen in einem Script zum ersten Mal einen Wert zuweist, dann ist sie nur dort verfügbar und nicht auf der Konsole, von der man das Script ausgeführt hat. Genauso wenig lässt sich der Wert einer Variablen auslesen oder verändern, wenn man sie in einer Funktion definiert hat und auf Script-Ebene auf sie zugreifen möchte. Verantwortlich dafür sind ihre Geltungsbereiche.
Diese sind in einer hierarchischen Weise organisiert. Ganz oben ist der globale Gültigkeitsbereich angesiedelt. Er wird automatisch erzeugt, sobald man eine PowerShell-Sitzung startet. Direkt darunter findet sich der Gültigkeitsbereich für Scripts, gefolgt von Funktionen, die in einem Script definiert werden. Verschachtelt man Funktionen, dann erzeugt man für die inneren wieder einen eigenen Scope, der unter den äußeren liegt.
Variablen, die in einem untergeordneten Gültigkeitsbereich definiert wurden, sind im übergeordneten nicht verfügbar. Umgekehrt kann man jedoch lesend auf Variablen zugreifen, die in einem übergeordneten Scope initialisiert wurden, und zwar bis hinab in die untersten Ebenen der Hierarchie.
Es ist aber nicht möglich, eine Variable des übergeordneten Bereichs aus einem Code des untergeordneten Scopes zu verändern. Weist man dort einen Wert an eine Variable zu, die den gleichen Namen hat wie jene in einem darüber liegenden Bereich, dann erzeugt man eine neue Variable, die nur in diesem Bereich verfügbar ist. Alle Ebenen darunter verwenden nun diese Variable und nicht jene aus dem höheren Bereich.
Legt man eine Variable auf der Konsole an, dann hat sie globale Gültigkeit. Den obigen Ausführungen zufolge kann man ihren Wert mithin auch in Scripts lesen. Ebenso sieht man eine Variable in einer Funktion, wenn man sie auf Script-Ebene erzeugt hat, ändern kann man sie allerdings dort nicht. Es ist möglich, eine Variable gleichen Namens im Script- und im function-Kontext zu erstellen, aber alle Ebenen unterhalb dieser Funktion werden dann ihre Variable und nicht jene des Scripts verwenden.
Absolute versus relative Gültigkeitsbereiche
Ein anderes wichtiges Konzept stellt der lokale Gültigkeitsbereich dar. Es handelt sich dabei nicht um das Gegenteil des globalen Scopes, vielmehr bezieht sich der Begriff immer auf den Kontext, in dem man die Variable erzeugt. Tut man dies zum Beispiel auf der PowerShell-Konsole, dann ist ihr lokaler Gültigkeitsbereich identisch mit dem globalen.
Von Bedeutung ist der lokale Scope in Zusammenhang mit dem privaten Gültigkeitsbereich. Möchte man eine Variable explizit als privat deklarieren, dann ist sie nur in ihrem lokalen Scope verfügbar, aber nicht in untergeordneten Blöcken.
Die Gültigkeitsbereiche Global, Local, Script und Private sind absolut, weil man sie über entsprechende Bezeichner direkt referenzieren kann. Daneben kann man sich auf Gültigkeitsbereiche auch relativ zum lokalen Scope beziehen. So hat Letzterer den Wert 0, der direkt übergeordnete hat den Wert 1, eine weitere Ebene höher spricht man mit 2 an, usw.
Beispiel für Script vs. Funktion
Folgende Beispiele sollen die Wirkungsweise von Scopes veranschaulichen. Wenn man sie in PowerShell ISE ausprobieren möchte, dann sollte man bedenken, dass sich dort alle Fenster einen Gültigkeitsbereich teilen und dies zu unerwarteten Ergebnissen führen wird. Daher ist es besser, die Beispiele auf der Konsole auszuführen.
Das folgende Beispiel demonstriert die Hierarchie der Gültigkeitsbereiche:
"Script scope" wird das erste Mal in der Funktion ausgegeben, wo Lesezugriff auf die Variable besteht, die am Anfang des Scripts definiert wurde, und das zweite Mal am Ende des Scripts. Das Zuweisen eines neuen Werts zur Variable $Script innerhalb der Funktion hatte somit keine Auswirkung auf Script-Ebene. Jedoch existiert nun innerhalb der Funktion eine neue Variable gleichen Namens und einem eigenen Wert. Die Variable $Function ist auf Script-Ebene nicht verfügbar und wird entsprechend dort auch nicht angezeigt.
Standard-Scopes verändern
Dieses Standardverhalten von PowerShell lässt sich durch eigene Scope-Bezeichner ändern. Sie beziehen sich auf die 4 absoluten Gültigkeitsbereiche Private, Local, Script und Global. Die folgende Anweisung erzeugt eine Variable mit einem privaten Scope, die in untergeordneten Bereichen nicht ansprechbar ist:
$Private:myVariable = "Nur für den lokalen Bereich"
Umgekehrt kann man eine Script-Variable für die ganze Session verfügbar machen, indem man sie als global deklariert. Dadurch verändert sich aber auch ihr lokaler Gültigkeitsbereich, also im eben genannten Fall auf global. Aus diesem Grund kann man nach Beendigung des Scripts auf der Konsole immer noch auf die Variable zugreifen, während alle Werte aus den anderen Scopes verloren gehen. Dieses Verhalten kann nützlich für die Fehlersuche sein.
Das nächste Beispiel definiert zwei Variablen innerhalb der Funktion, aber erwartungsgemäß ist nur jene mit dem Bezeichner Script außerhalb verfügbar:
Den Bezeichner local benötigt man in der Praxis kaum, weil er ohnehin nur den standardmäßigen Gültigkeitsbereich beschreibt, in dem eine Variable erzeugt wurde. Nachdem man diese Modifizierer aber nicht nur verwendet, um den Scope zu verändern, sondern auch um sich auf einen bestimmten zu beziehen, kann die Verwendung von local den Code verständlicher machen. Dazu ein Beispiel:
Wenn man ältere oder einfachere Script-Sprachen gewohnt ist, dann zieht man es vielleicht vor, generell mit Variablen zu arbeiten, die im ganzen Script gelesen und verändert werden können. Dies entspricht natürlich nicht den Best Practices für PowerShell, aber mit dem Cmdlet Set-Variable lässt sich erreichen, dass alle untergeordneten Gültigkeitsbereiche vollen Zugriff erhalten.
Wie man sieht, benötigen wir hier keinen der oben genannten Bezeichner. Vielmehr sorgt Set-Variable dafür, dass die Variable in allen untergeordneten Gültigkeitsbereichen gelesen und geschrieben werden kann. Alternativ kann man das Cmdlet New-Variable verwenden, um eine Variable zu definieren und dabei gleich die Eigenschaft AllScope zu vergeben:
New-Variable -Name myVariable -Option AllScope -Value "Änderbar in allen untergeordneten Gültigkeitsbereichen"
Möchte man zum Debuggen den Gültigkeitsbereich von allen Variablen, die im Script-Scope definiert wurden, auf global ausweiten, dann stellt man dem Aufruf des Scripts einen Punkt und ein Leerzeichen voran:
. C:\Users\me\test.ps1
Nach der Ausführung des Scripts stehen alle Variablen, die dort auf der obersten Ebene definiert wurden, auch in der Konsole zur Verfügung. Das gilt aber nicht für jene aus den Funktionen.
Täglich Know-how für IT-Pros mit unserem Newsletter
Verwandte Beiträge
- WhatIf, Confirm und Force: Ausführung von PowerShell-Cmdlets steuern
- ScriptRunner Portal Edition R5: Konfiguration von Abfragen, OpenID-Support, Web-API statt IIS
- ScriptRunner Portal Edition R4: Support für Microsoft Graph, überarbeitetes Portal, zentrale Execution-Policy
- Microsoft Graph: Einheitliches (PowerShell)-API für Microsofts Cloud-Dienste
- Strings ersetzen oder löschen mit PowerShell
Weitere Links
2 Kommentare
Hallo,
bei allem Respekt, dieser Artikel wurde von folgender Seite gestohlen:
https://4sysops.com/archives/the-powershell-variable-scope/
Einfach einen Blick auf den Autor werfen, vielleicht kommt dann die Erleuchtung.