Zum Hauptinhalt springen

Variablen und Objekte

Wenn ein Powershell-Cmdlet Rückgabewerte ausgibt, werden diese normalerweise in die Konsole geschrieben, also in das Ein-/Ausgabefenster von Powershell. Wenn man Rückgaben weiterverarbeiten möchte, kann man sie aber in einer Variablen "auffangen".

Variablen müssen in Powershell vor der Verwendung nicht explizit deklariert werden. Stattdessen bestimmt Powershell den Datentyp (die "Datenstruktur") einer Variablen selbständig anhand des Rückgabewertes. Sie können die Ausgabe eines Cmdlets mit Hilfe des Gleichheitszeichens direkt einer Variable zuweisen. Dem Variablennamen muss immer ein $ vorangestellt werden, der Name selbst kann aus Buchstaben, Zahlen und dem Unterstrich bestehen.

$Text = 'Hallo Welt' # Der Text 'Hallo Welt' wird der Variablen Text zugewiesen
$Zahl = 1            # Die Zahl 1 wird der Variablen Zahl zugewiesen
$Files = Get-Childitem -Path c:\windows # entspricht Dir c:\windows

Um den Inhalt einer Variablen wieder auszugeben, geben Sie einfach den vollständigen Variablennamen inklusive des $-Zeichens (jetzt ohne das Gleichheitszeichen) in die Konsole ein.

$Text
> Hallo Welt

Sie können den Inhalt der Variable ändern, indem Sie ihr einen neuen Inhalt zuweisen.

$Text = Read-Host -Prompt 'Geben Sie einen Text ein, der $Text zugewiesen werden soll'
> Geben Sie einen Text ein, der $Text zugewiesen werden soll:

Das Cmdlet Read-Host gibt den Text, der mit dem Parameter -Prompt übergeben wurde, in die Konsole aus und erwartet anschließend eine Benutzereingabe. Die Benutzereingabe wird dann in der Variablen Text gespeichert. Ohne die Variablenzuweisung würde die Textausgabe einfach direkt in die Konsole geschrieben werden. Es wird also immer erst das Kommando hinter dem Gleichheitszeichen ausgeführt, und das Ergebnis wird dann anschließend in der Varbiable gespeichert. 

In Powershell sind alle Rückgaben grundsätzlich Objekte. Das bedeutet nicht, dass Sie mit Powershell Objektorientiert programmieren, obwohl das seit Powershell 5 geht. Powershell "konsumiert" lediglich Objekte aus dem .NET Framework. Obwohl das Konzept der Objektorientierung an sich nicht trivial ist, sind Objekte selbst simpel erklärt. Objekte sind nämlich letztlich nichts anderes als Container (Behälter), um Daten, die zusammengehören, strukturiert gemeinsam speichern zu können. Nehmen Sie hierfür eine IP-Adresse.

Um eine IP-Adresse auszugeben, können Sie IPConfig.exe verwenden. IPConfig.exe ist kein natives Powershell-Kommando, sondern ein Programm. Es gibt statt eines Objekts einen sehr langen Beschreibungstext zurück. (Eigentlich ist diese Aussage nicht ganz korrekt, denn in Powershell ist auch ein Text tatsächlich ein Objekt, aber in diesem Zusammenhang spielt das keine Rolle). Das Problem an Text ist, dass alle Informationen in dem Text von unserem Gehirn interpretiert werden müssen – für den Computer ist die Ausgabe nur eine zusammenhanglose Aneinanderreihung von Zeichen. Jegliche Strukturen, die einer IP-Adresse innewohnen, wie die IP selbst, aber auch die Subnetzmaske, der Adresstyp (IPv4, IPv6), die gebundene Netzwerkkarte usw. gehen verloren. Um aus der Textausgabe nur die IP-Adresse auszulesen, muss man den Text im Computer wieder interpretieren. Das geht nur durch Textanalyse (siehe hierzu auch der Abschnitt Reguläre Ausdrücke) oder heutzutage durch KI, ist aber immer in relativ aufwendiger Prozess.

Objekte verhalten sich dagegen wie Sortimentsboxen, in denen die relevanten Daten zu einem Ding einzeln gespeichert und einzeln wieder abgerufen werden. Die Idee bei der Objektorientierten Programmierung ist, dass man zusammenhängende Daten kapselt, weil das die Verarbeitung vereinfacht. Damit alle Objekte eines Typs gleich sind, erstellt ein Programmierer hierfür eine Objektvorlage, die alle relevanten Daten eines Dings in einzelnen Eigenschaften ablegt. Jede Eigenschaft ist dabei für sich selbst eine Variable – wie eine Sortimentsbox mit beschrifteten Fächern. Objektvorlagen werden auch als Klassen (oder in .NET als Typen) bezeichnet. Ein Objekt wird immer von einer Vorlage abgeleitet.

