The PowerShell Foreach statement is useful when working with collections of items. It can be used to execute a command or a set of commands for each item in a collection. In this article, I will explain three different ways to use the Foreach keyword: the Foreach loop, the ForEach object cmdlet, and the ForEach method.
With PowerShell, there are different ways to process a collection of items. A collection could be retrieved from files using commands such as Get-Content for text files, Import-CSV for comma-separated value files, and Import-CliXML for hierarchical data stored in an XML file, etc. The collection could also be the result of a command execution or a filtered result using the Where-Object cmdlet.
When you need to perform an action against the members of this collection, you will be using the Foreach statement, which can be used as a loop, a cmdlet, or a method. In general, loops in PowerShell allow you to repeat a command or a set of commands until a condition is met.
If you use a Foreach loop, the operations will repeat until all items in the collection are processed. This reduces the size of the code and makes it more readable.
A PowerShell Foreach loop will move through the items in a collection and execute a command or a set of commands for each of them. The syntax you need to use is pretty simple.
A PowerShell Foreach loop starts with the keyword followed by parenthesis with two variables: One for the collection and one for the current item that is being processed. The item variable is automatically created and assigned the value of the next item in the array before each iteration of the loop until all items are processed.
foreach ($<item> in $<collection>){<statement list>}
At the end of the loop, the variable is not removed and contains the last processed item. The syntax continues with a script block containing a command or a list of commands which are executed against each item in the collection.
To demonstrate how to use a Foreach loop, let’s take the following example: We will take three services – WinRM, Spooler, and BITS providing information about the Display name, Service name, Status, and Start type. Display name will be on a separate line to simulate multiple commands being executed.
Without using any loop, the script would look like this:
# Service: WinRM $ServiceWinRM = Get-Service -Name WinRM Write-Host Processing Service: $ServiceWinRM.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $ServiceWinRM.Name is with status: $ServiceWinRM.Status and start type: $ServiceWinRM.StartType -BackgroundColor DarkGreen # Service: Spooler $ServiceSpooler = Get-Service -Name Spooler Write-Host Processing Service: $ServiceSpooler.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $ServiceSpooler.Name is with status: $ServiceSpooler.Status and start type: $ServiceSpooler.StartType -BackgroundColor DarkGreen # Service: BITS $ServiceBITS = Get-Service -Name BITS Write-Host Processing Service: $ServiceBITS.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $ServiceBITS.Name is with status: $ServiceBITS.Status and start type: $ServiceBITS.StartType -BackgroundColor DarkGreen
This script provides the following result:
If we use this initial example and modify it using the Foreach loop syntax, our code would look like this:
$serviceList = Get-Service -Name 'WinRM', 'Spooler', 'BITS' foreach ($service in $serviceList) { Write-Host Processing Service: $service.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $service.Name is with status: $service.Status and start type: $service.StartType -BackgroundColor DarkGreen }
By following the syntax of a Foreach loop, the variable containing the collection of services is named ‘serviceList’. The variable containing the current item that is being processed is named ‘service’.
Here’s what happens when we run this example in PowerShell:
As we explained, the variable containing the currently processed item in the collection is being created on the first iteration of the loop. It remains available once the loop completes.
Another way to process items in a collection is to use the PowerShell Foreach-Object cmdlet. There’s often some confusion about this command as it has an alias with Foreach, which looks like the loop statement covered in the previous section.
However, it is quite easy to differentiate the two of them: The Foreach loop is always at the beginning of the statement, while the Foreach alias appears after a pipeline symbol, processing the collection coming through the pipe.
The Foreach-Object command also has another alias which you may see here and there – %. The general rule in PowerShell is to avoid using aliases in your code as it makes it harder to read and could lead to confusion. Aliases can be handy to save some time while typing, but be mindful when you are writing your code and try to make it more readable for the next person reading it.
The Foreach-Object cmdlet processes the objects send through the pipeline using the following (simplified) syntax:
<collection> | Foreach-Object -Process {<statement list>}
Items from the collection are sent through the pipeline one after the other, and they’re processed in the script block of the Process parameter. There could be one or more lines of code.
Again, as in a Foreach loop, we have a variable containing the current item that is being processed. This time, though, it is a built-in variable (- $PSItem or $_) that’s only available until the pipeline is completed.
If we keep the same example as before with the three services WinRM, Spooler and BITS, here’s how our code would look like if we use the Foreach-Object cmdlet:
Get-Service -Name 'WinRM', 'Spooler', 'BITS' | ForEach-Object -Process { Write-Host Processing Service: $_.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $_.Name is with status: $_.Status and start type: $_.StartType -BackgroundColor DarkGreen }
The result is the same as in the previous examples. Here we are using the built-in variable ‘$_’, but it would be the same if we used ‘$PSItem’ as they are interchangeable.
Get-Service -Name 'WinRM', 'Spooler', 'BITS' | ForEach-Object -Process { Write-Host Processing Service: $PSItem.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $PSItem.Name is with status: $PSItem.Status and start type: $PSItem.StartType -BackgroundColor DarkGreen }
In our scenario, the script block in the Process parameter of our Foreach-Command example is executed as many times as new objects are sent through the pipeline. There are also two parameters that you may find useful – Begin and End. They are executed only once at the beginning of the processing and after the processing is completed.
These two parameters are useful if you would like to set the ground for the execution of the collection. For example, at the beginning of your script, you may want to remove old log files, create new log files, set connections, provide user-friendly messages, etc. At the end of it, you may also want to clear big variables, close connections, provide messages, etc.
Here’s how the syntax you need to use with these two parameters:
<collection> | Foreach-Object -Begin {<statement list>} -Process {<statement list>} -End {<statement list>}
In the context of our current example, let’s provide some start and end messages that include the current date and time in dark yellow color for readability:
Get-Service -Name 'WinRM', 'Spooler', 'BITS' | ForEach-Object -Begin { Write-Host Starting Foreach-Object processing -BackgroundColor DarkYellow } -Process { Write-Host Processing Service: $_.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $_.Name is with status: $_.Status and start type: $_.StartType -BackgroundColor DarkGreen } -End { Write-Host Completed Foreach-Object processing -BackgroundColor DarkYellow }
The ForEach method is another alternative you can use to process a collection of objects with PowerShell. Here’s how it works.
The ForEach method uses the following syntax:
<$collection>.ForEach ({<statement list>})
The PowerShell ForEach method processes all items in a collection until completion. Again, as with the Foreach-Object cmdlet, you are referring to the current item in the collection using the built-in variables ‘$_’ or ‘$PSItem’.
If we apply the ForEach method to our previous example, here’s how the syntax would look like:
$serviceList = Get-Service -Name 'WinRM', 'Spooler', 'BITS' $serviceList.ForEach({ Write-Host Processing Service: $_.DisplayName -BackgroundColor DarkRed Write-Host Service Name: $_.Name is with status: $_.Status and start type: $_.StartType -BackgroundColor DarkGreen })
As you can see, we get the same results as before.
In this article, I have covered three different ways to use the Foreach statement in PowerShell, including the Foreach loop, the ForEach-object cmdlet, and the ForEach method. To go further, you can check out our previous article about using the PowerShell ForEach Parallel option, as well as our general article on how to use PowerShell For Loop, While Loop, and other loops.