Blogbeitrag

02 | 09 | 2018

PowerShell-Quirks: Das erste Element bestimmt den Typ, Object Edition

Geschrieben von um 23:19 Uhr

Wir wissen ja schon lange, dass in PowerShell das erste Element in einem Ausdruck in der Regel den Typ des Ergebnisses bestimmt.

(Wem das noch nicht klar ist, probiert bitte das folgende Beispiel aus:

$a = 1
$b = "1"
$a + $b
$b + $a

)

Besonders perfide ist dieses Phänomen allerdings bei inzwischen sehr beliebten Arrays aus PSCustomObjects, die man anfertigt, um das Ganze dann in einheitlich strukturierter Art und Weise auszugeben, z.B. nach Out-GridView oder Export-CSV. Die Konstruktion sieht dann z.B. so aus:

$output = @()
Get-Irgendwas | foreach {
    $object = New-Object PSCustomObject
    if (eine Bedingung) {
        $object | Add-Member -MemberType NoteProperty -Name "eine Property" -Value "ein Wert"
    }
    if (eine andere Bedingung) {
        $object | Add-Member -MemberType NoteProperty -Name "eine andere Property" -Value "ein anderer Wert"
    }
    <# ... usw. #>
    $output += $object
}

oder auch moderner und performanter mit Hashtables:

$output = @()
Get-Irgendwas | foreach {
    $hashtable = @{}
    if (eine Bedingung) {
       $hashtable.Add("eine Property","ein Wert")
    }
    if (eine andere Bedingung) {
        $hashtable.Add("eine andere Property","ein anderer Wert")
    }
    <# ... usw. #>
    $object = New-Object PSCustomObject -Property $hashtable
    $output += $object
}

Für die weitere Verarbeitung entsteht, wenn man nicht aufpasst und das wirklich so schreibt wie oben abgebildet, eine blöde Situation: Das Array enthält Objekte gleichen Typs (PSCustomObject), die aber unterschiedliche Properties haben. Adressiert man die Elemente einzeln, hat man Zugriff auf alle Properties des jeweiligen Objektes. Gibt man das array als Ganzes aus, diktiert das erste Element den Satz an Properties, und die Restlichen sind nicht sichtbar! Probiert es aus:

$a1 = New-Object PSCustomObject -Property @{"prop1"="prop1val1";"prop2"="prop2val1"}
$a2 = New-Object PSCustomObject -Property @{"prop1"="prop1val2";"prop2"="prop2val2"}
$a3 = New-Object PSCustomObject -Property @{"prop1"="prop1val3";"prop3"="prop3val3"}
$a4 = New-Object PSCustomObject -Property @{"prop1"="prop1val4";"prop3"="prop3val4"}
$a5 = New-Object PSCustomObject -Property @{"prop4"="prop4val5";"prop3"="prop3val5"}
$a6 = New-Object PSCustomObject -Property @{"prop4"="prop4val6";"prop3"="prop3val6"}
$arr1 = @($a1,$a2,$a3,$a4,$a5,$a6)
$arr2 = @($a6,$a5,$a4,$a3,$a2,$a1)
$arr1 | Out-GridView
$arr2 | Out-GridView

Das gleiche passiert bei Format-Table, Export-CSV und – das hat mich überrascht – selbst dann, wenn man die Elemente einfach nacheinander ausgibt, also einfach nach dem obigen Beispiel

$a1
$a3
$a5

schreibt. Explizite Out-Default und Out-Host Befehle funktionieren hingegen korrekt und geben für das jeweilige Objekt alle dort enthaltenen Properties aus, ebenso Format-List.

Man sollte also in solchen PSCustomObject-Konstrukten stets jedem Objekt alle Properties mitgeben, auch wenn manche davon leer bleiben. Im obigen Pseudocode-Beispiel würde man folgendes machen:

$output = @()
Get-Irgendwas | foreach {
    $hashtable = @{"eine Property"=$null;"eine andere Property"=$null;...;"die letzte Property"="Vorgabewert"}
    if (eine Bedingung) {
       $hashtable["eine Property"] = "ein Wert"
    }
    if (eine andere Bedingung) {
        $hashtable["eine andere Property"] = "ein anderer Wert"
    }
    <# ... usw. #>
    $object = New-Object PSCustomObject -Property $hashtable
    $output += $object
}

Dann haben alle Objekte im Array alle Properties, ggfls. nur mit Vorgabewerten, und die Ausgabe-Welt ist in Ordnung.
Happy scripting!

Beitragsdetails

Tags » , , «

gravatar

Kategorie » Microsoft, PowerShell «

Trackback: Trackback-URL |  Kommentar-Feed: RSS 2.0 | 581 Worte

Beitrag kommentieren

Kommentar schreiben ...

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

(X)HTML Tags zur Formatierung der Kommentare

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

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