Um eine IP-Adresse in Powershell abzurufen, verwendet man statt IPConfig das Cmdlet Get-NetIPAddress

Get-NetIPAddress -PrefixOrigin dhcp

> IPAddress : 192.168.100.103
> InterfaceIndex : 2 
> InterfaceAlias : Ethernet 10 
> AddressFamily : IPv4
> Type : Unicast
> PrefixLength : 24
> PrefixOrigin : Dhcp
> SuffixOrigin : Dhcp 
> AddressState : Preferred 
> ValidLifetime : 9.22:51:50
> PreferredLifetime : 9.22:51:50 
> SkipAsSource : False
> PolicyStore : ActiveStore

Die Konsole liefert ein IP-Adressobjekt zurück, das für die Konsolenausgabe in Text umgewandelt wird. Speichert man es in einer Variable, bleibt seine Struktur aber erhalten und man kann über die Punkt-Schreibweise (Objekt.Eigenschaft) direkt auf einzelne Eigenschaften der IP-Adresse zurückgreifen:

$IP = Get-NetIPAddress -PrefixOrigin dhcp
$IP.IPAddress

> 192.168.100.103

Das Cmdlet Get-NetIPAddress ruft alle IP-Adressen des Computers ab. Der Parameter -PrefixOrigin ist ein Filter, mit dem man Get-NetIPAddress anweisen kann, nur die IP-Adressen auszugeben, bei denen in der Eigenschaft PrefixOrigin der Wert DHCP steht. In diesem Beispiel dient das dazu, nur eine einzelne IP-Adresse zurück zu bekommen. Wenn Ihre IP-Adresse manuell vergeben wurde, können Sie statt dhcp nach static filtern. 

Powershell liefert die IP-Adresse als Text (String) zurück. Da aber ein Text auch ein Objekt ist, hat er eigene Eigenschaften (tatsächlich nur eine, nämlich Length, in der die Anzahl der Zeichen gespeichert ist), auf die man durch einen weiteren Punkt direkt zurückgreifen kann:

$IP.IPAddress.Length

> 15

Mit Objekten ist man also deutlich flexibler als mit einer reinen Text-Ausgabe – selbst, wenn man nur Text ausgibt. 

Um den Datentyp (also die Vorlage) eines Objekts zu bestimmen, kann man die Methode GetType() verwenden. Methoden können wie Eigenschaften Teil eines Objekts sein und werden ebenfalls wie eine Eigenschaft über die Punkt-Schreibweise aufgerufen, allerdings mit runden Klammern hinter dem Namen. Im Unterschied zu Eigenschaften sind Methoden Programmcode, der Eigenschaften direkt aus dem Objekt auslesen und verändern kann. Eine ausführliche Einführung in Methoden finden Sie im gleichnamigen Abschnitt.

$Text.GetType()

> IsPublic IsSerial Name    BaseType
> -------- -------- ----    --------
> True     True     String  System.Object

$Text.GetType().Fullname

> System.String

Im ersten Beispiel sehen Sie die Ausgabe von GetType(). Die Rückgabe ist, wie immer in Powershell, aus in diesem Fall ein Objekt. Wenn Sie nur den Namen des Datentyps (also der Objektvorlage) haben wollen, können Sie hinter den Methodenraufruf wieder einen Punkt schreiben, gefolgt von der Objekteigentschaft, und Powershell gibt Ihnen nur den Wert der Eigenschaft Name zurück.

Einfache Datentypen

Powershell setzt auf dem .net-Framework auf und verwendet dessen Typen. Die Methode GetType() zeigt an, dass die Variable $Text vom Typ String (=Text) ist. Die Zuweisung zum String-Typ hat Powershell dabei automatisch vorgenommen. Dass es sich um einen String handelt, hat Powershell anhand der Anführungszeichen erkannt.

Man kann einer Variablen einen festen Typ zuweisen (typisieren), indem man den Variablentyp in eckigen Klammern vor den Variablennamen schreibt: 

[Int]$Zahl = '3' # konvertiert den Text 3 automatisch in eine Zahl

Die Variable behält den Datentyp jetzt solange bei, bis Sie explizit gelöscht wird (z.B. mit Remove-Variable).

