Understanding Simplified Foreach Syntax in PowerShell 3.0

Understanding Simplified Foreach Syntax in PowerShell 3.0

  • Comments 3
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, talks about the simplified Foreach syntax in Windows PowerShell 3.0 and provides guidance on when to use which syntax.

Microsoft Scripting Guy, Ed Wilson, is here. I finally had a bit of time to get some stuff done that I had been needing to do, such as submitting my session proposals for the Windows PowerShell Summit, and registering for Windows PowerShell Saturday in Atlanta. It is amazing how much time simple things seem to take. Both of these events will be really cool. Of course, the one I am concentrating on right now is Windows PowerShell Saturday #003 in Atlanta, Georgia on October 27, 2012.

PowerShell 3.0 simplified Foreach-Object syntax

One of the cool things about Windows PowerShell 3.0 is the simplified syntax. Whereas the simple syntax for the Where-Object is straightforward, the simple syntax for the Foreach-Object cmdlet does not work the way one might expect. For example, using the simple Where-Object works as illustrated in the following example.

PS C:\> Get-Process | where name -match word

 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

-------  ------    -----      ----- -----   ------     -- -----------

    687      55    72940     135240   717    68.11   1208 WINWORD

For reference, the Windows PowerShell 1.0 and Windows PowerShell 2.0 syntax of the previous command are shown here.

Get-Process | where {$_.name -match 'word'}

Now, there is also a simplified syntax for the Foreach-Object cmdlet. It is shown here.

PS C:\> Get-Process -Name w* | foreach name

wininit

winlogon

WINWORD

wmpnetwk

WUDFHost

The old Windows PowerShell 1.0 and Windows PowerShell 2.0 way of doing this is shown here.

Get-Process -Name w* | foreach {$_.name}

As you can see, the old method requires a script block and the automatic variable $_.

Incidentally, it seems that the $_ automatic variable is hard for some people to understand—or at least it seems to bother some people. (That is what I hear. Personally, in all the years I have been teaching Windows PowerShell to people, I have never run across someone who had a major problem with the $_ automatic variable, but that is just my experience. I can certainly conceive of someone not liking the $_ automatic variable.)

Problems with the simple Foreach-Object syntax

Wow, it is great! We have a simple Foreach-Object syntax! The problem is that it is really simple (meaning that it does not do what I would like it to do—at least not exactly). Most of the time, when I am using the Foreach-Object cmdlet, I am doing something. If I only wanted the names from a collection, I would probably use the Select-Object cmdlet as shown here.

PS C:\> Get-Process -Name w* | select name

 

Name

----

wininit

winlogon

WINWORD

wmpnetwk

WUDFHost

But I might be interested in doing something to the names, like using the ToUpper method from the System.String .NET Framework class. In the old days, this is the syntax I would use:

PS C:\> Get-Process -Name w* | foreach {$_.name.toupper()}

WININIT

WINLOGON

WINWORD

WMPNETWK

WUDFHOST

Therefore, I should be able to do the same thing in the new simplified syntax as shown here.

PS C:\> Get-Process -Name w* | foreach name.toupper()

At line:1 char:45

+ Get-Process -Name w* | foreach name.toupper()

+                                             ~

An expression was expected after '('.

    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordExcepti

   on

    + FullyQualifiedErrorId : ExpectedExpression

Nope. It does not work. Bummer! So, I can go back to my “old” syntax, or I can use the new $PSItem variable. Because, some people do not like the $_ automatic variable, a new $PSItem variable is created, to do the same thing. Personally, I never use it because it is too much typing. It is shown here.

PS C:\> Get-Process -Name w* | foreach {$psitem.name.toupper()}

WININIT

WINLOGON

WINWORD

WMPNETWK

WUDFHOST

There is a way to use the simple syntax, and to avoid using the $_ or the $PSItem automatic variables. It involves using my group and dot technique. This is shown here.

PS C:\> (Get-Process -Name w* | foreach name).toupper()

WININIT

WINLOGON

WINWORD

WMPNETWK

WUDFHOST

This syntax is just a bit shorter, but is not necessarily any cleaner than the other two ways of doing this. What happens is that the first command returns the names, and because of the way that Windows PowerShell 3.0 does the automatic Foreach to permit working with a collection, it calls the ToUpper method. This could also be written as shown here.

PS C:\> (Get-Process -Name w*).name.toupper()

WININIT

WINLOGON

WINWORD

WMPNETWK

WUDFHOST

In the example above, the Foreach happens automatically; and therefore, it does not need to be explicitly called. In fact, this is the way that I would more than likely write this code. To me it is readable, it is certainly cleaner, and when you understand that Windows PowerShell 3.0 provides direct access to properties from a collection (the automatic Foreach), it makes sense.

These commands and their associated output are shown in the image that follows.

Image of command output

Join me tomorrow when I will talk about more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • <p>Hi Ed,</p> <p>very well explained and exacltly what I think about the new simplified foreach syntax.</p> <p>(Get-Process -Name w*).name.toupper()</p> <p>is my favorite way of writing the code, too.</p> <p>Expanding the collection implicitly and running the &quot;automatic foreach&quot; on each element of a collection is quite easy to understand. </p> <p>Using a little bit more complex statements with the new simplified foreach like:</p> <p>Get-Process -Name w* | foreach name.toupper()</p> <p>which returns an error, is not that easy to understand. </p> <p>It seems to be a half hearted solution and I really don&#39;t know why the Powershell development team has stopped here ( I guess they had very good reason to do so ... but from an end-user&#39;s point of view, this is not WYTIWYG ( What You Think ... Is What You Get :-))</p> <p>Klaus.</p>

  • <p>The short form of foreach-object is really nice, it can be used to expand properties easily</p> <p>ps | % name</p> <p>ps | % { $_.name}</p> <p>ps | select -expand name &nbsp;(this one fails if the name is null)</p>

  • <p>Hi there. Could you please help me with an issue I am having, I have used this batch mailbox script many times before but it has suddenly decided to stop working it appears to run but does not create any mailboxes and does not give any error messages at all. Am I missing something blindingly obvious?</p> <p>$c = Get-Credential</p> <p>Import-CSV Test.csv | foreach {New-Mailbox -SamAccountName $_.samaccountname -Name $_.name -UserPrincipalName $_.UPN -Database “Brumbies” -OrganizationalUnit &#39;blucloud.com.au/BC ESP/Tenant&#39; -AddressBookPolicy &#39;Brumbies&#39; -Password $c.password -ResetPasswordOnNextLogon $false}</p> <p>I tried a similar script from technet which is pretty much the same as mine</p> <p>$c = Get-Credential</p> <p>$users = Import-CSV &quot;C:\Test.csv&quot; </p> <p>$users| foreach</p> <p>{</p> <p>New-Mailbox -SamAccountName $_.samaccountname -Name $_.name -UserPrincipalName $_.UPN -Database “Brumbies” -OrganizationalUnit &#39;blucloud.com.au/BC ESP/TEST&#39; -AddressBookPolicy &#39;Brumbies&#39; -Password $c.password -ResetPasswordOnNextLogon $false</p> <p>}</p> <p>Although when I run this one I get the following message</p> <p>Process[0]:</p> <p>Any help would be hugely appreciated.</p> <p>Thanks</p>