2

So one of the very first pitfalls you learn to avoid with PowerShell is that this foreach:

$null | foreach { "hello" }

will execute once. However I was trying to explain this concept to a colleague and had the following thing happen:

PS> $a = 1..9 | ? {$_ -eq 10}
PS> $a | % { "hello" }
PS>

i've tried to understand what's going on with the following but $a seems to be $null, plain and simple.

PS> $a = 1..9 | ? {$_ -eq 10}
PS> $a -eq $null
True
PS> $a.gettype()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $a.gettype()
+ ~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

PS> $a | gm
gm : No object has been specified to the get-member cmdlet.
At line:1 char:6
+ $a | gm
+      ~~
    + CategoryInfo          : CloseError: (:) [Get-Member], InvalidOperationException
    + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand

PS> $a | % { "hello" }
PS> $null | % { "hello" }
hello
PS> 

Are there two different "types" of null somehow? Is $a really an array that's being obfuscated from me for some reason?

Nacht
  • 3,342
  • 4
  • 26
  • 41

1 Answers1

2

Some history on this -

Given PS > $a = 1..9 | ? {$_ -eq 10}

Powershell 2.0 behavior

PS > $a | % { "hello" }
hello
PS > $null | % { "hello" }
hello

Powershell 3.0 behavior - "fixed" the issue when variable is null, but built-in $null still iterates

PS > $a | % { "hello" }
PS > $null | % { "hello" }
hello

In the ForEach-Object documentation in 3.0 they added this -

Because Windows PowerShell treats null as an explicit placeholder, the ForEach-Object cmdlet generates a value for $null, just as it does for other objects that you pipe to it.

PS C:\> 1, 2, $null, 4 | ForEach-Object {"Hello"} 
Hello 
Hello
Hello
Hello

The about_automatic_variables documentation for $null was also updated for 3.0.

Powershell 2.0 $null documentation

$NULL
   Contains a NULL or empty value. You can use this variable to
   represent NULL in commands and scripts instead of using the string
   "NULL". The string can be interpreted as TRUE if it is converted to a
   non-empty string or a non-zero integer.

Powershell 3.0 $null documentation

$NULL
       $null is an automatic variable that contains a NULL or empty value. You
       can use this variable to represent an absent or undefined value in commands
       and scripts.

       Windows PowerShell treats $null as an object with a value, that is, as an
       explicit placeholder, so you can use $null to represent an empty value in a
       series of values.  

       For example, when $null is included in a collection, it is counted as one of
       the objects.

           C:\PS> $a = ".dir", $null, ".pdf"
           C:\PS> $a.count
           3

       If you pipe the $null variable to the ForEach-Object cmdlet, it generates a
       value for $null, just as it does for the other objects.

           PS C:\ps-test> ".dir", "$null, ".pdf" | Foreach {"Hello"}
           Hello
           Hello
           Hello

       As a result, you cannot use $null to mean "no parameter value." A parameter
       value of $null overrides the default parameter value.

       However, because Windows PowerShell treats the $null variable as a placeholder,
       you can use it scripts like the following one, which would not work if $null
       were ignored.

           $calendar = @($null, $null, “Meeting”, $null, $null, “Team Lunch”, $null)
           $days = Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
           $currentDay = 0

           foreach($day in $calendar)
           {
               if($day –ne $null)
               {
                   "Appointment on $($days[$currentDay]): $day"
               }

               $currentDay++
           } 

           Appointment on Tuesday: Meeting
           Appointment on Friday: Team lunch

Note - they also made foreach keyword behave the same as ForEach-Object in 3.0 (skipping $null valued variables but converts the $null built-in to a iterable value) but they didn't mention the new behavior in the documentation like they did with ForEach-Obect.

Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
  • I don't understand... according to that logic, wouldn't `$aosfjiuhf | % { "hello" }` yield nothing too? – Nacht Sep 04 '14 at 11:35
  • @Nacht check out new answer – Andy Arismendi Sep 04 '14 at 23:51
  • Thanks, that does help me understand the change in 3.0 a little better. It sounded like something that would break lots of stuff, but that makes more sense. However I am using 2.0, so it doesn't really answer my question. The answer in the comment above by Jason Shirk seems to be what's causing my issue. $nulls are a lot more complicated than i thought! – Nacht Sep 05 '14 at 04:41
  • @Nacht if you're on 2.0 your question example isn't right - in 2.0 `$a | % { "hello" }` will return one 'hello' – Andy Arismendi Sep 05 '14 at 05:40
  • My mistake! here's my problem. I forgot that I had installed v3.0 on my win7 machine a while back. sorry! – Nacht Sep 05 '14 at 12:04
  • @Nacht ok so this should explain the behavior you are seeing then – Andy Arismendi Sep 05 '14 at 14:58
  • Powershell version 5.1 seems to have the same behavior as version 2 for this – undefinedvariable Jan 24 '18 at 19:47