Friday, May 1, 2009

Advanced Functions: Using Values from the Pipeline (2.0 CTP3)

Note: The following code will only work on Powershell 2.0 CTP3 or later.

A while back I showed how you can use $input as a parameter to a function in order to use it in the pipeline. Unfortunately that technique has the side effect of making PowerShell stop and gather up all variables that are being sent down the pipeline into $Input and then passing it to the function. Ordinarily it doesn't matter much, but if you are processing a lot of data, say the results of a SQL query, you may end up using up massive amounts of memory and watch your computer grind to a halt while the memory manager swaps a billion times a second (okay, I made that number up).

The ValueFromPipeline Parameter Property

As the name suggests, this property indicates whether or not a parameter can take a value from the pipeline. Just set it to $true when declaring the variable (you'll see an example at the bottom of this post).

Doing that alone will not give you the effect you're looking for, though. It will mysteriously process the first item passed down the pipeline...and then suddenly stop. This was very frustrating when I first came across it, and left me scratching my head, until a post on the microsoft.public.windows.powershell group happened to mention the critical missing piece in an unrelated discussion.


BEGIN, END, and PROCESS Blocks

The first thing you need to know is that if you want to take a value from the pipeline and use it, then you need to add in the optional PROCESS Block. Every function essentially has a BEGIN, END, and PROCESS scriptblock in it:
  • BEGIN - This scriptblock is run the first time the function is launched
  • PROCESS - This scriptblock is run each time the function receives input
  • END - This scriptblock is run after all input has been processed
Here's the important part: explicitly declaring these blocks is optional, but if you don't use them, then the body of your function executes in the END Block. That's why it was only executing once.

As an example of a script that uses these blocks, take a look at this little ditty I whipped up for work (I highlighted the important parts):




No comments: