Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Ich möchte einen Schritt zurückgehen und über Hashtables sprechen. Ich verwende sie jetzt immer. Ich habe jemanden über sie unterrichtet, nachdem unsere Benutzergruppenbesprechung letzte Nacht stattfand, und ich erkannte, dass ich die gleiche Verwirrung über sie hatte wie er hatte. Hashtables sind in PowerShell wirklich wichtig, daher ist es gut, ein solides Verständnis dafür zu haben.
Hinweis
Die originale Version dieses Artikels erschien auf dem Blog, der von @KevinMarquette geschrieben wurde. Das PowerShell-Team danke Kevin für das Teilen dieser Inhalte mit uns. Bitte schauen Sie sich seinen Blog bei PowerShellExplained.com an.
Hashtable als Sammlung von Elementen
Ich möchte, dass Sie zuerst eine Hashtable als Sammlung in der herkömmlichen Definition einer Hashtabelle sehen. Diese Definition gibt Ihnen ein grundlegendes Verständnis dafür, wie sie funktionieren, wenn sie später für komplexere Inhalte verwendet werden. Das Überspringen dieses Verständnisses ist oft eine Ursache für Verwirrung.
Was ist ein Array?
Bevor ich zu einer Hashtabelle springe, muss ich zuerst Arrays erwähnen. Für diese Diskussion ist ein Array eine Liste oder Auflistung von Werten oder Objekten.
$array = @(1,2,3,5,7,11)
Sobald Sie Ihre Elemente in einem Array haben, können Sie entweder zum Durchlaufen der Liste oder zum Zugreifen auf einzelne Elemente im Array einen Index verwenden foreach .
foreach($item in $array)
{
Write-Output $item
}
Write-Output $array[3]
Sie können Werte auch mit einem Index auf die gleiche Weise aktualisieren.
$array[2] = 13
Ich habe gerade nur einen ersten Einblick in Arrays bekommen, aber das sollte helfen, sie in den richtigen Kontext einzuordnen, während ich zu Hashtables übergehe.
Was ist eine Hashtabelle?
Ich werde mit einer grundlegenden technischen Beschreibung beginnen, welche Hashtables im allgemeinen sind, bevor ich in die anderen Verwendungsmöglichkeiten von PowerShell wechsele.
Eine Hashtabelle ist eine Datenstruktur, ähnlich wie ein Array, mit der Ausnahme, dass Sie jeden Wert (Objekt) mithilfe eines Schlüssels speichern. Es ist ein einfacher Schlüssel-/Wertspeicher. Zunächst erstellen wir eine leere Hashtabelle.
$ageList = @{}
Beachten Sie, dass geschweifte Klammern anstelle von runden Klammern verwendet werden, um eine Hashtabelle zu definieren. Anschließend fügen wir ein Element mithilfe eines Schlüssels wie folgt hinzu:
$key = 'Kevin'
$value = 36
$ageList.Add( $key, $value )
$ageList.Add( 'Alex', 9 )
Der Name der Person ist der Schlüssel, und ihr Alter ist der Wert, den ich speichern möchte.
Verwenden der Klammern für den Zugriff
Nachdem Sie die Werte zur Hashtabelle hinzugefügt haben, können Sie sie mithilfe desselben Schlüssels wieder herausziehen (anstatt einen numerischen Index wie für ein Array zu verwenden).
$ageList['Kevin']
$ageList['Alex']
Wenn ich Kevins Alter will, verwende ich seinen Namen, um darauf zuzugreifen. Wir können diesen Ansatz verwenden, um werte auch in der Hashtabelle hinzuzufügen oder zu aktualisieren. Dies ist genau wie die oben beschriebene Add()-Methode verwenden.
$ageList = @{}
$key = 'Kevin'
$value = 36
$ageList[$key] = $value
$ageList['Alex'] = 9
Es gibt eine weitere Syntax, die Sie für den Zugriff auf und das Aktualisieren von Werten verwenden können, die ich in einem späteren Abschnitt behandeln werde. Wenn Sie von einer anderen Programmiersprache zu PowerShell wechseln, sollten diese Beispiele zu Ihrer bisherigen Verwendung von Hashtables passen.
Erstellen von Hashtables mit Werten
Bisher habe ich für diese Beispiele eine leere Hashtabelle erstellt. Sie können die Schlüssel und Werte beim Erstellen vorab auffüllen.
$ageList = @{
Kevin = 36
Alex = 9
}
Als Nachschlagetabelle
Der tatsächliche Wert dieses Typs einer Hashtabelle besteht darin, dass Sie sie als Nachschlagetabelle verwenden können. Hier ist ein einfaches Beispiel.
$environments = @{
Prod = 'SrvProd05'
QA = 'SrvQA02'
Dev = 'SrvDev12'
}
$server = $environments[$env]
In diesem Beispiel geben Sie eine Umgebung für die $env Variable an und wählen den richtigen Server aus. Sie können eine switch($env){...} Für eine Auswahl wie diese verwenden, aber eine Hashtabelle ist eine schöne Option.
Dies wird noch besser, wenn Sie die Nachschlagetabelle dynamisch erstellen, um sie später zu verwenden. Denken Sie also daran, diesen Ansatz zu verwenden, wenn Sie einen Querverweis auf etwas benötigen. Ich denke, wir würden dies noch mehr sehen, wenn PowerShell nicht so gut darin war, innerhalb der Pipeline zu filtern.Where-Object Wenn Sie jemals in einer Situation sind, in der Leistung wichtig ist, muss dieser Ansatz berücksichtigt werden.
Ich sage nicht, dass es schneller ist, aber es passt in die Regel Wenn Leistung wichtig ist, testen Sie es.
Mehrfachauswahl
Im Allgemeinen stellen Sie sich eine Hashtabelle als Schlüssel-Wert-Paar vor, bei dem Sie einen Schlüssel angeben und einen Wert abrufen. PowerShell ermöglicht es Ihnen, ein Array von Schlüsseln bereitzustellen, um mehrere Werte abzurufen.
$environments[@('QA','DEV')]
$environments[('QA','DEV')]
$environments['QA','DEV']
In diesem Beispiel verwende ich die gleiche Hashtable von oben und zeige drei verschiedene Array-Stile, um die Übereinstimmungen abzurufen. Dies ist ein verborgenes Juwel in PowerShell, das die meisten Personen nicht kennen.
Iterieren von Hashtables
Da es sich bei einer Hashtabelle um eine Sammlung von Schlüssel-Wert-Paaren handelt, durchlaufen Sie sie anders als für ein Array oder eine normale Liste von Elementen.
Als Erstes ist zu beachten, dass wenn Sie Ihre Hash-Tabelle weiterleiten, die Pipeline sie wie ein Objekt behandelt.
PS> $ageList | Measure-Object
count : 1
Obwohl die Count Eigenschaft Ihnen sagt, wie viele Werte sie enthält.
PS> $ageList.Count
2
Sie können dieses Problem umgehen, indem Sie die Values Eigenschaft verwenden, wenn sie nur die Werte benötigen.
PS> $ageList.Values | Measure-Object -Average
Count : 2
Average : 22.5
Häufig ist es nützlicher, die Schlüssel aufzulisten und sie für den Zugriff auf die Werte zu verwenden.
PS> $ageList.Keys | ForEach-Object{
$message = '{0} is {1} years old!' -f $_, $ageList[$_]
Write-Output $message
}
Kevin is 36 years old
Alex is 9 years old
Hier sehen Sie dasselbe Beispiel mit einer foreach(){...} Schleife.
foreach($key in $ageList.Keys)
{
$message = '{0} is {1} years old' -f $key, $ageList[$key]
Write-Output $message
}
Wir durchlaufen jeden Schlüssel in der Hashtabelle und verwenden ihn dann, um auf den entsprechenden Wert zuzugreifen. Dies ist ein gängiges Muster beim Arbeiten mit Hashtables als Datenstruktur.
GetEnumerator()
Das bringt uns zum Iterieren über unsere Hashtabelle GetEnumerator().
$ageList.GetEnumerator() | ForEach-Object{
$message = '{0} is {1} years old!' -f $_.Key, $_.Value
Write-Output $message
}
Der Enumerator gibt Ihnen jedes Schlüssel-Wert-Paar nacheinander. Es wurde speziell für diesen Anwendungsfall entwickelt. Vielen Dank an Mark Kraus, mich daran zu erinnern.
Fehlerhafte Aufzählung
Ein wichtiges Detail ist, dass Sie eine Hashtabelle nicht ändern können, während sie aufgezählt wird. Wenn wir mit unserem grundlegenden $environments Beispiel beginnen:
$environments = @{
Prod = 'SrvProd05'
QA = 'SrvQA02'
Dev = 'SrvDev12'
}
Und der Versuch, jeden Schlüssel auf denselben Serverwert festzulegen, schlägt fehl.
$environments.Keys | ForEach-Object {
$environments[$_] = 'SrvDev03'
}
An error occurred while enumerating through a collection: Collection was modified;
enumeration operation may not execute.
+ CategoryInfo : InvalidOperation: tableEnumerator:HashtableEnumerator) [],
RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
Dies schlägt auch fehl, auch wenn es so aussieht, als wäre es auch in Ordnung:
foreach($key in $environments.Keys) {
$environments[$key] = 'SrvDev03'
}
Collection was modified; enumeration operation may not execute.
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException
Der Trick zu dieser Situation besteht darin, die Schlüssel zu klonen, bevor Sie die Enumeration ausführen.
$environments.Keys.Clone() | ForEach-Object {
$environments[$_] = 'SrvDev03'
}
Hinweis
Sie können keine Hashtable mit einem einzelnen Schlüssel klonen. PowerShell löst einen Fehler aus. Stattdessen konvertieren Sie die Keys-Eigenschaft in ein Array und iterieren dann über das Array.
@($environments.Keys) | ForEach-Object {
$environments[$_] = 'SrvDev03'
}
Hashtable als Sammlung von Eigenschaften
Bisher waren die Objekte, die wir in unsere Hashtable eingetragen haben, alle derselben Objekttypen. Ich habe Altersstufen in all diesen Beispielen verwendet, und der Schlüssel war der Name der Person. Dies ist eine hervorragende Möglichkeit, sie zu betrachten, wenn Ihre Sammlung von Objekten jeweils einen Namen hat. Eine weitere gängige Möglichkeit zum Verwenden von Hashtables in PowerShell besteht darin, eine Sammlung von Eigenschaften zu enthalten, bei denen der Schlüssel der Name der Eigenschaft ist. Ich werde in diesem nächsten Beispiel auf diese Idee eingehen.
Eigenschaftenbasierter Zugriff
Die Verwendung von eigenschaftenbasiertem Zugriff ändert die Dynamik von Hashtables und deren Verwendung in PowerShell. Hier ist unser übliches Beispiel von oben, das die Schlüssel als Eigenschaften behandelt.
$ageList = @{}
$ageList.Kevin = 35
$ageList.Alex = 9
Genau wie in den obigen Beispielen fügt dieses Beispiel diese Schlüssel hinzu, wenn sie in der Hashtabelle noch nicht vorhanden sind. Je nachdem, wie Sie Ihre Schlüssel definiert haben und was Ihre Werte sind, ist dies entweder ein wenig seltsam oder perfekt geeignet. Das Beispiel für die Altersliste hat bis zu diesem Punkt sehr gut funktioniert. Wir brauchen ein neues Beispiel, damit dies in Zukunft passend erscheint.
$person = @{
name = 'Kevin'
age = 36
}
Wir können Attribute auf dem $person auf diese Weise hinzufügen und darauf zugreifen.
$person.city = 'Austin'
$person.state = 'TX'
Plötzlich beginnt diese Hashtabelle zu fühlen und wie ein Objekt zu wirken. Es ist immer noch eine Sammlung von Dingen, daher gelten alle oben genannten Beispiele weiterhin. Wir nähern uns einfach aus einer anderen Sicht.
Suchen nach Schlüsseln und Werten
In den meisten Fällen können Sie den Wert einfach wie folgt überprüfen:
if( $person.age ){...}
Es ist einfach, aber die Quelle vieler Fehler für mich, weil ich ein wichtiges Detail in meiner Logik übersehen habe. Ich begann, es zu verwenden, um zu testen, ob ein Schlüssel vorhanden war. Wenn der Wert $false oder null war, würde diese Anweisung $false unerwartet zurückgeben.
if( $person.age -ne $null ){...}
Dies umgeht das Problem für Nullwerte, aber nicht für $null im Vergleich zu nicht vorhandenen Schlüsseln. In den meisten Fällen müssen Sie diese Unterscheidung nicht treffen, aber es gibt Methoden, wenn Sie dies tun.
if( $person.ContainsKey('age') ){...}
Wir haben auch ein ContainsValue() für die Situation, in der Sie einen Wert prüfen müssen, ohne den Schlüssel zu kennen oder die komplette Sammlung durchlaufen zu müssen.
Entfernen und Löschen von Schlüsseln
Sie können Schlüssel mit der Remove() Methode entfernen.
$person.Remove('age')
Wenn Sie ihnen einen $null Wert zuweisen, bleibt Ihnen nur ein Schlüssel zugewiesen, der einen $null Wert aufweist.
Eine gängige Möglichkeit zum Löschen einer Hashtabelle besteht darin, sie einfach in eine leere Hashtabelle zu initialisieren.
$person = @{}
Während dies funktioniert, versuchen Sie stattdessen, die Clear() Methode zu verwenden.
$person.Clear()
Dies ist einer dieser Fälle, in denen die Verwendung der Methode Selbstdokumentierungscode erstellt und die Intentionen des Codes deutlich macht.
Alle lustigen Sachen
Sortierte Hashtabellen
Standardmäßig sind Hashtables nicht geordnet (oder sortiert). Im herkömmlichen Kontext spielt die Reihenfolge keine Rolle, wenn Sie immer einen Schlüssel für den Zugriff auf Werte verwenden. Möglicherweise stellen Sie fest, dass die Eigenschaften in der Reihenfolge verbleiben sollen, in der Sie sie definieren. Glücklicherweise gibt es eine Möglichkeit, dies mit dem ordered Schlüsselwort zu tun.
$person = [ordered]@{
name = 'Kevin'
age = 36
}
Wenn Sie nun die Schlüssel und Werte aufzählen, bleiben sie in dieser Reihenfolge.
Inlinehashtables
Wenn Sie eine Hashtabelle in einer Zeile definieren, können Sie die Schlüssel-Wert-Paare durch ein Semikolon trennen.
$person = @{ name = 'kevin'; age = 36; }
Dies ist praktisch, wenn Sie sie in der Pipeline erstellen.
Benutzerdefinierte Ausdrücke in allgemeinen Pipelinebefehlen
Es gibt einige Cmdlets, die die Verwendung von Hashtables zum Erstellen benutzerdefinierter oder berechneter Eigenschaften unterstützen. Sie sehen dies häufig mit Select-Object und Format-Table. Die Hashtables weisen eine spezielle Syntax auf, die wie folgt aussieht, wenn sie vollständig erweitert wird.
$property = @{
Name = 'TotalSpaceGB'
Expression = { ($_.Used + $_.Free) / 1GB }
}
Das Name Cmdlet würde diese Spalte beschriften. Dies Expression ist ein Skriptblock, der ausgeführt wird, wobei $_ der Wert des Objekts in der Pipe angegeben ist. Dies ist das Skript in Aktion:
$drives = Get-PSDrive | where Used
$drives | Select-Object -Property Name, $property
Name TotalSpaceGB
---- ------------
C 238.472652435303
Ich habe dies in eine Variable gesetzt, aber es könnte leicht inline definiert werden, und Sie können Name auf n und Expression auf e verkürzen, während Sie dabei sind.
$drives | Select-Object -Property Name, @{n='TotalSpaceGB';e={($_.Used + $_.Free) / 1GB}}
Ich persönlich mag es nicht, wie lang Befehle dadurch werden, und es fördert oft einige schlechte Verhaltensweisen, auf die ich nicht näher eingehen möchte. Ich bin wahrscheinlicher, dass ich eine neue Hashtabelle oder pscustomobject mit allen Feldern und Eigenschaften erstellt, die ich verwenden möchte, anstatt diesen Ansatz in Skripts zu verwenden. Es gibt eine Menge Code da draußen, der dies tut, daher wollte ich, dass Sie sich dessen bewusst sind. Ich spreche später über die Erstellung eines pscustomobject.
Benutzerdefinierter Sortierausdruck
Es ist einfach, eine Auflistung zu sortieren, wenn die Objekte über die Daten verfügen, nach denen Sie sortieren möchten. Sie können dem Objekt entweder die Daten hinzufügen, bevor Sie es sortieren, oder einen benutzerdefinierten Ausdruck erstellen für Sort-Object.
Get-ADUser | Sort-Object -Property @{ e={ Get-TotalSales $_.Name } }
In diesem Beispiel verwende ich eine Liste der Benutzer und verwende ein benutzerdefiniertes Cmdlet, um zusätzliche Informationen nur für die Sortierung abzurufen.
Sortieren einer Liste von Hash-Tabellen
Wenn Sie eine Liste mit Hashtables haben, die Sie sortieren möchten, stellen Sie fest, dass die Sort-Object Schlüssel nicht als Eigenschaften behandelt werden. Wir können dies umgehen, indem wir einen benutzerdefinierten Sortierausdruck verwenden.
$data = @(
@{name='a'}
@{name='c'}
@{name='e'}
@{name='f'}
@{name='d'}
@{name='b'}
)
$data | Sort-Object -Property @{e={$_.name}}
Splatting von Hashtabellen bei Cmdlets
Dies ist einer meiner Lieblings dinge über Hashtables, die viele Leute nicht früh entdecken. Die Idee besteht darin, dass Sie die Eigenschaften nicht in einer Zeile an ein Cmdlet übergeben, sondern sie zuerst in eine Hashtabelle packen können. Anschließend können Sie die Hashtabelle der Funktion auf besondere Weise zugeben. Hier ist ein Beispiel für das Erstellen eines DHCP-Bereichs auf herkömmliche Weise.
Add-DhcpServerV4Scope -Name 'TestNetwork' -StartRange '10.0.0.2' -EndRange '10.0.0.254' -SubnetMask '255.255.255.0' -Description 'Network for testlab A' -LeaseDuration (New-TimeSpan -Days 8) -Type "Both"
Ohne Splatting müssen alle diese Elemente in einer einzelnen Zeile definiert werden. Es verschwindet entweder vom Bildschirm oder bricht um, wo es gerade passt. Vergleichen Sie dies nun mit einem Befehl, der splatting verwendet.
$DHCPScope = @{
Name = 'TestNetwork'
StartRange = '10.0.0.2'
EndRange = '10.0.0.254'
SubnetMask = '255.255.255.0'
Description = 'Network for testlab A'
LeaseDuration = (New-TimeSpan -Days 8)
Type = "Both"
}
Add-DhcpServerV4Scope @DHCPScope
Die Verwendung des @ Zeichens anstelle des $ ruft den Splat-Vorgang auf.
Nehmen Sie sich einfach einen Moment Zeit, um zu schätzen, wie einfach dieses Beispiel zu lesen ist. Sie sind derselbe Befehl mit allen gleichen Werten. Der zweite ist einfacher zu verstehen und in Zukunft zu pflegen.
Ich nutze das „splatting“, immer wenn der Befehl zu lang wird. Ich definiere „zu lang“ als das, was mein Fenster zum Nach-rechts-Scrollen bringt. Wenn ich drei Eigenschaften für eine Funktion verwende, werde ich sie wahrscheinlich unter Verwendung eines Splatted-Hashtables umschreiben.
Splatting für optionale Parameter
Eine der häufigsten Methoden, mit denen ich Splatting verwende, besteht darin, optionale Parameter zu behandeln, die von einem anderen Ort in meinem Skript stammen. Angenommen, ich habe eine Funktion, die einen Get-CimInstance Aufruf umschließt, der ein optionales $Credential Argument enthält.
$CIMParams = @{
ClassName = 'Win32_BIOS'
ComputerName = $ComputerName
}
if($Credential)
{
$CIMParams.Credential = $Credential
}
Get-CimInstance @CIMParams
Ich beginne mit der Erstellung meiner Hashtable mit allgemeinen Parametern. Dann füge ich das $Credential hinzu, wenn es vorhanden ist.
Da ich hier splatting verwende, muss ich den Aufruf zu Get-CimInstance nur einmal in meinem Code haben. Dieses Entwurfsmuster ist sehr sauber und kann viele optionale Parameter problemlos verarbeiten.
Um fair zu sein, könnten Sie Ihre Befehle so schreiben, dass sie Werte für Parameter zulassen $null. Sie haben nicht immer die Kontrolle über die anderen Befehle, die Sie aufrufen.
Mehrere Splats
Sie können mehrere Hashtables für dasselbe Cmdlet splatieren. Wenn wir unser ursprüngliches Beispiel für splatting noch einmal betrachten:
$Common = @{
SubnetMask = '255.255.255.0'
LeaseDuration = (New-TimeSpan -Days 8)
Type = "Both"
}
$DHCPScope = @{
Name = 'TestNetwork'
StartRange = '10.0.0.2'
EndRange = '10.0.0.254'
Description = 'Network for testlab A'
}
Add-DhcpServerv4Scope @DHCPScope @Common
Ich verwende diese Methode, wenn ich einen allgemeinen Satz von Parametern habe, die ich an viele Befehle übergibt.
Splatting für sauberen Code
Es gibt nichts Falsches daran, einen einzelnen Parameter zu "splatten", wenn es Ihren Code übersichtlicher macht.
$log = @{Path = '.\logfile.log'}
Add-Content "logging this command" @log
Splatting von ausführbaren Dateien
Das Splatting funktioniert auch für einige ausführbare Dateien, die eine Syntax im /param:value-Format verwenden.
Robocopy.exehat beispielsweise einige Parameter wie diese.
$robo = @{R=1;W=1;MT=8}
robocopy source destination @robo
Ich weiß nicht, dass dies alles so nützlich ist, aber ich fand es interessant.
Hinzufügen von Hashtables
Hashtables unterstützen den Additionsoperator, um zwei Hashtables zu kombinieren.
$person += @{Zip = '78701'}
Dies funktioniert nur, wenn die beiden Hashtables keinen gemeinsamen Schlüssel haben.
Geschachtelte Hashtables
Wir können Hashtables als Werte in einer Hashtabelle verwenden.
$person = @{
name = 'Kevin'
age = 36
}
$person.location = @{}
$person.location.city = 'Austin'
$person.location.state = 'TX'
Ich begann mit einer einfachen Hashtabelle mit zwei Schlüsseln. Ich habe einen Schlüssel location mit einer leeren Hashtabelle hinzugefügt. Dann habe ich die letzten beiden Elemente zu dieser location Hashtabelle hinzugefügt. Wir können dies auch inline tun.
$person = @{
name = 'Kevin'
age = 36
location = @{
city = 'Austin'
state = 'TX'
}
}
Dadurch wird dieselbe Hashtabelle erstellt, die wir oben gesehen haben und auf die Eigenschaften auf die gleiche Weise zugreifen können.
$person.location.city
Austin
Es gibt viele Möglichkeiten, die Struktur Ihrer Objekte zu gestalten. Hier ist eine zweite Möglichkeit, eine geschachtelte Hashtabelle zu betrachten.
$people = @{
Kevin = @{
age = 36
city = 'Austin'
}
Alex = @{
age = 9
city = 'Austin'
}
}
Dadurch wird das Konzept der Verwendung von Hashtables als Sammlung von Objekten und einer Auflistung von Eigenschaften kombiniert. Auf die Werte kann immer noch leicht zugegriffen werden, auch wenn sie mit dem von Ihnen gewünschten Ansatz geschachtelt sind.
PS> $people.kevin.age
36
PS> $people.kevin['city']
Austin
PS> $people['Alex'].age
9
PS> $people['Alex']['City']
Austin
Ich tendiere dazu, die Dot-Eigenschaft zu verwenden, wenn ich sie wie eine Eigenschaft behandle. Dies sind im Allgemeinen Dinge, die ich in meinem Code statisch definiert habe und auswendig kenne. Wenn ich die Liste durchgehe oder programmgesteuert auf die Schlüssel zugreife, verwende ich die Klammern, um den Schlüsselnamen anzugeben.
foreach($name in $people.Keys)
{
$person = $people[$name]
'{0}, age {1}, is in {2}' -f $name, $person.age, $person.city
}
Wenn Sie die Möglichkeit haben, Hashtables zu verschachteln, bietet Ihnen das viel Flexibilität und viele Optionen.
Betrachten geschachtelter Hashtables
Sobald Sie mit dem Verschachteln von Hashtables beginnen, benötigen Sie eine einfache Möglichkeit, sie in der Konsole anzuzeigen. Wenn ich diese letzte Hashtabelle nehme, erhalte ich eine Ausgabe, die wie folgt aussieht und nur so tief geht:
PS> $people
Name Value
---- -----
Kevin {age, city}
Alex {age, city}
Mein bevorzugter Befehl, um diese Dinge zu betrachten, ist ConvertTo-Json, weil er sehr übersichtlich ist und ich JSON häufig für andere Aufgaben verwende.
PS> $people | ConvertTo-Json
{
"Kevin": {
"age": 36,
"city": "Austin"
},
"Alex": {
"age": 9,
"city": "Austin"
}
}
Auch wenn Sie JSON nicht kennen, sollten Sie in der Lage sein, zu sehen, wonach Sie suchen. Es gibt einen Format-Custom Befehl für strukturierte Daten wie diese, aber ich mag die JSON-Ansicht noch besser.
Erstellen von Objekten
Manchmal braucht man einfach ein Objekt, und die Verwendung einer Hashtabelle zum Speichern von Eigenschaften reicht einfach nicht aus, um die Aufgabe zu erledigen. Am häufigsten möchten Sie die Schlüssel als Spaltennamen anzeigen. Mit einem pscustomobject wird das einfach.
$person = [pscustomobject]@{
name = 'Kevin'
age = 36
}
$person
name age
---- ---
Kevin 36
Auch wenn Sie es nicht als pscustomobject erstellen, können Sie es jederzeit bei Bedarf umwandeln.
$person = @{
name = 'Kevin'
age = 36
}
[pscustomobject]$person
name age
---- ---
Kevin 36
Ich habe bereits einen detaillierten Bericht über pscustomobject, den Sie nach diesem lesen sollten. Es baut auf viele der hier gelernten Dinge auf.
Lesen und Schreiben von Hashtabellen in eine Datei
Speichern in CSV
Schwierigkeiten beim Abrufen einer Hashtabelle zum Speichern in einer CSV-Datei ist eine der Schwierigkeiten, auf die ich oben bezuge. Konvertieren Sie Die Hashtabelle in eine pscustomobject Datei, und sie wird ordnungsgemäß in CSV gespeichert. Dies hilft, wenn Sie mit einer pscustomobject Spalte beginnen, damit die Spaltenreihenfolge erhalten bleibt. Sie können sie aber bei Bedarf in eine pscustomobject Inline umwandeln.
$person | ForEach-Object{ [pscustomobject]$_ } | Export-Csv -Path $path
Sehen Sie sich auch meinen Artikel über die Verwendung eines pscustomobject an.
Speichern einer geschachtelten Hashtabelle in einer Datei
Wenn ich eine geschachtelte Hashtable in einer Datei speichern und dann wieder lesen muss, verwende ich die JSON-Cmdlets, um sie zu erledigen.
$people | ConvertTo-Json | Set-Content -Path $path
$people = Get-Content -Path $path -Raw | ConvertFrom-Json
Es gibt zwei wichtige Punkte zu dieser Methode. Erstens ist, dass der JSON-Code multiline geschrieben wird, sodass ich die -Raw Option verwenden muss, um ihn wieder in eine einzelne Zeichenfolge zu lesen. Der Zweite ist, dass das importierte Objekt nicht mehr ein [hashtable]. Es ist jetzt ein [pscustomobject], und das kann Probleme verursachen, wenn Sie es nicht erwarten.
Achten Sie auf tief geschachtelte Hashtables. Wenn Sie sie in JSON konvertieren, erhalten Sie möglicherweise nicht die erwarteten Ergebnisse.
@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json
{
"a": {
"b": {
"c": "System.Collections.Hashtable"
}
}
}
Verwenden Sie den Depth-Parameter , um sicherzustellen, dass Sie alle geschachtelten Hashtables erweitert haben.
@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json -Depth 3
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Wenn Sie es beim Import zu einem [hashtable] benötigen, müssen Sie die Befehle Export-CliXml und Import-CliXml verwenden.
Konvertieren von JSON in Hashtable
Wenn Sie JSON in einen [hashtable]konvertieren müssen, gibt es eine Möglichkeit, die ich mit dem JavaScriptSerializer in .NET tun kann.
[Reflection.Assembly]::LoadWithPartialName("System.Web.Script.Serialization")
$JSSerializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
$JSSerializer.Deserialize($json,'Hashtable')
Ab PowerShell v6 verwendet die JSON-Unterstützung die NewtonSoft-JSON.NET und fügt Hashtable-Unterstützung hinzu.
'{ "a": "b" }' | ConvertFrom-Json -AsHashtable
Name Value
---- -----
a b
PowerShell 6.2 hat den Depth-Parameter hinzugefügt.ConvertFrom-Json Die Standardtiefe ist 1024.
Direktes Lesen aus einer Datei
Wenn Sie über eine Datei verfügen, die eine Hashtable mit PowerShell-Syntax enthält, gibt es eine Möglichkeit, sie direkt zu importieren.
$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create( $content )
$scriptBlock.CheckRestrictedLanguage( $allowedCommands, $allowedVariables, $true )
$hashtable = ( & $scriptBlock )
Sie importiert den Inhalt der Datei in ein scriptblock, überprüft dann, ob sie keine anderen PowerShell-Befehle enthält, bevor sie ausgeführt wird.
Wussten Sie in diesem Hinweis, dass ein Modulmanifest (die .psd1 Datei) nur eine Hashtabelle ist?
Schlüssel können ein beliebiges Objekt sein.
Meistens sind die Schlüssel nur Zeichenfolgen. So können wir Zitate um alles herum setzen und es zu einem Schlüssel machen.
$person = @{
'full name' = 'Kevin Marquette'
'#' = 3978
}
$person['full name']
Sie können einige seltsame Dinge tun, die Sie möglicherweise nicht erkannt haben, dass Sie es tun könnten.
$person.'full name'
$key = 'full name'
$person.$key
Nur weil Sie etwas tun können, bedeutet dies nicht, dass Sie sollten. Dieser letzte sieht wie ein Fehler aus, der darauf wartet, und würde leicht von jedem, der Ihren Code liest, missverstanden werden.
Technisch muss Ihr Schlüssel keine Zeichenfolge sein, aber sie sind einfacher zu überlegen, wenn Sie nur Zeichenfolgen verwenden. Die Indizierung funktioniert jedoch nicht gut mit den komplexen Schlüsseln.
$ht = @{ @(1,2,3) = "a" }
$ht
Name Value
---- -----
{1, 2, 3} a
Der Zugriff auf einen Wert in der Hashtabelle anhand seines Schlüssels funktioniert nicht immer. Beispiel:
$key = $ht.Keys[0]
$ht.$($key)
a
$ht[$key]
a
Wenn es sich bei dem Schlüssel um ein Array handelt, müssen Sie die $key Variable in einem Unterausdruck umschließen, damit sie mit der Memberzugriffsnotation (.) verwendet werden kann. Sie können auch die Arrayindexnotation ([]) verwenden.
Verwendung bei automatischen Variablen
$PSBoundParameters
$PSBoundParameters ist eine automatische Variable, die nur im Kontext einer Funktion vorhanden ist. Sie enthält alle Parameter, mit denen die Funktion aufgerufen wurde. Dies ist nicht genau eine Hashtabelle, aber hinreichend ähnlich, dass man sie so behandeln kann.
Dazu gehören das Entfernen von Schlüsseln und das Übergeben an andere Funktionen. Wenn Sie feststellen, dass Sie Proxyfunktionen schreiben, schauen Sie sich diesen genauer an.
Weitere Informationen finden Sie unter about_Automatic_Variables .
PSBoundParameters-Falle
Eine wichtige Sache zu beachten ist, dass dies nur die Werte enthält, die als Parameter übergeben werden. Wenn Sie auch Parameter mit Standardwerten haben, aber nicht vom Aufrufer übergeben werden, $PSBoundParameters enthalten diese Werte nicht. Dies wird häufig übersehen.
$PSDefaultParameterValues
Mit dieser automatischen Variablen können Sie jedem Cmdlet Standardwerte zuweisen, ohne das Cmdlet zu ändern. Sehen Sie sich dieses Beispiel an.
$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"
Dadurch wird der Hashtable ein Eintrag $PSDefaultParameterValues hinzugefügt, mit dem UTF8 als Standardwert für den Parameter Out-File -Encoding festgelegt wird. Dies ist sitzungsspezifisch, daher sollten Sie sie in Ihrem $PROFILE platzieren.
Ich verwende dies oft, um Werte vorab zuzuweisen, die ich häufig eingebe.
$PSDefaultParameterValues[ "Connect-VIServer:Server" ] = 'VCENTER01.contoso.local'
Dies akzeptiert auch Wildcards, sodass Sie Werte in Massen festlegen können. Hier sind einige Möglichkeiten, die Sie verwenden können:
$PSDefaultParameterValues[ "Get-*:Verbose" ] = $true
$PSDefaultParameterValues[ "*:Credential" ] = Get-Credential
Eine ausführlichere Aufschlüsselung finden Sie in diesem großartigen Artikel zu "Automatic Defaults " von Michael Sorens.
Regex $Matches
Wenn Sie den -match Operator verwenden, wird eine automatische Variable namens $Matches erstellt, die die Ergebnisse der Übereinstimmung enthält. Wenn Sie Unterausdrücke in Ihrem Regulären Ausdruck haben, werden die Teilmatches ebenfalls aufgelistet.
$message = 'My SSN is 123-45-6789.'
$message -match 'My SSN is (.+)\.'
$Matches[0]
$Matches[1]
Benannte Treffer
Dies ist eine meiner Lieblingsfeatures, die die meisten Personen nicht kennen. Wenn Sie eine benannte regex-Übereinstimmung verwenden, können Sie auf diese Übereinstimmung anhand des Namens für die Übereinstimmungen zugreifen.
$message = 'My Name is Kevin and my SSN is 123-45-6789.'
if($message -match 'My Name is (?<Name>.+) and my SSN is (?<SSN>.+)\.')
{
$Matches.Name
$Matches.SSN
}
Im obigen Beispiel ist der Ausdruck (?<Name>.*) ein benannter Unterausdruck. Dieser Wert wird dann in die $Matches.Name Eigenschaft eingefügt.
Group-Object -AsHashtable
Ein wenig bekanntes Feature Group-Object besteht darin, dass einige Datasets für Sie in eine Hashtabelle umgewandelt werden können.
Import-Csv $Path | Group-Object -AsHashtable -Property Email
Dadurch wird jede Zeile zu einer Hashtabelle hinzugefügt und die angegebene Eigenschaft als Schlüssel verwendet, um darauf zuzugreifen.
Hashtabelle kopieren
Eine wichtige Sache zu wissen ist, dass Hashtables Objekte sind. Und jede Variable ist nur ein Verweis auf ein Objekt. Dies bedeutet, dass mehr Arbeit benötigt wird, um eine gültige Kopie einer Hashtabelle zu erstellen.
Zuweisen von Verweistypen
Wenn Sie über eine Hashtabelle verfügen und sie einer zweiten Variablen zuweisen, zeigen beide Variablen auf dieselbe Hashtabelle.
PS> $orig = @{name='orig'}
PS> $copy = $orig
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name
Copy: [copy]
Orig: [copy]
Dies hebt hervor, dass sie identisch sind, da das Ändern der Werte in einem fall auch die Werte in der anderen ändert. Dies gilt auch beim Übergeben von Hashtables an andere Funktionen. Wenn diese Funktionen Änderungen an dieser Hashtabelle vornehmen, wird das Original ebenfalls geändert.
Flache Kopien, einzelne Ebene
Wenn wir wie im obigen Beispiel eine einfache Hashtabelle haben, können wir Clone() verwenden, um eine flache Kopie zu erstellen.
PS> $orig = @{name='orig'}
PS> $copy = $orig.Clone()
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name
Copy: [copy]
Orig: [orig]
Auf diese Weise können wir einige grundlegende Änderungen an einem vornehmen, die sich nicht auf die andere auswirken.
Flache Kopien, geschachtelt
Der Grund dafür, warum sie als flache Kopie bezeichnet wird, ist, dass nur die Basisebeneneigenschaften kopiert werden. Wenn eine dieser Eigenschaften ein Verweistyp ist (z. B. eine andere Hashtabelle), zeigen diese geschachtelten Objekte weiterhin aufeinander.
PS> $orig = @{
person=@{
name='orig'
}
}
PS> $copy = $orig.Clone()
PS> $copy.person.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.person.name
PS> 'Orig: [{0}]' -f $orig.person.name
Copy: [copy]
Orig: [copy]
Sie können also sehen, dass der Verweis person nicht geklont wurde, obwohl ich die Hashtable geklont habe. Wir müssen eine tiefe Kopie erstellen, um wirklich eine zweite Hashtabelle zu haben, die nicht mit der ersten verknüpft ist.
Tiefe Kopien
Es gibt eine Reihe von Möglichkeiten, eine tiefe Kopie einer Hashtabelle zu erstellen (und sie als Hashtable beizubehalten). Hier ist eine Funktion, die PowerShell verwendet, um rekursiv eine tiefe Kopie zu erstellen:
function Get-DeepClone
{
[CmdletBinding()]
param(
$InputObject
)
process
{
if($InputObject -is [hashtable]) {
$clone = @{}
foreach($key in $InputObject.Keys)
{
$clone[$key] = Get-DeepClone $InputObject[$key]
}
return $clone
} else {
return $InputObject
}
}
}
Es behandelt keine anderen Verweistypen oder Arrays, aber es ist ein guter Ausgangspunkt.
Eine weitere Möglichkeit besteht darin, .NET zum Deserialisieren mithilfe von CliXml wie in dieser Funktion zu verwenden:
function Get-DeepClone
{
param(
$InputObject
)
$TempCliXmlString = [System.Management.Automation.PSSerializer]::Serialize($obj, [int32]::MaxValue)
return [System.Management.Automation.PSSerializer]::Deserialize($TempCliXmlString)
}
Bei extrem großen Hashtables ist die Deserialisierungsfunktion beim Skalieren schneller. Es gibt jedoch einige Faktoren, die Sie bei der Verwendung dieser Methode berücksichtigen sollten. Da cliXml verwendet wird, ist es speicherintensiv und wenn Sie riesige Hashtables klonen, ist dies möglicherweise ein Problem. Eine weitere Einschränkung der CliXml ist eine Tiefeneinschränkung von 48. Wenn Sie also über eine Hashtable mit 48 Ebenen geschachtelter Hashtables verfügen, schlägt das Klonen fehl, und es wird überhaupt keine Hashtabelle ausgegeben.
Irgendetwas anderes?
Ich habe in kurzer Zeit viel erreicht. Meine Hoffnung ist, dass Sie jedes Mal, wenn Sie dies lesen, etwas Neues lernen oder es besser verstehen. Da ich das gesamte Spektrum dieser Funktion behandelt habe, gibt es Aspekte, die sich vielleicht gerade nicht auf Sie beziehen. Das ist perfekt ok und wird abhängig davon erwartet, wie viel Sie mit PowerShell arbeiten.