Prof. Powershell

ForEach and Everyone

Whether you're using ForEach as a cmdlet or a construct, PowerShell will know what you mean.

In this week's lesson I want to revisit a topic that still seems to throw people: Using a for each enumerator with the PowerShell pipeline. Most of the time, when working with a collection of things, we try to use the PowerShell pipeline:

PS C:\> get-process | where {$_.workingset -ge 100MB} | Select name

Get all the process objects, filter out those with a working set that isn't at least 100MB and then just select the Name property. But sometimes we want to work with objects one at a time. On other words, we need to enumerate the collection. This can be done with the ForEach-Object cmdlet.

Typically, we pipe a bunch of stuff to this cmdlet and use a script block to dictate what should happen to each object as it is processed:

PS C:\> "alice","bob","charlie" | foreach-object {$_.ToUpper()}
ALICE
BOB
CHARLIE

Here I took three strings and piped them to ForEach-Object. The code in the {} scriptblock is executed once for every piped-in object. It can be as many lines of code as necessary. The $_ is a placeholder for each processed object. In this case, I'm invoking the ToUpper() method for each incoming object. The results are written to the pipeline:

PS C:\> "charlie","alice","darla","bob" | foreach-object {$_.ToUpper()} | sort
ALICE
BOB
CHARLIE
DARLA

Using ForEach object is great, especially when you may not know in advance what objects you will be dealing with.

The other enumeration technique is ForEach, which unfortunately is also the alias for ForEach-Object. But PowerShell can tell from context which you mean. This construct is great when you know in advance the items you want to work with. It is also a little easier to read because you can use whatever variable you want as the placeholder. You don't need to use the cryptic $_:

PS C:\> $numbers=1,3,5,7,9
PS C:\> foreach ($number in $numbers) { ($number*$number)*3.14}
3.14
28.26
78.5
153.86
254.34

Again, you can have as much code as you need in the {} scriptblock. All I'm doing here is multiplying the number by itself and then by 3.14. I like this technique when I want to do several operations with each object. It just seems easier to read.

But, when you use this ForEach nothing comes out of the pipeline after the scriptblock which means you can't pipe this to anything else:

PS C:\> foreach ($number in $numbers) { ($number*$number)*3.14} | sort -descending
An empty pipe element is not allowed.
At line:1 char:58
 + foreach ($number in $numbers) { ($number*$number)*3.14} | <<<< sort -descending
 + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
 + FullyQualifiedErrorId : EmptyPipeElement

But if your main concern is only what happens within the scriptblock, this should be no big deal. Otherwise, you might have to restructure using Foreach object. This command will work but it may be a little harder to follow and also it is usually a little slower:

PS C:\> $numbers | foreach { ($_*$_)*3.14} | sort -descending

So, first determine if you even need to enumerate objects. Then determine if you need to incorporate your code into a longer pipelined expression.

I encourage you to read full help and examples for ForEach-Object and about_ForEach.

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.

comments powered by Disqus
Most   Popular