2010-12-05 27 views
18

Ich habe oft die folgende Situation in meinem PowerShell-Code: Ich habe eine Funktion oder eine Eigenschaft, die eine Auflistung von Objekten oder $null zurückgibt. Wenn Sie die Ergebnisse in die Pipeline übertragen, behandeln Sie auch ein Element in der Pipeline, wenn $null das einzige Element ist.

Beispiel:

$Project.Features | Foreach-Object { Write-Host "Feature name: $($_.Name)" } 

Wenn es keine Merkmale sind ($ project.Features liefert $ null), werden Sie nur eine einzige Zeile mit siehe "Feature-Name:".

Ich sehe drei Möglichkeiten, dies zu lösen:

if ($Project.Features -ne $null) 
{ 
    $Project.Features | Foreach-Object { Write-Host "Feature name: $($_.Name)" } 
} 

oder

$Project.Features | Where-Object {$_ -ne $null) | Foreach-Object { 
    Write-Host "Feature name: $($_.Name)" 
} 

oder

$Project.Features | Foreach-Object { 
    if ($_ -ne $null) { 
    Write-Host "Feature name: $($_.Name)" } 
    } 
} 

Aber eigentlich mag ich nicht jeder dieser Ansätze, aber was tun Sie sehen als den besten Ansatz?

Antwort

25

Ich glaube nicht jemand mag die Tatsache, dass sowohl "foreach ($ a in $ null) {}" und "$ null | foreach-Objekt {}" einmal iterieren. Leider gibt es keinen anderen Weg als die Art und Weise, wie Sie demonstriert haben. Sie könnten prägnanter sein:

$null | ?{$_} | % { ... } 

die ?{$_} ist eine Abkürzung für where-object {$_ -ne $null} als $null als boolean Ausdruck ausgewertet wird als $false

behandelt werden Ich habe ein Filter definiert in meinem Profil wie folgt aus:

filter Skip-Null { $_|?{ $_ } } 

Verwendung:

$null | skip-null | foreach { ... } 

Ein Filter ist derselbe wie eine Funktion, außer dass der Standardblock der Prozess {} not end {} ist.

UPDATE: Ab Powershell 3.0 ist $null nicht mehr iterable als Sammlung. Yay!

-Oisin

+5

Das Problem mit dem prägnanten Kurzschrift ist, dass es irgendetwas, das nötigt ablehnen zu 'false', was Dinge wie' 0', '" '', '@()', '@ (0)', ... beinhaltet, würde ich wahrscheinlich erwarten, dass "Skip-Null" nur "$" überspringt null ". Ich weiß, dass im Zusammenhang mit dieser Frage das Ergebnis dasselbe ist, aber für einen Filter, der auch anderswo verwendet werden könnte ... – Joey

+0

@joey guter Punkt. – x0n

+0

leider '@() |? {$ False}' gibt immernoch $ null zurück, anstatt eine leere Liste zu senden – ekkis

12

Wenn Sie Ihre Funktion ändern können, muss es eine leere Sammlung/Array zurück statt $ null:

PS> function Empty { $null } 
PS> Empty | %{'hi'} 
hi 

PS> function Empty { @() } 
PS> Empty | %{'hi'} 

Andernfalls gehen mit dem, was Oisin schlägt vor, obwohl ich eine leichte würde vorschlagen zwicken:

filter Skip-Null { $_|?{ $_ -ne $null } } 

Ansonsten ist diese auch 0 und $false filtert.

Update 4-30-2012: Dieses Problem wurde in PowerShell v3 behoben. V3 wird nicht über einen skalaren $ null-Wert iterieren.

+0

Warum kann ich nicht nur eine Antwort akzeptieren? Sie sind beide großartig, danke Jungs! Ich gab Oisin die "Antwort", Keith hat bereits den meisten Punkt :-) –

+0

Was sind die Chancen, dass ich gerade diese Antwort sah, als Sie es fast 17 Monate nach der Veröffentlichung bearbeitet haben? Gibt es ein Cmdlet, um das zu berechnen? – BACON

+0

@BACON Es ist definitiv nicht zufällig. Ich habe über meinen SO-Posteingang bemerkt, dass du einen Kommentar zu einer meiner Antworten geschrieben hast. :-) Ich dachte nur, es wäre gut, darauf hinzuweisen, dass dies in V3 kein Problem ist. –

2

Eine kurze Anmerkung zu Keith Antwort abzuschließen es

Ich persönlich nichts zurückkehren würde. Es macht Sinn:

PS> function Empty { if ('a' -eq 'b') { 'equal' } } 
PS> Empty | % { write-host result is $_ } 

Aber jetzt sind Sie in Probleme, wenn Sie resultieren aus Empty einer Variablen zuweisen:

PS> $v = Empty 
PS> $v | % { write-host result is $_ } 

Es gibt einen kleinen Trick ist es funktioniert. Nur wickeln Sie das Ergebnis aus Empty als Array wie folgt:

PS> $v = @(Empty) 
PS> $v | % { write-host result is $_ } 
PS> $v.GetType() 
IsPublic IsSerial Name  BaseType 
-------- -------- ----  -------- 
True  True  Object[] System.Array 
PS> $v.Length 
0 
+1

Das ist der Ansatz, den ich heute verwende * wenn * ich die Definition von nicht kontrolliere Der Befehl wird aufgerufen. Ansonsten gebe ich ein leeres Array zurück, wenn die Funktion normall mehrere Items zurückgibt, so dass ich darüber foreach - habe zu oft vergessen, in @() einzupacken. :-) –

+0

@Keith, ich glaube du hast einen tollen Artikel über dieses heikle Verhalten geschrieben. Du kannst es hier verlinken, andere sollten es unbedingt lesen;) – stej

2

Eine andere Möglichkeit:

$objects | Foreach-Object -Begin{If($_ -eq $null){continue}} -Process {do your stuff here} 

Mehr Informationen in about_Continue

Verwandte Themen