Alles, was Sie schon immer über Arrays wissen wollten

Arrays sind ein grundlegendes Sprachfeature der meisten Programmiersprachen. Es handelt sich um eine Auflistung aus Werten oder Objekten, um die Sie kaum herumkommen werden. Sehen wir uns also Arrays und ihre Möglichkeiten einmal genauer an.

Hinweis

Die Originalversion dieses Artikels wurde im Blog von @KevinMarquette veröffentlicht. Das PowerShell-Team dankt Kevin Marquette, dass er diesen Inhalt mit uns teilt. Weitere Informationen finden Sie in seinem Blog auf PowerShellExplained.com.

Was ist ein Array?

Ich beginne mit einer grundlegenden technischen Beschreibung von Arrays und ihrer Verwendung in den meisten Programmiersprachen. Danach werde ich die weiteren Möglichkeiten der Verwendung in PowerShell vorstellen.

Ein Array ist eine Datenstruktur, die als Auflistung mehrerer Elemente dient. Sie können ein Array durchlaufen oder über einen Index auf einzelne Elemente zugreifen. Ein Array wird als sequenzieller Arbeitsspeicherblock erstellt, in dem die einzelnen Werte direkt nebeneinander gespeichert sind.

Ich werde im Verlauf dieses Artikels auf die Details eingehen.

Grundlegende Verwendung

Da Arrays ein so grundlegendes Feature von PowerShell sind, gibt es eine einfache Syntax für die Arbeit mit Arrays in PowerShell.

Erstellen eines Arrays

Ein leeres Array lässt sich mit @() erstellen.

PS> $data = @()
PS> $data.count
0

Wir können ein Array erstellen und mit Werten füllen, indem wir diese einfach in die runden Klammern @() einfügen.

PS> $data = @('Zero','One','Two','Three')
PS> $data.count
4

PS> $data
Zero
One
Two
Three

Dieses Array besteht aus vier Elementen. Wenn wir die Variable $data aufrufen, wird die Liste der Elemente angezeigt. Bei einem Zeichenfolgenarray erhalten wir eine Zeile pro Zeichenfolge.

Wir können ein Array auf mehreren Zeilen deklarieren. Das Komma ist in diesem Fall optional und wird in der Regel weggelassen.

$data = @(
    'Zero'
    'One'
    'Two'
    'Three'
)

Ich deklariere meine Arrays lieber so auf mehreren Zeilen. Das vereinfacht nicht nur bei mehreren Elementen das Lesen des Arrays, Sie können Ihre Arrays auch einfacher mit vorherigen Versionen vergleichen, wenn Sie die Quellcodeverwaltung verwenden.

Eine andere Syntax

Im Allgemeinen wird @() als Syntax zum Erstellen eines Arrays verwendet, aber durch Trennzeichen getrennte Listen funktionieren meistens auch.

$data = 'Zero','One','Two','Three'

Write-Output zum Erstellen von Arrays

Ein netter kleiner Trick, den ich nicht unerwähnt lassen möchte: Sie können Write-Output verwenden, um schnell Zeichenfolgen an der Konsole zu erstellen.

$data = Write-Output Zero One Two Three

Das ist wirklich praktisch, denn Sie müssen keine Anführungszeichen um die Zeichenfolgen setzen, wenn der Parameter Zeichenfolgen akzeptiert. In einem Skript würde ich nie so vorgehen, aber an der Konsole – kein Problem.

Zugreifen auf Elemente

Nachdem Sie jetzt über ein Array mit Elementen verfügen, möchten Sie sicher darauf zugreifen und diese Elemente aktualisieren.

Offset

Um auf einzelne Elemente zuzugreifen, verwenden wir die eckigen Klammern [] mit einem Offsetwert, der bei 0 beginnt. So rufen wir das erste Element im Array ab:

PS> $data = 'Zero','One','Two','Three'
PS> $data[0]
Zero

Wir verwenden hier 0, weil das erste Element am Anfang der Liste steht, wir also einen Offset von 0 Elementen benötigen, um zu diesem Element zu gelangen. Um zum zweiten Element zu gelangen, müssen wir einen Offset von 1 verwenden, um das erste Element zu überspringen.

PS> $data[1]
One

Das bedeutet, dass sich das letzte Element am Offsetwert 3 befindet.

PS> $data[3]
Three

Index

Jetzt sehen Sie, warum ich für dieses Beispiel diese Werte ausgewählt habe. Ich habe das Konzept als „Offset“ vorgestellt, weil es sich tatsächlich um einen solchen handelt. In der Regel wird der Offset aber meist als „Index“ bezeichnet. Ein Index, der bei 0 beginnt. Im weiteren Verlauf dieses Artikels werde ich den Offset als Index bezeichnen.

Spezielle Tricks für Indizes

In den meisten Sprachen können Sie nur eine einzige Zahl als Index angeben und erhalten ein einzelnes Element zurück. PowerShell ist da wesentlich flexibler. Sie können mehrere Indizes gleichzeitig verwenden. Indem wir eine Liste von Indizes angeben, können wir mehrere Elemente auswählen.

