PowerShell: Zeig mir, wie du mit Arrays umgehst, und ich sage dir, aus welcher Sprache du kommst

Kaum ein Sprachkonstrukt in PowerShell verursacht mehr Verwirrung und unnötige Diskussionen wie die Arrays. OK, Hashtables hätten vielleicht noch das Potential, aber an die trauen sich viele nicht ran (zu unrecht). An Arrays kommt man aber nicht gut vorbei, liefert doch nahezu jedes Get-Cmdlet ein Array von irgendwas, wenn es davon mehr als eins gibt. Je nachdem, ob ihr aus VisualBasic, Java, PHP oder C# kommt, werdet ihr die PowerShell-Arrays auf eine unterschiedliche, aber jeweils sehr charakteristische Art fehlinterpretieren. Dabei ist es so einfach 🙂

Die simple Wahrheit über PowerShell-Arrays

Sprecht mir einfach nach:

Arrays in PowerShell sind immer eindimensional.

Und dann noch:

Arrays in PowerShell haben immer eine feste Größe.

Jetzt sagt ihr aber: Ich hab‘ doch schon oft so etwas gesehen wie

$arr[3][5]

oder auch, vielleicht sogar noch häufiger,

$arr += "new element"

das kann also nicht beides stimmen? Nun, nicht alles in PowerShell ist so wie es scheint. Lasst uns einen Blick unter die Haube werfen.

Dimensional drive

Dass die Arrays in PowerShell immer eindimensional sind, schließt natürlich absolut nicht aus, dass ihre Elemente jeweils Arrays sein können, welche ihrerseits Arrays als Elemente beinhalten. So entsteht der Anschein mehrerer Dimensionen. Doch wie so oft, trügt der Anschein auch hier. Schauen wir uns mal ein scheinbar zweidimensionales Array an:

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

Man kann einzelne Elemente mit zwie Dimensionen adressieren, so liefert $a[0][0] eine 1 zurück, $a[2][2] eine 9 und $a[1][1] eine 5. Doch bei $a[1][2] offenbart sich der Unterschied zu klassischen mehrdimensionalen Arrays aus anderen Programmiersprachen: Während das Ergebnis, dass $a[1][2] -eq $null, noch nicht ganz unerwartet ist, scheitert der Versuch, diesen Wert zu setzen, mit der Fehlermeldung, dass der Index außerhalb des zulässigen Bereiches liegt.
Ebensowenig erfolgreich ist der Versuch, das obige Array um eine neue Zeile zu erweitern:

$a += @(10,11,12)

liefert nicht etwa ein Array der Stärke 4 mit vier Arrays als Elementen, sondern ein Array der Stärke 6 mit drei Arrays und drei ganzen Zahlen. Beide Arrays sind nämlich eindimensional, und deren Summe ist auch eindimensional – das zweite Array wird einfach hinter das erste geschrieben!

Klammern über Klammern

Eine weitere gängige, aber unzutreffende Vorstellung ist es, dass man pseudo-mehrdimensionale Arrays „einfach so“ initialisieren und durch Hinzufügen erweitern kann. Tatsächlich wird folgendes ein Array der Stärke drei erzeugen, dessen Mitglieder jeweils leere Arrays sind:

$y = @(@(),@(),@())

Was hingegen nicht funktioniert, ist

$z = @()
$z += @()
$z += @()
$z += @()

oder auch

$x = @(@())
$x += @()
$x += @()

Beides ergibt nur ein einfaches leeres Array, ohne irgendwelche Stärken oder Dimensionen.

Und was war das mit der festen Länge?

Das kann jeder sehr schnell herausfinden. Versucht einfach, die Methode .Add() zu verwenden, die ja bei einem Array angeboten wird.

Übrigens: Man kann Get-Member nur mit einem nichtleeren Array aufrufen!

In einer deutschen PowerShell lautet die Fehlermeldung „Die Liste hatte eine feste Größe“. Wie funktioniert das also mit der Addition? Vielleicht ahnt ihr das schon: Das Array wird in eine neue Liste kopiert, die ebenfalls eine feste – aber größere! – Größe hat, und die Elemente am Ende werden durch den zweiten Summanden aufgefüllt. Dass das nicht performant sein kann, muss nicht extra gesagt werden. Ich sage es aber trotzdem, und zum Ausmaß der Misere könnt ihr gern diesen alten Beitrag lesen.

Ich will aber ein zweidimensionales Array!

Dann benutze doch einfach eine Sprache, die solche Konstrukte kennt, z.B. C#. Glücklicherweise lässt sich C# sogar in PowerShell einbetten. Das folgende definiert ein „echtes“ zweidimensionales Array der Stärke 5 in jeder Dimension:

Add-Type @"
public class array2d {
    public int[,] values = new int[5,5];
}
"@
$x = [array2d]::new()
$x.values[1,1] = 7
$x.values[2,4] = 13

Auch dieses Array hat eine fixe Größe, und eine Erweiterung gestaltet sich noch problematischer als bei nativen PowerShell-Arrays. Will man die Flexibilität bezüglich der Typen der Array-Mitglieder beibehalten, so muss man das Array aus object definieren, etwa so:

Add-Type @"
public class array2d { 
    public object[,] values = new object[5,5]; 
} 
"@ 
$x = [array2d]::new() 
$x.values[1,1] = 7 
$x.values[2,4] = "sometext"
$x.values[4,3] = @("text",42) 

Auf die Werte des letzten Elements würde man dann mit $x.values[4,3][0] und $x.values[4,3][1] zugreifen.

Übrigens: Die Indizes eines sochen zweidimensionalen Arrays sind Arrays mit jeweils zwei Elementen:

$i = @(4, 3)
$x.values[$i]

Statt eines Fazits

Arrays in PowerShell sind umständlich, aber unvermeidlich. Jeder PowerShell-Scripter muss die Eigenarten der Arrays kennen und die Stolperfallen – auch die bei der Performance! – umschiffen, indem andere, geeignetere Datenkonstrukte zur Anwendung kommen.

Happy Scripting!

Ersten Kommentar schreiben

Antworten

Deine E-Mail-Adresse wird nicht veröffentlicht.


*


Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.