PowerShell with a Purpose Blog

ForEach Makes me Die a Little Inside

I was recentlyreading this post on the Scripting Guys' blog, and noticed in a comment that someone lamented the fact that you can't just throw Get-ADComputer into parentheses to get computer names. In other words, this won't work:

Invoke-Command -computername (Get-ADComputer -filter *) -script { dir c:\ }

The reason is doesn't work is simple: The parenthetical expression is returning entire computer objects, but the -computerName parameter only wants simple strings. One helpful commenter posted a solution:

Invoke-Command -computername (Get-ADComputer -filter *| %{$_.name}) -script {dir c:\}

JRV, I'm gonna pick on you a bit :). Nicely. First of all, I freakin' hate the % alias. Talk about hard to read and communicate. I wish we could kill that alias for all time, at least in blog posts. The real command is this:

Invoke-Command -computername (Get-ADComputer -filter *| ForEach-Object {Write-Output $_.name}) -script {dir c:\}

Notice that I explicitly inserted the Write-Output cmdlet, too - it was implied before, but I think this reads more clearly with it explicitly typed out.

And that still makes me die a little bit inside. Generally speaking, using ForEach-Object means you've either run into something that the shell should do for you, but doesn't, or that you're not really using the shell properly. In the latter case, it's perhaps an instance of the poster not realizing that there's a more PowerShell-centric way:

Invoke-Command -computername (Get-ADComputer -filter * | Select -expand name) -script {dir c:\}

Why do I like this better? Because ForEach is tricky for a lot of folks to process in their brain. It's also physically less efficient. Select-Object is perfectly capable of taking a whole batch of objects and extracting a single property - in this case, the name of the computer. There's no need to force the shell to enumerate each computer object and write out the name property.

Now, I'm not picking on JRV, who helpfully posted this tip. After all, what he/she posted does work just fine. But I think this is a good opportunity to point out an additional way of doing things. For one, it's good to know about the -expandProperty parameter of Select-Object, because it's useful in a number of situations. It's also good, just for the long-term benefit of mankind, to eliminate a use of ForEach when possible (I think Select-Object is easier to mentally parse, if for no other reason than because it eliminates a set of {curly brackets}). 

This also highlights the point that, in PowerShell, there's usually many ways to do the same thing. No way is necessarily better than another, and whatever gets the job done for you most easily is the one to use. But... if you've found yourself using ForEach, then there's a good chance there's a slightly easier, less programmer-y way that you could also use!

And I just hate that percent sign. <grin>

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.