PowerShell Pipeline

Introduction to the ForEach Method in PowerShell

This is a continuation of my series on a series on the Where and ForEach methods which were made available in PowerShell V4.

In this article, I will discuss using the ForEach() method and how you can use it in your day-to-day activities.

Using the .ForEach() Method
Using the .ForEach() method requires that we work with a collection, otherwise we will not have the option to use this method. Once we have a collection to work with, the next step is determining how we actually use this approach. Luckily, we can force an error which gives us more of an idea on how it can be used to iterate through the collection.

@().ForEach()
[Click on image for larger view.]  Figure 1. Finding the use by looking at the error.X

As we can see in the image above, we can use an expression, which we use in our usual activities with ForEach or ForEach-Object, but we also have an additional arguments member that can provide some other options that we can use with .ForEach() to further enhance its usefulness.

Simple Expression
The obligatory simple example of using this will simply take a collection and iterate through each item while performing a simple action. In this case, I am going through some numbers and multiplying them by two.

@(1..10).ForEach({$_ * 2}) 

Note here that I am forcing my collection using @(), even though in this case it is not necessary. I can use the well-known $_ to show that I am working with the pipeline, or I could use $PSItem instead. Either way, I am working through each item in the collection and performing an action against it.

Displaying a Property Name of an Object
We can simply specify a known property name that exists in the object to have it displayed in the method.
(Get-Service).ForEach('DisplayName')

[Click on image for larger view.]  Figure 2. Displaying the Name property of the service object.

This only works against a single property name, anything more will cause it to throw an error. If you wanted to list more than one property, stay tuned as I will show you another way to accomplish this using a param statement.

Type Conversion Example
This example shows how we can supply a type conversion against each item in the collection. In this example I will take a collection of integers and then convert them to a byte. We can verify this by piping the output to Get-Member and seeing the type.

@(1..10).ForEach([byte]) | Get-Member 
[Click on image for larger view.]  Figure 3. Running a method against an object.

Just be specifying a particular type in the ForEach method, it will attempt to convert the item to that type.

Specify a Method to Run Against Item
Similar to how we handled a property to only display, we can provide a single method with or without arguments to run against each item in the collection. Let's assume that we have notepad.exe opened up and wanted to close it for whatever reason. We can get the collection of processes with notepad and run the Kill method against it.

Start-Process -FilePath  Notepad.Exe @(Get-Process -Name Notepad).ForEach('Kill') 

Now we will run another example using Get-WMIObject to get a service and change its start mode to manual (I did change this one back to auto because I wanted to keep getting my updates).

@(Get-WmiObject -Class Win32_Service -Filter  "name = 'wuauserv'").ForEach('ChangeStartMode','Manual').ReturnValue 
[Click on image for larger view.]  Figure 4. Running a method with arguments against an object.

The 0 being returned tells me that this was successfully changed. If I had a collection of services that I needed to change the startmode of, I would filter for the services and then just run the method like this to make that change.

Using a Param Statement with ForEach
Now we can look at using a Param statement along with our expression to pull out more property names and build an object to display the data.

#Display name parameter for the scriptblock 
(Get-Service).ForEach({Param($Name)$_.$Name},'DisplayName')

I simply provide the param statement with the parameter and then use that parameter in the scriptblock to list the data in the parameter. My property name is then used outside of the scriptblock which will display the DisplayName of the service in this case.

We can add as many parameters as we need to the ForEach() method and it will handle each one in the order that it was supplied.

(Get-Service).ForEach({Param($Name,$Status)[pscustomobject]@{DisplayName=$_.$Name;Status=$_.$Status}},'DisplayName','Status')
[Click on image for larger view.]  Figure 5. Providing parameters to a ForEach method scriptblock.

Mixing it up With ForEach and Where Methods
I've shown how we can use .Where() and .ForEach() during this article and the previous one and now it is time to merge these and show how we can not only provide some filtering, but also iterate through the filtered collection by chaining these methods together.

(Get-Service).Where({$_.Status -eq 'Running'}).
ForEach({Param($Name,$Status)[pscustomobject]@{DisplayName=$_.$Name;Status=$_.$Status}},'DisplayName','Status')

In this case, I didn't do anything special with the Where filtering other than just filtering the data. But I could use one of the possible SelectionModes if needed. As soon as it is filtered, it then processes each item under the ForEach method which then would display the object showing only services that are currently running.

With that, you should go out and play with these methods and see where they could benefit you in your day-to-day operations using PowerShell!

About the Author

Boe Prox is a Microsoft MVP in Windows PowerShell and a Senior Windows System Administrator. He has worked in the IT field since 2003, and he supports a variety of different platforms. He is a contributing author in PowerShell Deep Dives with chapters about WSUS and TCP communication. He is a moderator on the Hey, Scripting Guy! forum, and he has been a judge for the Scripting Games. He has presented talks on the topics of WSUS and PowerShell as well as runspaces to PowerShell user groups. He is an Honorary Scripting Guy, and he has submitted a number of posts as a to Microsoft's Hey, Scripting Guy! He also has a number of open source projects available on Codeplex and GitHub. His personal blog is at http://learn-powershell.net.

comments powered by Disqus
Most   Popular