PowerShell ForEach-Object: Examples and foreach vs ForEach-Object

ForEach-Object runs a script block once for every object that comes down the pipeline. Inside the block, $_ is the current object:

'Austin', 'Denver', 'Seattle' | ForEach-Object {
    "Sending the weekly report to the $_ office"
}

Output:

Sending the weekly report to the Austin office
Sending the weekly report to the Denver office
Sending the weekly report to the Seattle office

$PSItem is another name for $_, if you find it easier to read. I ran every example in PowerShell 7.6 and Windows PowerShell 5.1, and I note where they differ.

Work with each file, user or row

ForEach-Object is most useful at the end of a pipeline, where each object has properties you can use:

Get-ChildItem -Path C:\psfaqs\Reports -File | ForEach-Object {
    '{0,-18} {1,6:N1} KB' -f $_.Name, ($_.Length / 1KB)
}

Output:

budget-2026.xlsx      5.0 KB
notes.txt             0.3 KB
q3-sales.csv          2.0 KB

The same pattern works for rows from Import-Csv, services, users or anything else a cmdlet returns.

Run code before and after the loop with -Begin and -End

-Begin runs once before the first object, -Process runs for each object, and -End runs once at the end. That’s handy for totals:

Get-ChildItem -Path C:\psfaqs\Reports -File | ForEach-Object -Begin {
    $total = 0
    'Checking files...'
} -Process {
    $total += $_.Length
} -End {
    "Total size: $([int]($total / 1KB)) KB"
}

Output:

Checking files...
Total size: 7 KB
PowerShell ForEach-Object with -Begin, -Process and -End blocks
Adding up file sizes with -Begin, -Process and -End (PowerShell 7)

Shortcut: call a property or method on every object

For a single property or method, skip the script block and pass its name:

'austin', 'denver' | ForEach-Object -MemberName ToUpper

Get-ChildItem -Path C:\psfaqs\Reports -File | ForEach-Object -MemberName Name

Output:

AUSTIN
DENVER
budget-2026.xlsx
notes.txt
q3-sales.csv

This is the same as ForEach-Object { $_.ToUpper() }, just shorter.

Skip an item: use return, not continue

Inside ForEach-Object, return moves on to the next object:

1..5 | ForEach-Object {
    if ($_ -eq 3) { return }
    "Processing order 100$_"
}
'Done'

Output:

Processing order 1001
Processing order 1002
Processing order 1004
Processing order 1005
Done

continue and break don’t work the way they do in a loop here. They stop the whole script, which the Import-Csv guide shows step by step.

ForEach-Object vs the foreach loop

PowerShell also has a foreach statement. It looks similar, but it works on a list that’s already in memory instead of the pipeline:

# ForEach-Object: works in the pipeline, one object at a time
Get-ChildItem -Path C:\Reports | ForEach-Object { $_.Name }

# foreach statement: loops over a list you already have
$files = Get-ChildItem -Path C:\Reports
foreach ($file in $files) { $file.Name }

To compare speed, I doubled 200,000 numbers three ways:

$numbers = 1..200000

$tests = [ordered]@{
    'ForEach-Object' = { $numbers | ForEach-Object { $_ * 2 } }
    'foreach loop'   = { foreach ($n in $numbers) { $n * 2 } }
    '.ForEach()'     = { $numbers.ForEach({ $_ * 2 }) }
}

foreach ($name in $tests.Keys) {
    '{0,-15} {1,6:N0} ms' -f $name, (Measure-Command { $null = & $tests[$name] }).TotalMilliseconds
}

Output in PowerShell 7 (your times will differ):

ForEach-Object     863 ms
foreach loop        16 ms
.ForEach()         363 ms

Output in Windows PowerShell 5.1:

ForEach-Object     873 ms
foreach loop        21 ms
.ForEach()         567 ms
PowerShell ForEach-Object vs foreach loop vs ForEach method speed
The foreach loop is the fastest (Windows PowerShell 5.1)
ForEach-Objectforeach statement
Speed on large listsSlowestFastest
MemoryOne object at a timeNeeds the whole list