Archive for November 26th, 2008

$input gotchas

$input (a.k.a. “dollar input” or “input variable”) is one of those esoteric parts of the PowerShell language that create a lot of confusion. In fact just today there was a discussion on how it actually works on the PowerShell MVP mailing list. We even had to read the documentation to figure it out. πŸ˜‰

Anyways, basically $input in an enumerator which provides access to the pipeline you have.

So basically if you have a function which sums up the elements from the pipeline you can have something like:

Function Sum {
    $sum = 0
    $input |  foreach { $sum += $_ }
    $sum
}

1, 2, 3 | Sum

Easy. In fact if you have ever added any script actions or links in the PowerGUI admin console, this is basically what you use to get access to the selection from the central grid.

Now, there are a couple not so obvious gotchas here:

1. Enumerator is not an array

Suppose you want to know how many objects you get from the pipeline – a totally valid question. Maybe your function is supposed to only get one.

You just do $input.Count and… get nothing. Such property does not exist. This is an enumerator and it simply does not have such a property.

OK, you say, let’s wrap it into an array and we’ll learn the size:

@($input).Count

This works… Kind of… If you modify our example above to:

Function Sum {
    "Number of elements: " + @($input).Count
    $sum = 0
    $input |  foreach { $sum += $_ }
    "Sum is: " + $sum
}

1, 2, 3 | Sum

You get:

Number of elements: 3
Sum is: 0

The first line is correct – we had 3 elements. But why the heck is the sum 0 now?

Well, $input is an enumerator, and when you use it – you get to the next element. So once we used it to create a temporary array we got to its end. To fix it, simply reset it back:

Function Sum {
    "Number of elements: " + @($input).Count
    $input.Reset()

    $sum = 0
    $input |  foreach { $sum += $_ }
    "Sum is: " + $sum
}

1, 2, 3 | Sum

Now we are good again:

Number of elements: 3
Sum is: 6

2. Just using $input holds the pipeline till all objects are collected

This second one was spotted by Per here. He tried using $input and noticed that his function did not get executed until the whole pipeline was processed (that is his function was not invoked for each element one by one, but rather for the whole collection) – read his post for details.

This happens because according to PowerShell help:

“In the Process block of a function, $input contains the object currently in the pipeline… If the function does not have a Process block, the value of $input is available to the End block, and it contains all of the input to the function.”

So basically the reason for the code above to process the whole collection (rather than go item by item) is that we did not have a process block inside the function, so if you care about item by item processing – go with the process block, if not – feel free to use $input.

And by the way, inside the process block just stick to $_ like this:

Function Sum {
  begin { $sum = 0 }
  process { $sum += $_ }
  end { $sum }
}

1, 2, 3 | Sum

Using $input inside process is a hustle and Oisin promised a post on his blog on the reasons why. πŸ˜‰

Tags: , , ,


Legal

The posts on this blog are provided β€œas is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer - WSO2 or anyone else for that matter. All trademarks acknowledged.

Β© 2007-2014 Dmitry Sotnikov

November 2008
M T W T F S S
 12
3456789
10111213141516
17181920212223
24252627282930