For Each
How to do something to every item in a collection of things. This is where the automation really kicks in.
Basic Usage
$trees = @(
@{name="Alder";family="Betulaceae"},
@{name="Birch";family="Betulaceae"},
@{name="Beech";family="Fagaceae"},
@{name="Walnut";family="Juglandaceae"},
@{name="Ash";family="Oleaceae"}
)
foreach ($tree in $trees) {
$tree.name + " is in the " + $tree.family + " family."
}
Structure of foreach
The foreach function is great for looping through items, it follows this beautifully illustrated structure:
So in the above example: First Loop: $item
is $something[0]
, do something with value x
, if [0]
looks new to you refer to Indexing Second Loop: $item
is $something[1]
, do something with value y
. Third Loop: $item
is $something[2]
, do something with value z
. …
This is great in scripts and can be used for almost anything, sometimes you might want to end the loop early:
Continue, Break
For Each has a couple key words that it can use to dictate whether it can keep looping or stop early.
continue
means go to the next loop please, for example:
foreach ($num in 1,2,3,4,5) {
if ($num -eq 2) { continue }
$num
}
This skips the number 2, because we told it to continue
(to the next loop) before it could get to the $num
call that would display the number.
break
means cut the loops off completely, if a foreach runs into a break
word is will not run any further loops at all.
foreach ($num in 1,2,3,4,5,6,7) {
if ($num -eq 5) { break }
$num
}
Again, the loop did not display 5
as an output because the break
clause was reached before the $num
call to print the number.
ForEach-Object
Foreach works great for writing scripts, its great strength is readability as you can set the iterator to a sensible name like $item
or $tree
. However, we often just want to use foreach on the fly, sometimes even on the same line.
For this purpose we use ForEach-Object
which is used in the pipeline (below we use shorthand ForEach
):
get-service -DisplayName *update* | ForEach-Object {'Service "{0}" is {1}' -f $_.DisplayName, $_.Status}
In one line, we can perform an action for each object generated from the left side of the |
. The only thing that differs, is having to use the $_
variable.
The shorthand for the shorthand
Even further than the above, we can completely avoid using the foreach keyword and replace it with %
.
Here’s the same command as above but with the shortest foreach declaration:
get-service -DisplayName *update* | % {'Service "{0}" is {1}' -f $_.DisplayName, $_.Status}
The foreach method
A similar approach to the .where()
method mentioned in Where there is a .foreach()
method following the same structure.
(Get-Service r*).foreach({'{0}--{1}' -f $_.name, $_.status})
This is still very useful, but can reduce readability when writing very long lines of code. The |
pipeline is one of the core functions of PowerShell and has more troubleshooting resources online, which is why it is often preferred.