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 first
Works in a pipelineYesNo
Skip an itemreturncontinue
Parallel option-Parallel (PowerShell 7)No

I use ForEach-Object in pipelines and for huge inputs I don’t want in memory, and foreach for loops over data I already have. Microsoft describes the statement in about_Foreach.

One confusing detail: foreach is also an alias for ForEach-Object. After a pipe, like 1..3 | foreach { $_ }, it’s the cmdlet.

Run items in parallel with -Parallel (PowerShell 7)

When each item waits on something, like a network call or a remote computer, -Parallel runs several at once. Here five one-second tasks run sequentially, then in parallel:

$sequential = Measure-Command {
    1..5 | ForEach-Object { Start-Sleep -Seconds 1 }
}
$parallel = Measure-Command {
    1..5 | ForEach-Object -Parallel { Start-Sleep -Seconds 1 } -ThrottleLimit 5
}

'One at a time: {0:N1} seconds' -f $sequential.TotalSeconds
'In parallel:   {0:N1} seconds' -f $parallel.TotalSeconds

Output in PowerShell 7:

One at a time: 5.1 seconds
In parallel:   1.2 seconds
PowerShell ForEach-Object -Parallel runs five tasks at once
Five one-second tasks in about one second (PowerShell 7)

-ThrottleLimit sets how many run at the same time, and the default is 5. -Parallel doesn’t exist in Windows PowerShell 5.1, where the command fails with “Parameter set cannot be resolved.”

Use outside variables with $using:

Each parallel block runs in its own runspace, so it can’t see your variables. Prefix them with $using::

$prefix = 'SRV'

1..3 | ForEach-Object -Parallel {
    "$using:prefix-0$_ checked"
} | Sort-Object

Output in PowerShell 7:

SRV-01 checked
SRV-02 checked
SRV-03 checked

Parallel blocks can finish in any order, which is why I sorted the output.

For quick work, -Parallel is much slower: doubling 200 numbers took 20 ms normally and about 2.5 seconds with -Parallel on my PC, because each runspace takes time to start. The ForEach-Object documentation covers the details.

Frequently Asked Questions

What does ForEach-Object do in PowerShell?

It runs a script block for each object in the pipeline. Inside the block, $_ (or $PSItem) is the current object.

What is the difference between ForEach-Object and foreach?

ForEach-Object works in the pipeline, one object at a time. The foreach statement loops over a list in memory and was many times faster in my test.

How do I skip an item in ForEach-Object?

Use return. continue inside ForEach-Object stops the whole script instead of skipping one item.

Does ForEach-Object -Parallel work in PowerShell 5.1?

No. -Parallel was added in PowerShell 7. In 5.1, use background jobs with Start-Job, or run the items one at a time.

How do I use a variable inside ForEach-Object -Parallel?

Prefix it with $using:, like $using:prefix. Parallel blocks can’t see variables from the outer script otherwise.

Pipelines get more useful with these: