Step up your FOREACH Game

Last night Bruce Payette and I were interviewed on the PowerScripting Podcast with Hal Rottenberg and Jonathan Walz.  It was a blast!  There were a ton of people connected and were firehosing questions & comments in the Chat window.  There were lots of comments/discussions about Twitter.  I confessed to having never used twitter. 

This morning I got an email from Jeffery Hicks recommended a site called TweetGrid which lets you “monitor the tweet stream” without a Twitter account.  (If I get an account, do I become a Twit?)  He sent me a link to a page with search panels for PowerShell and CTP3.

I’m not ready to recommend it yet BUT the very first item on the bullet was from depping with a link to a blog post by  Hugo Peeters about script that adds RDM size info to a VI client using PowerShell.  I love looking at other peoples scripts – it gives me a view into how they think about the world and helps me understand what patterns are in use and gives me ideas about how to evolve the language and engine.

Hugo wrote a nice script – it is very easy to read and maintain.  One thing caught my eye and I left him a note about it.  He wrote:

$VMs = Get-VM
ForEach ($VM in $VMs)

This is perfectly fine code – it is the traditional way foreach loops are used.  But foreach loops in PowerShell are much more than that.  I’ve found that wherever I had code like that, I’m now converting it to the following form:

Foreach ($VM in Get-VM)

 

In PowerShell, the COLLECTION part of the statement can be ANYTHING EXPRESSION OR STATEMENT THAT GENERATES A COLLECTION.  So you can use statements in your foreach.  I don’t have VMWARE so I can give you good VM examples but here are some examples of what I mean

foreach ($p in Get-Process *ss)
{ $p.Name}
# A cmdlet is a statement which generates a collection.  Don’t worry if it
# only generates 1 value – PowerShell casts it to a collection

foreach ($p in Get-Process |where {$_.handles -ge 500})
{ $p.Name}
# A pipeline is a statement as well

foreach ($p in Get-Process |where {$_.handles -ge 500} | sort handles)
{ $p.Name}
# There is no restrictions on what you do in  your statement

 

If  you want to do something which is multiple statements, you can put them inside a $() and they are considered a single statement.
foreach ($p in $($max=700; gps |where {$_.handles -le $max})) {$p.Name}

 

Here is why I like this usage of foreach:

  1. It is more readable/maintainable.
  2. It is smaller.  IF the code is readable, smaller code is generally better.  There are fewer things to keep track of and fewer things that can go wrong.
  3. I don’t have to think up a good variable name.
  4. I get a charge of the fact that you can do this in PowerShell (not a good reason but it’s honest. :-) ).

Thanks for the nice script Hugo!

Thanks for the recommendation Jeffrey!

Thanks for a great podcast Hal and  Jonathan!

Cheers!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx