2010-12-05 6 views
18

J'ai souvent la situation suivante dans mon code PowerShell: J'ai une fonction ou une propriété qui retourne une collection d'objets, ou $null. Si vous insérez les résultats dans le pipeline, vous gérez également un élément dans le pipeline si $null est le seul élément.

Exemple:

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

S'il n'y a pas de caractéristiques ($ retourne Project.Features $ null), vous verrez une seule ligne avec "nom d'entité:".

Je vois trois façons de résoudre ce:

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

ou

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

ou

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

Mais en fait je n'aime pas l'une de ces approches, mais qu'est-ce que vous voyez comme la meilleure approche?

Répondre

25

Je ne pense pas quelqu'un aime le fait que tous les deux "foreach ($ a in $ null) {}" et "$ null | foreach-object {}" itèrent une fois. Malheureusement, il n'y a pas d'autre moyen de le faire que les moyens que vous avez démontrés. Vous pourriez être pithier:

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

le ?{$_} est un raccourci pour where-object {$_ -ne $null} comme $null évalué comme une expression booléenne sera considérée comme $false

J'ai un filtre défini dans mon profil comme celui-ci:

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

Utilisation:

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

Un filtre est identique à une fonction, sauf que le bloc par défaut est process {} not end {}.

MISE À JOUR: À partir de PowerShell 3.0, $null n'est plus itérable en tant que collection. Yay!

-Oisin

+5

Le problème avec le raccourci concis est qu'il rejettera tout ce qui contraint à 'false', qui inclut des choses comme' 0', '" "', '@()', '@ (0)', ... Je m'attendrais probablement à ce que 'Skip-Null' saute seulement' '$ '' null'. Je sais que dans le contexte de cette question le résultat est le même, mais pour un filtre qui pourrait être utilisé ailleurs aussi ... – Joey

+0

@joey bon point. – x0n

+0

malheureusement '@() |? {$ False}' renvoie toujours $ null au lieu de retourner une liste vide – ekkis

12

Si vous pouvez modifier votre fonction, faites revenir une collection/tableau vide au lieu de $ null:

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

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

Sinon, passez avec ce que Oisin suggère bien que je suggère une légère tweak:

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

Sinon, cela va aussi filtrer 0 et $false.

Mise à jour 4-30-2012: Ce problème est résolu dans PowerShell v3. V3 ne va pas parcourir une valeur $ scalaire nulle.

+0

Pourquoi ne puis-je pas accepter une seule réponse! Ils sont tous les deux géniaux, merci les gars! J'ai donné la «réponse» à Oisin, Keith a déjà le plus de points :-) –

+0

Quelles sont les chances que j'ai trouvé cette réponse lorsque vous l'avez édité près de 17 mois après sa publication? Y a-t-il une cmdlet pour le calculer? – BACON

+0

@BACON Ce n'est certainement pas une coïncidence. J'ai remarqué via ma boîte de réception SO que vous aviez posté un commentaire sur l'une de mes réponses. :-) Je pensais juste qu'il serait bon de souligner que ce n'est pas un problème dans V3. –

2

Une note rapide à la réponse de Keith pour le compléter

Personnellement, je retournerait rien. Il est logique:

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

Mais maintenant, vous êtes des problèmes si vous assignez résultat de Empty à une variable:

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

Il y a un petit truc pour le faire fonctionner. Juste envelopper le résultat de Empty comme un tableau comme celui-ci:

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

C'est l'approche que j'utilise aujourd'hui * si * je ne contrôle pas la définition de la commande étant invoquée. Sinon, je renvoie un tableau vide si la fonction normall renvoie plusieurs éléments de telle sorte que je voudrais foreach dessus - été trop souvent oublier d'envelopper dans @(). :-) –

+0

@Keith, je pense que vous avez écrit un excellent article sur ce comportement délicat. Vous pouvez lier ici, d'autres devraient le lire, certainement;) – stej

2

Une autre possibilité:

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

Plus d'info dans about_Continue

Questions connexes