Die Konsequenz der strengen Typzuweisung ist, dass die Variable $Zahl jetzt keine anderen Daten als Ganzzahlen (Integer) mehr speichern kann. Powershell versucht bei einer Zuweisung nun automatisch, den Datentyp des zugewiesenen Wertes in einen Integer umzuwandeln. Sind die Datentypen miteinander inkompatible (also nicht ineinander umwandelbar), wirft Powershell eine Fehlermeldung.

Um der Variablen einen anderen Datentyp zuzuweisen, muss man sie nun entweder löschen oder einen neuen Datentyp angeben.

$Zahl = 'drei'
Der Wert "drei" kann nicht in den Typ "System.Int32" konvertiert werden. Fehler: "Die Eingabezeichenfolge hat das falsche Format."

[string]$Zahl = 'drei' 

# Löschen
Remove-Variable -Name Zahl

Achten Sie darauf, dass Sie bei Remove-Variable nur den Variablennamen angeben, aber nicht das $-Zeichen. Eine Stolperfalle ist Clear-Variable, da die Variable (und ihr Typ) hier erhalten bleiben, und nur der Inhalt gelöscht wird. 

[Int]$Zahl = '3'

Clear-Variable -Name Zahl

$Zahl = 'drei'

Der Wert "drei" kann nicht in den Typ "System.Int32" konvertiert werden. Fehler: "Die Eingabezeichenfolge hat das falsche Format."

 

Datentypen konvertieren

Powershell versucht, Variablen automatisch zu konvertieren, wie man im obigen Beispiel gesehen hat. Man kann das Umwandeln von Variablen aber auch manuell durchführen, indem man den Datentyp rechts vom Gleichheitszeichen angibt, oder indem man den Operator -as verwendet.

$Text = [string]4
$Text.GetType().fullname
> System.String
$Text –as [datetime]

 

Im ersten Beispiel wird die Ganzzahl ( = Integer) direkt in einen String umgewandelt und erst dann der Variablen $Text zugewiesen. Wird der Datentyp vor einen Wert geschrieben, wandelt Powershell den Typ sofort um. Die Variable $Text kann jetzt nach wie vor beliebige Datentypen annehmen.

Kann Powershell die Umwandlung nicht vornehmen, weil die Datentypen inkompatibel sind, kommt es zu einer Fehlermeldung. Um die Fehlermeldung zu vermeiden, können Sie stattdessen auf den Operator -as zurückgreifen, der auch eine Typumwandlung versucht, aber bei einem Fehlschlag keinen Rückgabewert liefert. Bei Datetime-Datentypen, die Datum und Uhrzeit speichern, werden bei der Konvertierung außerdem die Regions-Einstellungen der Powershell berücksichtigt.

Alternativ kann man auch die Convert-Klasse aus dem .net-Framework zur Konvertierung von Datentypen verwenden. Die Convert-Klasse stellt auch Methoden zur Umwandlung von Zahlensystemen bereit, z.B. vom Binär ins Dezimalsystem:

[convert]::ToInt32('10000010',2) 130

 

Mehr zur Konvertierung gibt es auch unter https://devblogs.microsoft.com/scripting/use-powershell-to-easily-convert-decimal-to-binary-and-back/ oder kurz https://bit.ly/2pDvzCO. Mehr über statische Methoden, Eigenschaften und die Convert-Klasse erfahren Sie im Abschnitt Statische Eigenschaften und Methoden ab S. 103.

Verkettete Typ-Umwandlung

[char[]]'Guten Tag!'
# Wandelt den Text in ein Array von [Char] um
[Byte[]][Char[]]'Hallo Welt' # String zu Char[] zu Byte[] (ergibt ASCII-Code)

Verkettete Typen-Umwandlung bedeutet, dass man einen Datentyp in ein Array eines anderen Datentyps umwandelt. Im folgenden Beispiel wird diese Technik verwendet, um einen Text in ASCII-Code umzuwandeln:

Im ersten Beispiel wird der String 'Guten Tag' in ein Array von einzelnen Buchstaben (Character) umgewandelt. Im zweiten Beispiel wird ein Buchstaben-Array in ein Byte-Array umgewandelt. Ein Byte-Array besteht aus einzelnen Bytes. Bei der Umwandlung wird jeder Buchstabe in seinen Byte-Code konvertiert, was dem ASCII-code entspricht. Wichtig ist sind die eckigen Klammern hinter dem Datentyp, die angeben, dass es sich um ein Array (eine Liste) handelt.

