Serge van den Oever
Serge van den Oever

Reputation: 4392

How to handle $null in the pipeline

I often have the following situation in my PowerShell code: I have a function or property that returns a collection of objects, or $null. If you push the results into the pipeline, you also handle an element in the pipeline if $null is the only element.

Example:

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

If there are no features ($Project.Features returns $null), you will see a single line with "Feature name:".

I see three ways to solve this:

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

or

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

or

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

But actually I don't like any of these approaches, but what do you see as the best approach?

Upvotes: 24

Views: 35734

Answers (4)

x0n
x0n

Reputation: 52450

I don't think anyone likes the fact that both "foreach ($a in $null) {}" and "$null | foreach-object{}" iterate once. Unfortunately there is no other way to do it than the ways you have demonstrated. You could be pithier:

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

the ?{$_} is shorthand for where-object {$_ -ne $null} as $null evaluated as a boolean expression will be treated as $false

I have a filter defined in my profile like this:

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

Usage:

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

A filter is the same as a function except the default block is process {} not end {}.

UPDATE: As of PowerShell 3.0, $null is no longer iterable as a collection. Yay!

-Oisin

Upvotes: 32

Keith Hill
Keith Hill

Reputation: 201832

If you can modify your function, have it return an empty collection/array instead of $null:

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

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

Otherwise, go with what Oisin suggests although I would suggest a slight tweak:

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

Otherwise this will also filter 0 and $false.

Update 4-30-2012: This issue is fixed in PowerShell v3. V3 will not iterate over a scalar $null value.

Upvotes: 12

Tom
Tom

Reputation: 1771

Another possibility:

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

More info in about_Continue

Upvotes: 2

stej
stej

Reputation: 29469

A quick note to Keith's answer to complete it

Personally, I would return nothing. It makes sense:

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

But now you are in problems if you assign result from Empty to a variable:

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

There is a little trick to make it work. Just wrap the result from Empty as a array like this:

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

Upvotes: 2

Related Questions