Hashtable in PowerShell erzeugen, Elemente hinzufügen, löschen, sortieren


    Tags:

    Hash-Table in PowerShellNeben Arrays unter­stützt Power­Shell mit Hash-Tabellen einen wei­teren zusammen­gesetzten Daten­typ. Er bietet Methoden, um Schlüs­sel/Wert-Paare nachzu­schlagen, hinzu­zufügen, zu ändern oder zu löschen. Hashtables lassen sich viel­fältig an­wenden, etwa zur Über­gabe von Werten an Cmdlet-Parameter.

    Der wesentliche Unterschied zwischen gewöhnlichen Arrays und Hash-Tabellen, die auch assoziative Arrays heißen, besteht darin, dass man nicht bloß einen nummerischen Index, sondern jedes .NET-Objekt als Schlüssel verwenden kann. Hashtables bestehen somit aus einer Sammlung von Key/Value-Paaren.

    Arrays versus Hashtables

    Legt man etwa dieses Array an

    $farben = @("Schwarz","Weiß","Gelb","Blau")

    dann ruft man das zweite Element mit dem Wert "Weiß" über $farben[1] ab. Entsprechend kann man durch Hochzählen des Indexes einfach über das gesamte Feld iterieren (siehe dazu Arrays in PowerShell: anlegen, ändern, auslesen, sortieren, löschen).

    Hashtables hingegen bestehen immer aus Paaren in der Form Schlüssel = Wert. Um beim Thema Farben zu bleiben, könnte eine Hash-Tabelle so aussehen:

    $farben =@{Auto ="Schwarz"; Wand="Weiß"; Ei ="Gelb"; Himmel="Blau"}

    Bei der Syntax zum Anlegen eines assoziativen Arrays ist zu beachten, dass die Key/Value-Paare in geschweiften Klammern stehen (beim normalen Array sind es runde) und durch Semikolon voneinander getrennt sind (beim Array ist auch ein Komma zulässig). Die Zuordnung des Werts zu einem Schlüssel erfolgt über das Gleichheits­zeichen.

    Auf Elemente zugreifen

    Möchte man nun auf das zweite Element mit Wert "Weiß" zugreifen, dann bietet PowerShell dafür mehrere Möglichkeiten. Analog zu Arrays könnte man dies mit

    $farben["Wand"]

    tun. Wie man sieht, dient hier der Schlüssel "Wand" als Index und nicht ein nummerischer Wert (auch wenn sich ein solcher ohne weiteres als Key verwenden ließe).

    Auf Elemente eines Hashtables kann man in PowerShell über verschiedene Methoden zugreifen.

    Wie bei Arrays erlaubt PowerShell auch hier, mehrere Elemente auf einmal anzusprechen:

    $farben["Auto", "Wand"]

    Alternativ kann man die Notation

    $farben.Wand

    verwenden, oder wenn man es aufwändiger haben möchte, die Funktion Get_Item bemühen:

    $farben.Get_Item("Wand")

    Wenn man dabei kein Ergebnis erhält, dann kann entweder der Wert für diesen Key $null sein oder der Schlüssel existiert nicht. Um zu überprüfen, ob er vorhanden ist, verwendet man die Funktion ContainsKey:

    $farben.ContainsKey("Wand")

    Der Gegenspieler, die Funktion ContainsValue(), informiert dagegen, ob ein bestimmter Wert in der Tabelle vorliegt:

    $farben.ContainsValue("Weiß")

    Beide Funktionen geben True oder False zurück, abhängig davon, ob sie den gewünschten Wert finden.

    Will man den gesamten Inhalt der Tabelle ausgeben, dann reicht wie in PowerShell üblich, die Eingabe des Variablennamens. Wie der Befehl

    $farben | measure

    jedoch zeigt, erhält man den ganzen Hashtable auf diese Weise als ein Objekt.

    Über den Variablennamen gibt PowerShell den ganzen Hashtable als ein Objekt aus. Count enthält die Zahl der Elemente.

    Wie viele Elemente er enthält, kann man aber wie bei einem Array über die Eigenschaft count erfahren:

    $farben.count

    würde in unserem Beispiel 4 ausgeben.

    Über Hash-Tabellen iterieren

    Bei verschiedenen Gelegenheiten wird man die gesamte Sammlung in einer Schleife durchlaufen wollen. Während man bei einem Array dafür nur den Index hochzählen muss, benötigt man hier ein anderes Verfahren.

    Zum einen eignet sich dafür eine foreach-Schleife über alle Keys, wobei sie bei jedem Durchlauf eine zweite Variable mit dem aktuellen Schlüssel belegt:

    foreach($k in $farben.Keys){ $farben[$k] }

    Zum anderen bietet sich die Funktion GetEnumerator() für diese Aufgabe an, so dass man sich die Laufvariable sparen kann:

    $farben.GetEnumerator() | foreach{ $_.value }

    Möchte man alle Elemente finden, die einen bestimmten Wert haben, dann muss man nicht über das Dictionary iterieren, hier tut es auch ein Filter mit Where-Object (Alias "?"):

    $farben.Keys | ? { $farben[$_] -eq "Weiß" }

    Schlüssel hinzufügen

    Wie ganz oben gezeigt, kann man gleich bei der Deklaration eines Hashtable die Schlüssel/Wert-Paare einfügen. Wenn man etwa mit einer leeren Hash-Tabelle

    $farben=@{}

    beginnt oder einfach zu bestehenden Keys weitere hinzufügen möchte, dann gibt es auch dafür mehrere Optionen. Die offen­sichtlichste folgt der gleichen Syntax wie bei der Deklaration:

    $farben["Wiese"] = "Grün"

    Hinzufügen eines neuen Schlüssels zu einem Hashtable

    Darüber hinaus bietet eine Hash-Tabelle für diesen Zweck die Methode add():

    $farben.Add("Wiese", "Grün")

    Schließlich kann man für diese Aufgabe auch den Additionsoperator heranziehen:

    $farben += @{Wiese = "Grün"}

    Wie man unschwer erkennen kann, legt man hier für den neuen Schlüssel eine eigene Hash-Tabelle an. Somit handelt es sich dabei um ein Verfahren, mit dem man generell zwei assoziative Arrays kombinieren kann.

    Schlüssel löschen

    Um einen Key aus dem Dictionary zu entfernen, sieht PowerShell die Methode remove() vor. Damit lässt sich bei jedem Aufruf jeweils nur ein Paar löschen:

    $farben.Remove("Wiese")

    Komplizierter wird die Angelegenheit, wenn man über die Tabelle iterieren will, um Schlüssel zu löschen, die einen bestimmten Wert haben, im folgenden Beispiel wäre das "Grün":

    $farben.GetEnumerator() | 
    %{ if($_.value -eq "Grün"){ $farben.remove($_.key)} }

    Dieses Vorgehen quittiert PowerShell aber mit der Meldung "Fehler beim Durchlaufen einer Auflistung: Die Auflistung wurde geändert. Der Enumerations­vorgang kann möglicher­weise nicht ausgeführt werden".

    Wenn man Schlüssel in einer Schleife aus einem Hashtable entfernen möchte, dann sollte man die Clone-Methode nutzen.

    Wenn man diese Reaktion vermeiden und ein verlässliches Ergebnis erzielen will, dann hilft hier der Einsatz der Funktion clone(), um eine separate Referenz auf die Tabelle zu bekommen:

    ($farben.Clone()).GetEnumerator() |
    foreach{ if($_.value -eq "Grün"){$farben.remove($_.key)} }

    Wenn man alle Keys entfernen möchte, dann dient die Funktion clear() diesem Zweck:

    $farben.Clear()

    Hash-Tabelle sortieren

    Ein naheliegender Ansatz könnte darin bestehen, die Ausgabe der Variablen über eine Pipe an Sort-Object zu übergeben:

    $farben | sort #funktioniert nicht

    Wie wir aber schon weiter oben gesehen haben, liefert die Eingabe des Variablennamens die gesamte Hash-Tabelle als ein Objekt zurück, ein Sortieren der Elemente ist daher so nicht zu erwarten.

    Auch für das Sortieren muss man somit über das Dictionary iterieren, wobei sich wieder die Methode GetEnumerator() als nützlich erweist:

    $farben.GetEnumerator() | sort -Property name

    Dieses Beispiel sortiert die Elemente nach dem Schlüssel. Möchte man sie stattdessen nach den Werten ordnen, dann ersetzt man im obigen Aufruf name durch value.

    Um einen Hashtable zu sortieren, muss man über die Schlüssel iterieren.

    Man könnte natürlich auf die Idee kommen, die Schlüssel beim Anlegen des Hashtables gleich in sortierter Reihenfolge einzugeben. Allerdings zeigt sich dann schnell, dass PowerShell diese Sortierung nicht beibehält und die Key/Value-Paare in beliebiger Reihenfolge ausgibt.

    Dieses Verhalten kann man jedoch abstellen, indem man seit PowerShell 3.0 das Keyword [ordered] verwendet:

    $farben = [ordered]@{Auto ="Schwarz"; Wand="Weiß"; Ei ="Gelb"; Himmel="Blau"}

    Es ist allerdings nicht möglich, damit eine vorhandene Hash-Tabelle durch einen Cast in eine geordnete Sammlung zu konvertieren.

    Splatting: Aufruf von Cmdlets mit Hashtable

    Eine häufige Anwendung von Hash-Tabellen besteht darin, die Parameter und ihre Werte darin zu speichern, bevor man sie an ein Cmdlet übergibt. Gerade bei Cmdlets mit sehr vielen Parametern, beispielsweise New-ADUser, erreicht man dadurch einen deutlich übersichtlicheren Code.

    Anstatt das Cmdlet wie üblich so aufzurufen:

    New-ADUser -Name Max.Meier -GivenName Max -Surname Meier -Path "OU=Benutzer,DC=contoso,DC=Com"

    könnte man folgendermaßen vorgehen:

    Zu beachten ist hier, dass man dem Hashtable ein '@' und nicht ein '$' voran­stellt.

    Keine Kommentare