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.

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 a Microsoft MVP in Windows PowerShell, Microsoft Certified Trainer and an IT veteran with over 20 years of experience, much of it spent as an IT consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He works today as an independent author, trainer and consultant. Jeff writes the popular Prof. PowerShell column for MPCMag.com and is a regular contributor to the Petri IT Knowledgebase and 4SysOps. If he isn't writing, then he's most likely recording training videos for companies like TrainSignal or hanging out in the forums at PowerShell.org. Jeff's latest books are Learn PowerShell 3 in a Month of Lunches, Learn PowerShell Toolmaking in a Month of Lunches and PowerShell in Depth: An Administrators Guide. You can keep up with Jeff at his blog http://jdhitsolutions.com/blog, on Twitter at twitter.com/jeffhicks and on Google Plus (http:/gplus.to/JeffHicks)

Reader Comments:

Thu, Mar 22, 2012

^^ Yeah right. I have 60,000+ users, in 1000+ OUs. You tell me how to get a csv of all their home folder locations. I'll wait while you fire up ADUC and check all their profiles... INDIVIDUALLY. or I could just ... $ous=@() $ous | % { Get-QADUser -Enabled -SearchRoot foo.bar.com/$_ -SizeLimit 0 | select samaccountname, DN, HomeDirectory, HomeDrive } | Export-csv 'homedrives.csv' I've got the results, how about you? Ridiculous is right heh

Wed, Feb 29, 2012

It seems there should be a balance and choice, use the GUI when it makes sense and use a script when it makes sense. The GUI, scripting, computers, life is made of contrast so that we may choose what works best for us.

Wed, Feb 29, 2012 David

Because the GUI isn't very efficient when you need to process a few hundred items (or more), and it takes multiple mouse clicks for each item, especially if different computers are involved. Not to mention the possiblility of human error in repeating the same task hundreds of times. For example, if you had to check the members of the local Administrators group on 500 computers, do you want to do that manually, or use a script? If you're familiar with PowerShell, you could save yourself many hours by writing and using a script, rather than the GUI.

Tue, Feb 28, 2012 Don

While I agree that scripting for non-recurring unusual tasks is important this is getting rediculous. Why don't we all just go backwards to DOS and do away with the GUI. Its just rediculous and getting way out of hand. ANYTHING that can be scripted can be given the efficency of a GUI

Add Your Comment Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above