Arnim van Lieshout Rotating Header Image

$Null or Nothing?

When looking at a lot of PowerCLI scripts available I noticed that people tend to forget to validate their output. For example let’s look at the following piece of code:

$Vms = Get-VM

Although this command will return a collection in almost all cases, it can however return 3 different output types. It can return a collection, a scalar or no output at all. So when you simply assume that it returns a collection, you can go wrong on the next statements. Let’s try to clear things up and look at the following slightly different code:

$Vms = Get-VM
ForEach ($Vm in $Vms) {
  $Vm.GetType()
}

This simply returns the object type of every virtual machine. Now let’s alter the code a bit and notice the Where-Object cmd-let to filter the output of Get-VM:

$vms = Get-VM | Where-Object {$_.Name –eq "Hyper-V"}
ForEach ($Vm in $Vms) {
  $Vm.GetType()
}

This code will generate the following error, unless you’re a Microsoft masochist and really have a virtual machine named “Hyper-V”. ;-)

You cannot call a method on a null-valued expression.
At line:1 char:35
+ Foreach ($Vm in $Vms) {$Vm.GetType <<<< ()}
+ CategoryInfo : InvalidOperation: (GetType:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

You probably wonder how that’s possible, because the first line will return nothing and therefore the ForEach statement shouldn’t do anything. Well not quite. The Get-VM part of the first line will return nothing and when assigning nothing to a variable in PowerShell, $Null is used to represent this empty set. So far so good, but there are some things you should know about PowerShell.

  1. $Null is treated as a scalar.
  2. The ForEach statement iterates a scalar once, because it is treated as an array with only one element, the scalar itself.

There you have it, because $Null is treated as a scalar, the ForEach statement iterates once and invoking the GetType() method on $Null will generate the aforementioned error. Therefore it is important that you always validate your output.

Now let’s look at a variation of this code:

ForEach ($Vm in (Get-VM | Where-Object {$_.Name –eq "Hyper-V"})) {
  $Vm.GetType()
}

Running this code won’t generate an error, because the Get-VM part is now inside the ForEach statement. (Get-VM | Where-Object {$_.Name –eq "Hyper-V"}) will still return nothing and the ForEach statement won’t run when nothing is returned. Spot the difference?

The problem is introduced by the use of the intermediate variable $Vms where nothing is replaced by $Null (a scalar meaning nothing)!

There are two ways to avoid this kind of error:

  1. Do not make use of the intermediate variable and put the entire statement inside the ForEach statement, which we just did in the last example.
    ForEach ($Vm in (Get-VM | Where-Object {$_.Name –eq "Hyper-V"})) {
      $Vm.GetType()
    }
  2. If the statement tends to grow rather large and putting it inside the ForEach statement renders the code unreadable, you could make use of the array subexpression to force the intermediate variable to be an array.
    $vms = @(Get-VM | Where-Object {$_.Name –eq "Hyper-V"})
    ForEach ($Vm in $Vms) {
      $Vm.GetType()
    }

So write your scripts wisely using one of these options and make them more robust and less error prone.

Also check out this Microsoft discussion about this subject:

https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=281908&SiteID=99

1 Comment on “$Null or Nothing?”

  1. #1 Bouke Groenescheij
    on Dec 15th, 2009 at 9:36 am

    Thanks for the very clear explanation!

Leave a Comment