Powershell: nulls, empty arrays, single-element arrays

One of the biggest gotchas for people new to Powershell is the handling of null, empty arrays, and single-element arrays.  If you come from another language such as c# you’ll be shocked.  And even for an experienced Powershell writer, the behavior can lead to ugly bugs.

Other people have covered this topic before.  Here’s a good discussion by Keith Hill.  But I wanted a short reference with examples of the strange (to many people) behavior, so I’m writing this today somewhat for my own benefit.  But maybe someone else will find it useful.

I’m usually trying to handle the return value of a function or cmdlet when I encounter these oddities, so that’s how my examples are structured.

Here is the sample code.  Try reading through this and predicting the output.  (Don’t read the summary at the bottom of the code.)  Then run it and see if you were right.

# Compare the results of returning an empty array, a null, or any other single value

function testEmptyArray
{
	$x = @()
	
	# explicitly return an array, but the calling code determines the actual result
	return $x
	
}

function testNull
{
	return $null
}


function testOneValue
{
	return "The Value"
}

function testOneElementArray
{
	return @("the element")
}


cls

" ------------- return a NULL -------------"
$a = testNull
$a.gettype() | Select-Object Name		# this is an error since $a is null
if ($a -eq $null) {"a is null"} else {"a is not null"}
"number of elements in `$a:" + $a.Count
foreach ($element in $a)
{
	if ($element -eq $null) {"this element of a is null"} else {"this element of a is not null"}
}


"`r`n`r`n ------------- return NULL, force to array -------------"
$b = @(testNull)
$b.gettype() | Select-Object Name
if ($b -eq $null) {"b is null"} else {"b is not null"}
"number of elements in `$b:" + $b.Count
foreach ($element in $b)
{
	if ($element -eq $null) {"this element of b is null"} else {"this element of b is not null"}
}




"`r`n`r`n ------------- return an empty array -------------"
$c = testEmptyArray
$c.gettype() | Select-Object Name		# this is an error since $c is null
if ($c -eq $null) {"c is null"} else {"c is not null"}
foreach ($element in $c)
{
	if ($element -eq $null) {"this element of c is null"} else {"this element of c is not null"}
}


"`r`n`r`n  ------------- return empty array, force to array -------------"
$d = @(testEmptyArray)
$d.gettype() | Select-Object Name
if ($d -eq $null) {"d is null"} else {"d is not null"}
"number of elements in `$d:" + $d.Count
foreach ($element in $d)
{
	if ($element -eq $null) {"this element of d is null"} else {"this element of d is not null"}
}


"`r`n`r`n  ------------- return single value -------------"
$e = testOneValue
$e.gettype() | Select-Object Name
"number of elements in `$e:" + $e.Count		# note: c does not have the Count property, since it's not an array
foreach ($element in $e)
{
	"this is an element of e."
}


"`r`n`r`n  ------------- return array with one element -------------"
$f = testOneElementArray
$f.gettype() | Select-Object Name
"number of elements in `$f:" + $f.Count
foreach ($element in $f)
{
	"this is an element of f."
}

"`r`n`r`n  ------------- return array with one element, force to an array -------------"
$g = @(testOneElementArray)
$g.gettype() | Select-Object Name
"number of elements in `$g:" + $g.Count
foreach ($element in $g)
{
	"this is an element of g."
}


"`r`n`r`n ------------- summary --------------"
"`$a = testNull `t`t(this results in a `$null)"
"`$b = @(testNull) `t(this results in an array with one element, whose value is null)"
"`$c = testEmptyArray `t(this results in a null, even though the function returns an array--but POSH will still walk it like an array)"
"`$d = @(testEmptyArray) `t(this results in an empty array)"
"`$e = testOneValue `t(this behaves as you might expect--except that you can walk through it like an array despite the object type)"
"`$f = testOneElementArray `t(this results in a string even though an array was explicitly returned)"
"`$g = @(testOneElementArray) `t(this results in an array of one element)"

These are a few of the rules you should know, illustrated by the above examples:

  1. You can iterate over a variable holding a single value as if it were an array, even if the value is $null.
  2. A function that returns an array with a single value might not be seen as an array to the code calling it.  Because of rule one, you can still iterate over it, but it won’t have array properties such as Count.
  3. If you want to guarantee that you get an array from a function, wrap the call with @().
  4. If you follow rule three, and the function returns a null, you will get an array with a single element, whose value is null.

————– EDIT —————–
The above was written a long time ago, using PowerShell 2.0. Behavior has changed significantly with newer versions of PowerShell. This test code is still useful though. Run on YOUR version of PowerShell and see whether you can predict the results.

Advertisements

4 Responses to “Powershell: nulls, empty arrays, single-element arrays”

  1. brustverkleinerung Says:

    Wonderful web site. A lot of useful info here.
    I’m sending it to some buddies ans also sharing in delicious.
    And obviously, thanks on your effort!

  2. ozOracle Says:

    As a beginner power-shell script developer, that was very interesting.
    Thanks for sharing!

  3. David J Says:

    “If you want to guarantee that you get an array from a function, wrap the call with @().”

    I kept trying to find ways to force the array at the return statement, which is a fool’s errand. A lot of search results try to force the array with something like: return , $myResult
    just leads to buggy scripts, hours wasted 🙂

    Thanks for posting this article

  4. Calling Reporting Services’ CreateReport() from Powershell « Surrounding The Code Says:

    […] The specification for CreateReport() provided by Microsoft is inaccurate, as far as I can tell.  In c# it won’t matter, but it does in Powershell, due to the oddities in how Powershell handles empty arrays. […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: