Prof. Powershell
ForEach Building Blocks
When we can't leverage the pipeline, we can turn to the ForEach-Object cmdlet to help with some heavy lifting.
- By Jeffery Hicks
- 02/28/2012
In PowerShell we always look for ways to leverage the pipeline. Ideally we want to take objects from one cmdlet and pipe them to another:
PS C:\> get-service | where {$_.status -eq "running"} | measure
Count : 102
Average :
Sum :
Maximum :
Minimum :
Property :
But sometimes this isn't possible with a collection of objects. For these situations we turn to the ForEach-Object cmdlet. You've most likely seen or written commands like this:
PS C:\> 1..10 | foreach {$_*(Get-random -min 1 -max 10)}
5
16
6
4
15
36
7
8
63
40
The numbers 1 through 10 are piped to ForEach-Object which executes the code in the curly braces, once for every piped in object. The piped in object is represented by $_. In this example, I'm multiplying it by a random number between 1 and 10. It is probably safe to say that this type of syntax is used 90 percent of the time or more. But there is a bit more to this cmdlet.
Technically there are three scriptblocks you can use with this cmdlet. These scriptblocks are the same that are used by cmdlet developers:
- Begin -- commands that run once before any pipelined objects are processed
- Process -- commands run once for each pipelined object
- End -- commands that run once after all pipelined objects have been processed.
The default is the Process scriptblock which is the way we normally use this cmdlet. The other two scriptblocks are completely optional. You can use either or both. Here's a simple example:
get-process | foreach -begin {
write-host "Formatting Workingset for:" -fore green
$a=@()
} -process {
Write-Host " $($_.name)" -fore green
$a+=$_.ws/1mb
} -end {
$a | measure-object -sum
}
The objects from Get-Process will be piped to ForEach-Object. But before any processing, the Begin scriptblock runs, which simply displays a text message and defines an empty array. The code in the Process script block is then executed for every piped in object. In my example I'm displaying the process name, dividing the workingset value by 1MB and adding that value to the array. After all of the process objects have been handled, the code in the End scriptblock executes which is taking the array and piping it to Measure-Object to calculate the sum.
Okay, maybe this isn't the most earth-shaking example, but I hope it is simple enough to illustrate the concept. When writing a construct like this, you should use the parameter names to make it clear where each scriptblock belongs. You also should enter the next parameter name after the closing brace on the preceding scriptblock, so PowerShell parses your expression properly.
This is a very powerful cmdlet, especially if you want to do multiple things to each object. If you need it, use the Begin scriptblock for any setup or initialization commands and the End scriptblock to close and clean up. Take advantage of these building blocks to build something amazing.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.