PS> $data[0,2,3]
Zero
Two
Three

Die Elemente werden basierend auf der Reihenfolge der angegebenen Indizes zurückgegeben. Wenn Sie einen Index duplizieren, erhalten Sie das entsprechende Element beide Male.

PS> $data[3,0,3]
Three
Zero
Three

Mit dem integrierten ..-Operator können wir eine Sequenz aus Zahlen angeben.

PS> $data[1..3]
One
Two
Three

Das funktioniert auch umgekehrt.

PS> $data[3..1]
Three
Two
One

Sie können auch negative Indexwerte angeben, um den Offset vom Ende der Liste anzugeben. Wenn Sie also das letzte Element einer Liste abrufen möchten, können Sie -1 verwenden.

PS> $data[-1]
Three

Beim Operator .. ist hierbei allerdings Vorsicht geboten. Die Sequenzen 0..-1 und -1..0 werden zu den Werten 0,-1 und -1,0 ausgewertet. Wenn Sie dieses Detail vergessen, könnten Sie fälschlicherweise davon ausgehen, dass $data[0..-1] alle Elemente aufzählt. Mit $data[0..-1] erhalten Sie dieselben Werte wie mit $data[0,-1] – das erste und das letzte Element im Array (und keinen der anderen Werte dazwischen). Hier finden Sie ein größeres Beispiel:

PS> $a = 1,2,3,4,5,6,7,8
PS> $a[2..-1]
3
2
1
8

Dies entspricht dem folgenden Beispiel:

PS> $a[2,1,0,-1]
3
2
1
8

Außerhalb des gültigen Bereichs

In den meisten Sprachen gilt Folgendes: Wenn Sie versuchen, auf den Index eines Elements zuzugreifen, das hinter dem Ende des Arrays liegt, wird ein Fehler oder eine Ausnahme zurückgegeben. PowerShell gibt einfach nichts zurück.

PS> $null -eq $data[9000]
True

In einem NULL-Array kann kein Index erstellt werden

Wenn Ihre Variable $null lautet und Sie versuchen, sie wie ein Array zu indizieren, wird eine System.Management.Automation.RuntimeException mit der Meldung Cannot index into a null array zurückgegeben.

PS> $empty = $null
PS> $empty[0]
Error: Cannot index into a null array.

Stellen Sie also sicher, dass Ihre Arrays nicht $null sind, bevor Sie versuchen, auf Elemente darin zuzugreifen.

Anzahl

Arrays und andere Auflistungen verfügen über eine count-Eigenschaft, die Sie darüber informiert, wie viele Elemente sich im Array befinden.

PS> $data.count
4

PowerShell 3.0 hat den meisten Objekten eine count-Eigenschaft hinzugefügt. Wenn Sie über ein einzelnes Objekt verfügen, sollte die Anzahl 1 zurückgegeben werden.

PS> $date = Get-Date
PS> $date.count
1

Selbst $null besitzt eine count-Eigenschaft, diese gibt allerdings 0 zurück.

PS> $null.count
0

Hier gibt es einige potenzielle Fallen, auf die ich weiter unten in diesem Artikel eingehen werde, wenn ich die Überprüfung auf $null oder leere Arrays erläutere.

Off-by-one-Fehler

Programmierfehler entstehen nicht selten dadurch, dass Arrays am Index 0 beginnen. Off-by-one-Fehler können auf zwei Arten entstehen.

Im ersten Fall möchten Sie eigentlich das zweite Element über den Index 2 abrufen, erhalten dadurch aber tatsächlich das dritte Element. Oder vielleicht verfügen Sie über vier Elemente, von denen Sie das letzte abrufen möchten, und verwenden daher die count-Eigenschaft für den Zugriff auf dieses letzte Element.

$data[ $data.count ]

PowerShell hindert Sie nicht daran, dies zu tun, und gibt Ihnen genau das Element zurück, das sich an Index 4 befindet: $null. Hierfür sollten Sie also $data.count - 1 oder -1 verwenden, wie etwas weiter oben erläutert.

PS> $data[ $data.count - 1 ]
Three

So können Sie den Index -1 verwenden, um das letzte Element abzurufen.

PS> $data[ -1 ]
Three

Lee Dailey hat mich darauf hingewiesen, dass wir auch $data.GetUpperBound(0) verwenden können, um die maximale Indexnummer abzurufen.

PS> $data.GetUpperBound(0)
3
PS> $data[ $data.GetUpperBound(0) ]
Three

Die zweithäufigste Fehlerquelle entsteht dadurch, dass eine Liste durchlaufen und der Durchlauf nicht rechtzeitig angehalten wird. Ich werde näher auf dieses Problem eingehen, wenn ich die for-Schleife erläutere.

Aktualisieren von Elementen

Wir können denselben Index zum Aktualisieren vorhandener Elemente im Array verwenden. So erhalten wir direkten Zugriff zum Aktualisieren einzelner Elemente.

$data[2] = 'dos'
$data[3] = 'tres'

Wenn wir versuchen, ein Element zu aktualisieren, das sich hinter dem letzten Element befindet, wird der Fehler Index was outside the bounds of the array. zurückgegeben.

PS> $data[4] = 'four'
Index was outside the bounds of the array.
At line:1 char:1
+ $data[4] = 'four'
+ ~~~~~~~~~~~~~
+ CategoryInfo          : OperationStopped: (:) [], IndexOutOfRangeException
+ FullyQualifiedErrorId : System.IndexOutOfRangeException

Dieses Problem werde ich später erläutern, wenn ich beschreibe, wie sich ein Array vergrößern lässt.

Iteration

An einem bestimmten Punkt werden Sie die gesamte Liste durchlaufen und eine Aktion für jedes Element im Array ausführen müssen.

Pipeline

Arrays und die PowerShell-Pipeline sind wie füreinander geschaffen. Dies ist eine der einfachsten Möglichkeiten, diese Werte zu verarbeiten. Wenn Sie ein Array an eine Pipeline übergeben, wird jedes Element im Array individuell verarbeitet.

PS> $data = 'Zero','One','Two','Three'
PS> $data | ForEach-Object {"Item: [$PSItem]"}
Item: [Zero]
Item: [One]
Item: [Two]
Item: [Three]

Wenn Sie $PSItem noch nie gesehen haben, müssen Sie zunächst nur wissen, dass es das gleiche ist wie $_. Sie können beide verwenden, weil beide das aktuelle Objekt in der Pipeline repräsentieren.

foreach-Schleife

Die ForEach-Schleife funktioniert gut bei Auflistungen. Die Syntax lautet foreach ( <variable> in <collection> ).

foreach ( $node in $data )
{
    "Item: [$node]"
}

foreach-Methode

Ich neige dazu, diese Methode zu vergessen, aber sie funktioniert bei einfachen Vorgängen wirklich gut. PowerShell ermöglicht den Aufruf von .ForEach() in einer Auflistung.

PS> $data.foreach({"Item [$PSItem]"})
Item [Zero]
Item [One]
Item [Two]
Item [Three]

.foreach() akzeptiert einen Parameter, bei dem es sich um einen Skriptblock handelt. Sie können die Klammern weglassen und einfach nur den Skriptblock angeben.

$data.foreach{"Item [$PSItem]"}

Diese Syntax ist weniger bekannt, funktioniert aber genauso gut. Diese foreach-Methode wurde in PowerShell 4.0 hinzugefügt.

for-Schleife

Die for-Schleife wird in den meisten anderen Sprachen ausgiebig genutzt, in PowerShell kommt sie jedoch nur selten vor. Wenn sie vorkommt, dann meist beim Durchlaufen eines Arrays.

for ( $index = 0; $index -lt $data.count; $index++)
{
    "Item: [{0}]" -f $data[$index]
}

Als Erstes initialisieren wir einen $index mit 0. Dann fügen wir die Bedingung hinzu, dass $index kleiner sein muss als $data.count. Schließlich geben wir an, dass bei jedem Durchlaufen der Schleife der Index um 1 erhöht werden muss. In diesem Fall ist $index++ die Kurzform von $index = $index + 1. Der Formatoperator (-f) wird verwendet, um den Wert von $data[$index] in die Ausgabezeichenfolge einzufügen.

Wann immer Sie eine for-Schleife verwenden, achten Sie besonders auf die Bedingung. Ich habe hier $index -lt $data.count verwendet. Vorsicht: Bei dieser Bedingung können Ihnen leicht kleine Fehler unterlaufen, die dann zu Off-by-one-Fehlern in Ihrer Logik führen. Beispiele für solche kleinen Fehler wären $index -le $data.count oder $index -lt ($data.count - 1). Das würde dazu führen, dass Ihr Ergebnis zu viele oder zu wenige Elemente verarbeitet. Das ist der klassische Off-by-one-Fehler.

switch-Schleife

Diese Schleife wird leicht übersehen. Wenn Sie in einer switch-Anweisung ein Array angeben, überprüft diese Anweisung jedes Element im Array.

$data = 'Zero','One','Two','Three'
switch( $data )
{
    'One'
    {
        'Tock'
    }
    'Three'
    {
        'Tock'
    }
    Default
    {
        'Tick'
    }
}
Tick
Tock
Tick
Tock

Diese Anweisung bietet einige echt coole Möglichkeiten. Ich habe darüber einen ganzen Artikel geschrieben:

Aktualisieren von Werten

Wenn es sich bei Ihrem Array um eine Auflistung von string- oder integer-Typen (Werttypen) handelt, werden Sie die Werte im Array beim Durchlaufen vermutlich aktualisieren wollen. Die meisten oben genannten Schleifen verwenden eine Variable in der Schleife, die eine Kopie des Werts enthält. Wenn Sie diese Variable aktualisieren, wird der ursprüngliche Wert im Array nicht aktualisiert.

Die Ausnahme dieser Anweisung ist die for-Schleife. Wenn Sie ein Array durchlaufen und Werte darin aktualisieren möchten, ist die for-Schleife genau das, wonach Sie suchen.

for ( $index = 0; $index -lt $data.count; $index++ )
{
    $data[$index] = "Item: [{0}]" -f $data[$index]
}

Dieses Beispiel ruft einen Wert anhand des Indexes ab, nimmt einige Änderungen vor und verwendet denselben Index, um den Wert wieder zuzuweisen.

Array aus Objekten

Bisher haben wir nur einen Werttyp in einem Array platziert, Arrays können aber auch Objekte enthalten.

$data = @(
    [pscustomobject]@{FirstName='Kevin';LastName='Marquette'}
    [pscustomobject]@{FirstName='John'; LastName='Doe'}
)

Viele Cmdlets geben Auflistungen von Objekten als Arrays zurück, wenn Sie sie einer Variable zuweisen.

$processList = Get-Process

All die grundlegenden Features, die ich Ihnen bereits vorgestellt habe, gelten auch für Arrays aus Objekten. Allerdings gibt es ein paar Details, auf die ich Sie gerne hinweisen möchte.

Zugreifen auf Eigenschaften

Wir können einen Index verwenden, um auf ein einzelnes Element in einer Auflistung zuzugreifen – genau wie bei Werttypen.

PS> $data[0]

FirstName LastName
-----     ----
Kevin     Marquette

Wir können auf Eigenschaften zugreifen und diese direkt aktualisieren.

PS> $data[0].FirstName

Kevin

PS> $data[0].FirstName = 'Jay'
PS> $data[0]

FirstName LastName
-----     ----
Jay       Marquette

Arrayeigenschaften

Normalerweise müssten Sie die gesamte Liste wie folgt aufzählen, um auf alle Eigenschaften zuzugreifen:

PS> $data | ForEach-Object {$_.LastName}

Marquette
Doe

Sie könnten auch das Cmdlet Select-Object -ExpandProperty verwenden.

PS> $data | Select-Object -ExpandProperty LastName

Marquette
Doe

PowerShell bietet aber die Möglichkeit, LastName direkt anzufordern. PowerShell zählt alle Elemente für uns auf und gibt eine bereinigte Liste zurück.

PS> $data.LastName

Marquette
Doe

Die Enumeration erfolgt weiterhin, aber die Komplexität dahinter bleibt verborgen.

Where-Object-Filterung

Hier kommt Where-Object ins Spiel, sodass wir Informationen basierend auf den Eigenschaften des Objekts filtern und auswählen können.

PS> $data | Where-Object {$_.FirstName -eq 'Kevin'}

FirstName LastName
-----     ----
Kevin     Marquette

Wir können dieselbe Abfrage schreiben, um das gewünschte FirstName-Objekt abzurufen.

$data | Where FirstName -eq Kevin

Where()

Arrays enthalten eine Where()-Methode, mit der Sie einen scriptblock für den Filter angeben können.

$data.Where({$_.FirstName -eq 'Kevin'})

Dieses Feature wurde in PowerShell 4.0 hinzugefügt.

Aktualisieren von Objekten in Schleifen

Bei Werttypen besteht die einzige Möglichkeit zum Aktualisieren des Arrays darin, eine for-Schleife zu verwenden, da wir den Index kennen müssen, um den Wert zu ersetzen. Bei Objekten haben wir mehr Optionen, weil es sich dabei um Verweistypen handelt. Hier ist ein kurzes Beispiel:

foreach($person in $data)
{
    $person.FirstName = 'Kevin'
}

Die Schleife durchläuft jedes Objekt im $data-Array. Da Objekte Verweistypen sind, verweist die Variable $person auf exakt dieses Objekt, das sich im Array befindet. Updates der Eigenschaften des Objekts aktualisieren also das ursprüngliche Objekt.

Ersetzen können Sie das gesamte Objekt auf diese Weise immer noch nicht. Wenn Sie versuchen, der Variable $person ein neues Objekt zuzuweisen, aktualisieren Sie den Variablenverweis auf etwas anderes, das nicht mehr auf das ursprüngliche Objekt im Array verweist. Das funktioniert nicht so, wie Sie es erwarten würden:

foreach($person in $data)
{
    $person = [pscustomobject]@{
        FirstName='Kevin'
        LastName='Marquette'
    }
}

Operatoren

Operatoren in PowerShell funktionieren auch in Arrays. Bei einigen ist die Funktionsweise allerdings etwas anders.

-join

Der -join-Operator ist der offensichtlichste, also sehen wir uns diesen zuerst an. Ich mag den -join-Operator und verwende ihn häufig. Er verknüpft alle Elemente im Array mit dem Zeichen oder der Zeichenfolge, das bzw. die Sie angeben.

PS> $data = @(1,2,3,4)
PS> $data -join '-'
1-2-3-4
PS> $data -join ','
1,2,3,4

Eines der Features, das mir am -join-Operator gefällt, ist die Verarbeitung einzelner Elemente.

PS> 1 -join '-'
1

Dieses Feature verwende ich in Protokollierungsmeldungen und ausführlichen Meldungen.

PS> $data = @(1,2,3,4)
PS> "Data is $($data -join ',')."
Data is 1,2,3,4.

-join $array

Jetzt zeige ich Ihnen einen cleveren Trick, auf den Lee Dailey mich gebracht hat. Wenn Sie alle Elemente ohne Trennzeichen miteinander verknüpfen möchten, gehen Sie nicht so vor:

PS> $data = @(1,2,3,4)
PS> $data -join $null
1234

Sie können -join mit dem Array als Parameter ohne Präfix verwenden. Sehen Sie sich das folgende Beispiel an, das verdeutlicht, wovon ich spreche.

PS> $data = @(1,2,3,4)
PS> -join $data
1234

-replace und -split

Die Operatoren -replace und -split werden für jedes Element im Array ausgeführt. Ich kann nicht behauptet, diese jemals auf diese Weise verwendet zu haben, aber hier finden Sie ein Beispiel.

PS> $data = @('ATX-SQL-01','ATX-SQL-02','ATX-SQL-03')
PS> $data -replace 'ATX','LAX'
LAX-SQL-01
LAX-SQL-02
LAX-SQL-03

-contains

Mit dem -contains-Operator können Sie ein Array mit Werten überprüfen, um zu ermitteln, ob es einen bestimmten Wert enthält.

PS> $data = @('red','green','blue')
PS> $data -contains 'green'
True

-in

Wenn Sie überprüfen möchten, ob ein einzelner Wert mit einem von mehreren Werten übereinstimmt, können Sie den -in-Operator verwenden. Der Wert steht dann auf der linken und das Array auf der rechten Seite des Operators.

PS> $data = @('red','green','blue')
PS> 'green' -in $data
True

Das Ganze kann recht umfangreich werden, wenn die Liste lang ist. Ich verwende häufig ein RegEx-Muster, wenn ich mehr als nur einige wenige Werte überprüfe.

PS> $data = @('red','green','blue')
PS> $pattern = "^({0})$" -f ($data -join '|')
PS> $pattern
^(red|green|blue)$

PS> 'green' -match $pattern
True

-eq und -ne

Die Verwendung des Gleichheitsoperators mit Arrays kann ziemlich kompliziert werden. Wenn sich das Array auf der linken Seite befindet, werden alle Elemente verglichen. Statt True wird das übereinstimmende Objekt zurückgegeben.

PS> $data = @('red','green','blue')
PS> $data -eq 'green'
green

Wenn Sie den -ne-Operator verwenden, erhalten Sie alle Werte, die nicht gleich Ihrem Wert sind.

PS> $data = @('red','green','blue')
PS> $data -ne 'green'
red
blue

Wenn Sie dies in einer if()-Anweisung verwenden, ist ein Wert, der zurückgegeben wird, ein True-Wert. Wenn kein Wert zurückgegeben wird, handelt es sich um einen False-Wert. Die beiden nächsten Anweisungen werden zu True ausgewertet.

$data = @('red','green','blue')
if ( $data -eq 'green' )
{
    'Green was found'
}
if ( $data -ne 'green' )
{
    'And green was not found'
}

Darauf gehe ich gleich näher ein, wenn ich das Überprüfen auf $null erläutere.

-match

Der Operator -match versucht, eine Übereinstimmung für jedes Element in der Auflistung zu finden.

PS> $servers = @(
    'LAX-SQL-01'
    'LAX-API-01'
    'ATX-SQL-01'
    'ATX-API-01'
)
PS> $servers -match 'SQL'
LAX-SQL-01
ATX-SQL-01

Wenn Sie -match mit einem einzelnen Wert verwenden, wird eine spezielle Variable, $Matches, mit Übereinstimmungsinformationen aufgefüllt. Dies ist nicht der Fall, wenn ein Array auf diese Weise verarbeitet wird.

Wir können denselben Ansatz mit Select-String verwenden.

$servers | Select-String SQL

In meinem Blog The many ways to use regex (Die vielfältigen Verwendungsmöglichkeiten von regulären Ausdrücken) habe ich Select-String, -match und die Variable $matches genauer erläutert.

$null- oder leere Werte

Die Überprüfung auf $null oder leere Arrays kann knifflig sein. Hier zeige ich Ihnen einige häufige Fallen bei Arrays.

Auf den ersten Blick sieht diese Anweisung aus, als sollte sie funktionieren.

if ( $array -eq $null)
{
    'Array is $null'
}

Ich habe aber gerade erläutert, wie -eq jedes Element im Array überprüft. Wenn wir also ein Array aus mehreren Elementen mit einem einzigen $null-Wert haben, würde dieses als $true ausgewertet.

$array = @('one',$null,'three')
if ( $array -eq $null)
{
    'I think Array is $null, but I would be wrong'
}

Darum empfiehlt es sich, $null auf der linken Seite des Operators zu platzieren. Und schon ist dieses Szenario kein Problem mehr.

