PowerShell foreach File in a Folder (With Examples)

To loop through each file in a folder in PowerShell, get the files with Get-ChildItem -File and process them in a foreach loop:

foreach ($file in Get-ChildItem -Path C:\Invoices -File) {
    $file.Name
}

Each $file is a full file object with Name, Length, LastWriteTime and more. Below I use a sample Invoices folder to show the pipeline version, filtering, subfolders, counters, renaming and parallel processing.

Loop through files with foreach

-File skips folders, so the Archive subfolder doesn’t show up here. The format string lines up the names and sizes:

foreach ($file in Get-ChildItem -Path .\Invoices -File) {
    '{0,-14} {1,3} bytes' -f $file.Name, $file.Length
}

Output:

inv-1001.txt    15 bytes
inv-1002.txt    17 bytes
summary.csv     12 bytes
PowerShell foreach file in folder
Each file’s name and size (PowerShell 7)

If the folder is empty, the loop simply runs zero times, so you don’t need a separate check.

Use ForEach-Object in a pipeline

ForEach-Object processes files as they arrive, and $_ is the current file. It’s handy at the end of a longer pipeline:

Get-ChildItem -Path .\Invoices -File | ForEach-Object {
    "$($_.BaseName) was last changed $($_.LastWriteTime.ToString('yyyy'))"
}

Output:

inv-1001 was last changed 2026
inv-1002 was last changed 2026
summary was last changed 2026

The foreach statement is usually faster because it doesn’t use the pipeline. ForEach-Object uses less memory on huge folders, because it doesn’t need the whole list first.

Filter by type and include subfolders

-Filter limits the loop to one extension, and -Recurse goes into every subfolder. This reads the first line of each .txt file:

foreach ($file in Get-ChildItem -Path .\Invoices -Filter *.txt -File -Recurse) {
    $first = Get-Content -Path $file.FullName -TotalCount 1
    '{0,-12} -> {1}' -f $file.Name, $first
}

Output:

inv-1001.txt -> Austin, $249.99
inv-1002.txt -> Denver, $1,200.00
inv-0950.txt -> Dallas, $89.50

inv-0950.txt comes from the Archive subfolder. Use $file.FullName with other cmdlets so the path is always complete. For more filters, see the Get-ChildItem filter guide.

Show progress with a counter

Wrap Get-ChildItem in @() to get a count up front, then increase a counter inside the loop:

$files = @(Get-ChildItem -Path .\Invoices -File -Recurse)
$i = 0
foreach ($file in $files) {
    $i++
    "[$i of $($files.Count)] $($file.Name)"
}

Output:

[1 of 4] inv-1001.txt
[2 of 4] inv-1002.txt
[3 of 4] summary.csv
[4 of 4] inv-0950.txt

For long jobs, you can pass the same numbers to Write-Progress to show a progress bar.

Rename every file safely

Changing files in a loop is where mistakes hurt. Add -WhatIf to preview the result first:

Get-ChildItem -Path .\Invoices -Filter 'inv-*.txt' -File | ForEach-Object {
    $newName = $_.Name.ToUpper().Replace('.TXT', '-2026.txt')
    Rename-Item -Path $_.FullName -NewName $newName -WhatIf
}

Output:

What if: Performing the operation "Rename File" on target "Item: C:\psfaqs\Invoices\inv-1001.txt Destination: C:\psfaqs\Invoices\INV-1001-2026.txt".
What if: Performing the operation "Rename File" on target "Item: C:\psfaqs\Invoices\inv-1002.txt Destination: C:\psfaqs\Invoices\INV-1002-2026.txt".
PowerShell ForEach-Object Rename-Item -WhatIf
-WhatIf previews each rename (Windows PowerShell 5.1)

Only the two inv- files match. Remove -WhatIf to rename them for real. More patterns are in copying and renaming files and renaming files with a date.

Process files in parallel in PowerShell 7

ForEach-Object -Parallel runs several files at once, which speeds up slow work such as uploads or hashing. -ThrottleLimit sets how many run together:

Get-ChildItem -Path .\Invoices -File -Recurse |
    ForEach-Object -Parallel { "$($_.Name): $($_.Length) bytes" } -ThrottleLimit 4 |
    Sort-Object

Output:

inv-0950.txt: 14 bytes
inv-1001.txt: 15 bytes
inv-1002.txt: 17 bytes
summary.csv: 12 bytes
PowerShell ForEach-Object -Parallel files
Results arrive in any order, so I sort them (PowerShell 7)

-Parallel needs PowerShell 7 and runs each block in its own runspace, so outside variables need $using:. It only pays off when each file takes real time. See the ForEach-Object reference.

Frequently Asked Questions

How do I loop through files in a folder in PowerShell?

Use foreach ($file in Get-ChildItem -Path C:\Folder -File) { ... }. Inside the loop, $file is the file object.

How do I include subfolders?

Add -Recurse to Get-ChildItem: Get-ChildItem -Path C:\Folder -File -Recurse.

What is the difference between foreach and ForEach-Object?

foreach is a language statement that’s usually faster. ForEach-Object works in the pipeline and handles items one at a time.

How do I loop through only certain file types?

Use -Filter, as in Get-ChildItem -Path C:\Folder -Filter *.csv -File.

Can I process files in parallel in PowerShell?

Yes, in PowerShell 7 with ForEach-Object -Parallel. Windows PowerShell 5.1 doesn’t support it.

If you automate file tasks, these are useful too: