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.
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