if ( $null -eq $array )
{
    'Array actually is $null'
}

Ein $null-Array ist nicht dasselbe wie ein leeres Array. Wenn Sie wissen, dass es sich um ein Array handelt, überprüfen Sie die Anzahl von Objekten darin. Wenn das Array $null ist, lautet die Anzahl 0.

if ( $array.count -gt 0 )
{
    "Array isn't empty"
}

Es gibt noch eine weitere Falle, nach der Sie Ausschau halten sollten. Sie können count verwenden, auch wenn Sie nur über ein einzelnes Objekt verfügen, es sei denn, dieses Objekt ist ein PSCustomObject. Dies ist ein Fehler, der in PowerShell 6.1 behoben wurde. Das sind zwar gute Nachrichten, aber viele Entwickler arbeiten noch mit Version 5.1 und müssen daher an dieser Stelle Vorsicht walten lassen.

PS> $object = [PSCustomObject]@{Name='TestObject'}
PS> $object.count
$null

Wenn Sie mit PowerShell 5.1 arbeiten, können Sie das Objekt vor der Überprüfung der Anzahl mit einem Array umschließen, um die exakte Anzahl zu erhalten.

if ( @($array).count -gt 0 )
{
    "Array isn't empty"
}

Um ganz sicherzugehen, überprüfen Sie zunächst auf $null und überprüfen dann die Anzahl.

if ( $null -ne $array -and @($array).count -gt 0 )
{
    "Array isn't empty"
}

Alles -eq

Jemand frage neulich, How to verify that every value in an array matches a given value. Der Reddit-Benutzer /u/bis hatte diese clevere Lösung parat, die auf falsche Werte überprüft und das Ergebnis dann umdreht.

$results = Test-Something
if ( -not ( $results -ne 'Passed') )
{
    'All results a Passed'
}

Hinzufügen zu Arrays

Möglicherweise fragen Sie sich an dieser Stelle, wie Sie Elemente zu einem Array hinzufügen. Die schnelle Antwort lautet: „Gar nicht.“ Ein Array weist im Arbeitsspeicher eine unveränderliche Größe auf. Wenn Sie es erweitern oder ihm ein einzelnes Element hinzufügen müssen, müssen Sie ein neues Array erstellen und die Werte aus dem alten Array hineinkopieren. Das klingt nach viel Arbeit, aber in PowerShell ist das Erstellen des neuen Arrays weniger komplex. PowerShell implementiert den Additionsoperator (+) für Arrays.

Hinweis

PowerShell implementiert keinen Subtraktionsoperator. Wenn Sie eine flexible Alternative zu Arrays benötigen, müssen Sie ein generisches List-Objekt verwenden.

Addition eines Arrays

Wir können den Additionsoperator mit Arrays verwenden, um ein neues Array zu erstellen. Wir haben diese beiden Arrays:

$first = @(
    'Zero'
    'One'
)
$second = @(
    'Two'
    'Three'
)

Diese können wir addieren, um ein neues Array zu erhalten.

PS> $first + $second

Zero
One
Two
Three

Plus-gleich (+=)

Wir können auf folgende Weise ein neues Array erstellen und diesem ein Element hinzufügen:

$data = @(
    'Zero'
    'One'
    'Two'
    'Three'
)
$data += 'four'

Denken Sie daran, dass Sie bei jeder Verwendung von += die Elemente duplizieren und ein neues Array erstellen. Das ist bei kleinen Datasets kein Problem, lässt sich aber nur schlecht skalieren.

Pipelinezuweisung

Sie können die Ergebnisse einer Pipeline in einer Variable zuweisen. Wenn mehrere Elemente enthalten sind, handelt es sich um ein Array.

$array = 1..5 | ForEach-Object {
    "ATX-SQL-$PSItem"
}

Wenn wir über die Verwendung der Pipeline nachdenken, denken wir in der Regel an die typischen PowerShell-Einzeiler. Wir können die Pipeline aber auch mit foreach()-Anweisungen und anderen Schleifen nutzen. Anstatt also Elemente in einer Schleife zu einem Array hinzuzufügen, können wir Elemente in der Pipeline ablegen.

$array = foreach ( $node in (1..5))
{
    "ATX-SQL-$node"
}

Arraytypen

Ein Array wird in PowerShell standardmäßig als [PSObject[]]-Typ erstellt. So kann es jeden Objekt- oder Werttyp enthalten. Das funktioniert, weil alles vom Typ PSObject geerbt wird.

Stark typisierte Arrays

Sie können mit einer ähnlichen Syntax ein Array eines beliebigen Typs erstellen. Wenn Sie ein stark typisiertes Array erstellen, kann es nur Werte oder Objekte des angegebenen Typs enthalten.

PS> [int[]] $numbers = 1,2,3
PS> [int[]] $numbers2 = 'one','two','three'
ERROR: Cannot convert value "one" to type "System.Int32". Input string was not in a correct format."

PS> [string[]] $strings = 'one','two','three'