Auf einen Datentyp prüfen

Mit dem Operator -is können Sie eine Variable auf Ihren Datentyp prüfen:

$Text = 'Ich bin ein String'
$Text –is [String]
> True

 -is gibt True zurück, wenn es sich um den geprüften Datentyp handelt, sonst False. Das funktioniert auch mit anderen Variablentypen:

$Text -is [Datetime]
> False
$String -is [INT]
> False
$String -is [Double]
> False

Arrays

Arrays sind Listen von Objekten. Sie werden erzeugt, indem man mehrere Werte mit Komma voneinander getrennt einer Variablen zuweist:

$Namen = 'Hans','Franz','Otto','Karl'

 

Gibt man das Array wieder aus, erhält man die Namen untereinander ausgegeben, da es sich bei jedem String um ein einzelnes Element des Arrays handelt.

Powershell nummeriert die Elemente eines Arrays von Null aufsteigend durch. So wird jedem Eintrag ein "Name" gegeben. Im Prinzip verhält sich ein Array also wie eine zweispaltige Tabelle, wobei in der einen Spalte der Index oder Name des Objekts gespeichert ist, während in der zweiten Spalte ein Verweis auf das eigentliche Objekt hinterlegt ist:

 

Index

Verweis

0

'Hans'

1

'Franz'

2

'Otto'

3

'Karl'

 

Um auf einen einzelnen Wert in einem Array zuzugreifen, gibt man den Index des Eintrags an.

$Namen[1]
Franz

 

Um die Anzahl der Elemente in einem Array zu bestimmen, kann man auf die Eigenschaften Length oder die Alias-Eigenschaft Count zurückgreifen, über die jedes Array verfügt. Count ist dabei keine eigene Eigenschaft, sondern nur ein Verweis auf Length.

$Namen.Length
> 4
$Namen[$Namen.Length-1]
> Karl
$Namen[-1] # Gibt auch Karl zurück – wir zählen Rückwärts in das Array
> Karl

Wie man im Beispiel sieht, kann man das letzte Element des Arrays ausgeben, indem man in Klammern -1 angibt. Man zählt quasi rückwärts in das Array.

Wenn man ein einzelnes Element in einer Variablen speichert, erhält man kein Array. Um Powershell zu zwingen, auch einzelne Objekte immerin einem Array zu speichern, gibt es zwei Wege:

$Dateien = Get-Childitem C:\Test # Der Ordner Test beinhaltet nur eine Datei
$Dateien.Length # Das Ergebnis ist die Größe der Datei
> 3301
$Dateien = @( Get-ChildItem C:\Test ) # @() erzwingt die Konvertierung in ein Array
$Dateien.Length
> 1
[Array]$Dateien = Get-ChildItem C:\Test # Variable Typisieren

Das Erzwingen von Arrays macht immer Sinn, wenn man mehrere Objekte zurück erwartet. Im ersten Beispiel wird z.B. auf die Dateien eines Ordners zugegriffen und danach versucht, über die Eigenschaft Length des Arrays die Anzahl der Elemente anzuzeigen. Wenn aber nur eine einzelne Datei im Ordner liegt, ist die Eigenschaft Length eine Eigenschaft des Dateiobjekts und entspricht der der Größe der Datei und nicht der Zahl 1. Durch das Konvertieren bzw. Typisieren können Sie sicher sein, dass Length immer die Menge der Elemente im Array enthält.

Statt Powershell-Arrays können auch Arraylisten aus dem .Net-Framework verwendet werden, die über mehr Methoden verfügen und u.a. das Hinzufügen von neuen Datensätzen deutlich beschleunigen. Arrays werden nämlich mit einer festen Anzahl von Elementen erstellt und können nicht erweitert werden. Zum Erweitern eines Arrays muss Powershell deshalb immer ein neues Array erstellen und das alte Array dann in das Neue kopieren.

$Namen += "Holger" # Erweitert das Namensarray, ist aber langsam.

In diesem Beispiel wird dem Namen-Array ein weiterer Wert hinzugefügt. Das klappt, ist aber langsam.

Besser geht es mit einer Arrayliste:

# konvertieren in [system.collections.arrayList]
$NamenListe = [system.collections.arrayList]$Namen # $Array umwandeln und speichern
$NamenListe.insert(2,'Dirk') # Einen Datensatz an Position 3 (Index 2) einfügen # neu anlegen
[system.collection.arrayList]$ArrayListe