Introducing the Pipeline and ForEach

Introducing the Pipeline and ForEach

Warming up to more complex Active Directory one-liners

Last month, in "Where-Object and the Pipeline," I showed you PowerShell's pipeline and the pipeline's Secret Name ($_). In previous columns, you've seen how the pipeline can let you combine two Active Directory (AD) cmdlets and get some non-trivial work done, such as the following combination that would find all users who haven't logged on in 90 days, then disable their accounts:

search-adaccount –usersonly –AccountInactive –timespan "90" | disable-ADAccount

Although that "one-liner" looks simple, it masks a bit of complexity that can't remain masked if we want to tackle other one-liners that are only mildly more complex.

Consider the following problem: In auditing your AD environment, you find that although your user accounts have their first names, middle initials, and last names properly entered in their correct attributes, the DisplayName attribute is empty. You'd like to populate it. The names of the attributes for first name, middle initials, and last name are respectively called givenname, initials, and sn (I'm pretty sure there was drinking going on when the X.500 committee came up with those names!), and you'd like to automatically construct a DisplayName out of those three attributes for each user, fixing a flaw in your otherwise-tidy AD implementation. (OK, it's a bit contrived, but it's non-trivial enough to be a good example.)

To do that, you need to understand a command called ForEach-Object, which has the aliases foreach and % (yep, just a simple percent sign). Like the For command I showed you years ago, it's one of those PowerShell cmdlets that takes a useful one-off tool and maks it a "power tool." ForEach works like this:

<commands that load up the pipeline> | foreach { <cmdlets that do something with the pipeline contents> }

How do you load up a pipeline? Every time you run a cmdlet that produces any information at all, you stuff things in a pipeline. (If you have 15 users, typing get-aduser -filter * will put 15 objects in the pipeline.) But a simpler way to load a pipeline is to simply enter some pieces of data on the command line, separated by commas. For example, just type


press Enter, and you'll see three lines: one for 1 by itself, one for orange, and one for 77. It's the simplest way to stuff a pipeline.

In that case, I just loaded the pipeline but did nothing else. Whenever you do that, PowerShell says "Hmm, nothing more to do," and just writes the objects in the pipeline to the screen. To do something more complex, you need ForEach. After the ForEach statement, PowerShell needs one or more cmdlets between a left and right curly brace with semicolons between each cmdlet—a structure that PowerShell calls a scriptblock.

Another simple example is

"Wally",7,33,"Cloud" | foreach { "There's something in the pipeline" }

Type that, and There's something in the pipeline will appear four times on the screen. Put simply, PowerShell first filled the pipeline with the four objects, then handed that filled pipeline to ForEach.

But understanding what ForEach does and how it does it is important to getting the most out of ForEach, so let's take this slowly. One at a time, ForEach takes the next object in the pipeline and does three things with that object. First, it temporarily stores the object in PowerShell's pipeline variable, $_. Second, it executes whatever cmdlets are in the scriptblock. Third, it discards the object, getting ready to go back and grab the next one.

Thus, in this case, ForEach first grabs "Wally", stuffs it in $_, then does what the scriptblock says to do, which is extremely simple: displays There's something in the pipeline on the screen. (Notice that ForEach ends up not making any use of $_. That's OK, again due to the simplicity of the example.) ForEach then throws "Wally" away, grabs the number 7, puts it in $_, displays There's something in the pipeline on the screen, then discards the 7. It does it all again with 33 and "Cloud", and then the pipeline is empty, and it's done.

But now, try a small change in the scriptblock:

"Wally",7, 33,"Cloud" | foreach { "Pipeline contains:" + $_ }

Run that and you'll see a different line every time, such as

Pipeline contains:Wally
Pipeline contains:7

And so on. The difference is that this time around, instead of unchanging, static text, you added $_, which changes every time. (The plus sign means stick the static text "Pipeline contains:" string and whatever is currently in $_ together. Techies call this concatenation.)

Yes, those are trivial examples, but I hope they've given you a feel for how ForEach works so that I can start showing you its power as we tackle the DisplayName cleaning task. See you next time!

Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.