ArrayList

Dass sich nicht einfach so Elemente hinzufügen lassen, ist eine der größten Einschränkungen von Arrays, aber es gibt einige andere Auflistungen, mit denen sich dieses Problem lösen lässt.

Wenn ich ein Array brauche, mit dem ich schneller arbeiten kann, fällt mir in der Regel zuerst eine ArrayList ein. Eine Arrayliste funktioniert wie ein Objektarray überall dort, wo es notwendig ist, ermöglicht aber das schnelle Hinzufügen von Objekten.

So erstellen wir eine ArrayList und fügen Elemente hinzu.

$myarray = [System.Collections.ArrayList]::new()
[void]$myArray.Add('Value')

Wir rufen .NET auf, um diesen Typ zu erhalten. In diesem Fall verwenden wir den Standardkonstruktor zum Erstellen. Dann rufen wir die Add-Methode auf, um ein Element hinzuzufügen.

Ich verwende [void] am Anfang der Zeile, weil ich den Rückgabecode unterdrücken möchte. Einige .NET-Aufrufe tun dies, und es kann zu einer unerwarteten Ausgabe führen.

Wenn die einzigen Daten, die in Ihrem Array enthalten sind, Zeichenfolgen sind, sollten Sie auch die Verwendung von StringBuilder in Betracht ziehen. Das ist fast das Gleiche, bietet aber einige Methoden nur für die Verarbeitung von Zeichenfolgen. StringBuilder wurde speziell im Hinblick auf eine hohe Leistung entwickelt.

Viele Entwickler steigen von Arrays auf ArrayList um. Das stammt aber noch aus Zeiten, in denen C# keine generische Unterstützung bot. ArrayList ist zugunsten des generischen List[]-Elements als veraltet gekennzeichnet.

Generischer List-Typ

Ein generischer Typ ist ein spezieller Typ in C#, der eine generalisierte Klasse definiert. Der Benutzer gibt dann bei der Erstellung die verwendeten Datentypen an. Wenn Sie also eine Liste mit Zahlen oder Zeichenfolgen haben möchten, definieren Sie, dass Sie eine Liste mit int- oder string-Typen benötigen.

So erstellen Sie eine Liste für Zeichenfolgen.

$mylist = [System.Collections.Generic.List[string]]::new()

Und so erstellen Sie eine Liste für Zahlen.

$mylist = [System.Collections.Generic.List[int]]::new()

Wir können ein vorhandenes Array in eine Liste umwandeln, ohne zuerst das Objekt erstellen zu müssen:

$mylist = [System.Collections.Generic.List[int]]@(1,2,3)

Mit der using namespace-Anweisung in PowerShell 5 und höher können wir die Syntax kürzen. Die using-Anweisung muss die erste Zeile im Skript sein. Durch Deklarieren eines Namespaces können Sie diese Anweisung in PowerShell weglassen, wenn Sie auf Datentypen verweisen.

using namespace System.Collections.Generic
$myList = [List[int]]@(1,2,3)

Damit wird List viel nützlicher.

Ihnen steht eine weitere ähnliche Methode zur Verfügung: Add. Im Gegensatz zu ArrayList gibt es keinen Rückgabewert für die Add-Methode, daher müssen wir void nicht verwenden.

$myList.Add(10)

Dennoch können wir wie in anderen Arrays auf die Elemente zugreifen.

PS> $myList[-1]
10

List[PSObject]

Sie können mit Listen beliebiger Typen arbeiten. Wenn Sie aber den Objekttyp nicht kennen, können Sie [List[PSObject]] als Container für die Objekte verwenden.

$list = [List[PSObject]]::new()

Remove()

Sowohl ArrayList als auch das generische List[] unterstützen das Entfernen von Elementen aus der Auflistung.

using namespace System.Collections.Generic
$myList = [List[string]]@('Zero','One','Two','Three')
[void]$myList.Remove("Two")
Zero
One
Three

Bei Werttypen wird der erste Wert aus der Liste entfernt. Sie können die Funktion immer wieder aufrufen, um den jeweils ersten Wert zu entfernen. Bei Verweistypen müssen Sie das Objekt angeben, das entfernt werden soll.

[list[System.Management.Automation.PSDriveInfo]]$drives = Get-PSDrive
$drives.remove($drives[2])
$delete = $drives[2]
$drives.remove($delete)

Die remove-Methode gibt true zurück, wenn sie das Element in der Auflistung finden und daraus entfernen konnte.

Weitere Auflistungen

Es gibt noch viel mehr Auflistungen, die verwendet werden können, aber die gerade vorgestellten sind gute generische Ersatzmöglichkeiten für Arrays. Wenn Sie mehr über diese Optionen erfahren möchten, sehen Sie sich dieses Gist an, das Mark Kraus zusammengestellt hat.

Andere Nuancen

Die wichtigen Funktionen habe ich abgedeckt, und jetzt möchte ich Ihnen noch einige weitere Aspekte vorstellen, bevor ich zum Ende komme.

Arrays mit vorab festgelegter Größe

Ich habe bereits erläutert, dass Sie die Größe eines Arrays nach dem Erstellen nicht mehr ändern können. Wir können ein Array mit einer vorab festgelegten Größe erstellen, indem wir das Array mit dem Konstruktor new($size) aufrufen.

$data = [Object[]]::new(4)
$data.count
4

Multiplizieren von Arrays

Ein interessanter kleiner Trick ist, dass Sie ein Array mit einer Ganzzahl multiplizieren können.

PS> $data = @('red','green','blue')
PS> $data * 3
red
green
blue
red
green
blue
red
green
blue

Initialisieren mit 0

Es kommt häufig vor, dass Sie ein Array erstellen möchten, das nur aus Nullen besteht. Wenn Sie nur Ganzzahlen verwenden, enthält ein stark typisiertes Array mit Ganzzahlen standardmäßig nur Nullen.

PS> [int[]]::new(4)
0
0
0
0

Hier können wir auch den Multiplikationstrick anwenden.

PS> $data = @(0) * 4
PS> $data
0
0
0
0

Das Schöne bei diesem Trick ist, dass Sie jeden Wert verwenden können. Wenn Sie also lieber 255 als Standardwert haben möchten, ist dies ein guter Ansatz.

PS> $data = @(255) * 4
PS> $data
255
255
255
255

Geschachtelte Arrays

Ein Array innerhalb eines Arrays wird als geschachteltes Array bezeichnet. Ich verwende diese selten in PowerShell, arbeite aber in anderen Sprachen häufig damit. Wenn Ihre Daten in ein rasterförmiges Muster passen, sollten Sie ein Array aus Arrays in Erwägung ziehen.

Es gibt zwei Möglichkeiten, ein zweidimensionales Array zu erstellen.

$data = @(@(1,2,3),@(4,5,6),@(7,8,9))

$data2 = @(
    @(1,2,3),
    @(4,5,6),
    @(7,8,9)
)

Das Komma ist in diesen Beispielen sehr wichtig. Weiter oben habe ich ein Beispiel eines normalen Arrays auf mehreren Zeilen vorgestellt, bei dem das Komma optional war. Bei mehrdimensionalen Arrays ist das nicht der Fall.

Bei Verwendung eines geschachtelten Arrays ändert sich die Indexnotation geringfügig. Mit $data (siehe oben) können wir auf den Wert 3 zugreifen.

PS> $outside = 0
PS> $inside = 2
PS> $data[$outside][$inside]
3

Umschließen Sie jede Schachtelungsebene des Arrays mit eckigen Klammern. Der erste Satz Klammern umschließt das äußerste Array, danach geht es immer eine Ebene weiter nach innen.

Write-Output -NoEnumerate

In PowerShell werden Arrays häufig aufgezählt, oder ihre Umschließung wird aufgehoben. Dies ist ein wesentlicher Aspekt der Art und Weise, in der PowerShell die Pipeline verwendet, aber manchmal ist diese Vorgehensweise nicht wünschenswert.

Ich übergebe Objekte meist per Pipe an Get-Member, um Informationen zu den Objekten zu erhalten. Wenn ich auf diese Weise ein Array übergebe, wird dessen Umschließung aufgehoben, und „Get-Member“ zeigt die Member des Arrays an, nicht das tatsächliche Array.

PS> $data = @('red','green','blue')
PS> $data | Get-Member
TypeName: System.String
...

Um zu verhindern, dass die Umschließung eines Arrays aufgehoben wird, können Sie Write-Output -NoEnumerate verwenden.

PS> Write-Output -NoEnumerate $data | Get-Member
TypeName: System.Object[]
...

Ich kenne auch noch eine zwei Möglichkeit, das ist aber eher ein Hack (und in der Regel vermeide ich solche Hacks). Sie können ein Komma vor das Array setzen, bevor Sie es per Pipe übergeben.

PS> ,$data | Get-Member
TypeName: System.Object[]
...

Zurückgeben eines Arrays

Die Umschließung eines Arrays wird auch aufgehoben, wenn Sie Werte aus einer Funktion ausgeben oder zurückgeben. Sie erhalten weiterhin ein Array, wenn Sie die Ausgabe einer Variable zuweisen, daher ist dies in der Regel kein Problem.

Der Haken bei der Sache ist, dass Sie dadurch über ein neues Array verfügen. Wenn dies ein Problem darstellt, können Sie Write-Output -NoEnumerate $array oder return ,$array als Umgehung verwenden.

Noch etwas?

Ich weiß, das waren eine Menge Informationen. Ich hoffe, dass Sie jedes Mal, wenn Sie diesen Artikel lesen, etwas Neues lernen und diesen Artikel noch lange als gute Referenz nutzen können. Wenn Sie den Artikel hilfreich finden, teilen Sie ihn gerne mit anderen, die auch davon profitieren könnten.

Jetzt möchte ich Ihnen noch einen ähnlichen Artikel ans Herz legen, den ich über Hashtabellen geschrieben habe.