# Doing Something to Each: ForEach-Object

ForEach-Object runs a script block once for every object the pipeline hands it, with $\_ as this item, so one line can rename 200 photos, and -WhatIf goes inside the braces, on the command that makes the change, so you can rehearse first.

Apprentice · PowerShell · Operating System · Updated November 10, 2026

## After this lesson you can

- Run a command for each object with ForEach-Object, using $\_ for this item
- Rehearse a change to many files with -WhatIf inside ForEach-Object's braces before running it
- Choose Where-Object, Sort-Object, Measure-Object, Group-Object or ForEach-Object for a job

## Key takeaways

- `ForEach-Object { … }` runs the code in the braces once for each object that arrives. Inside, `$_` is this item.
- One line renames 200 files: `ForEach-Object { Rename-Item $_ $_.Name.Replace('IMG_', 'beach-day-') }`.
- Put -WhatIf inside the braces, on the command that changes things. Rehearse on a few with Select-Object -First 3, then run it on all.
- Commands such as Remove-Item take the pipe themselves. ForEach-Object is for when each item needs its own work, like a new name made from its old one.

Your camera named every photo from the beach trip `IMG_1.jpg`, `IMG_2.jpg`, all the way to `IMG_200.jpg`, and you'd like them called `beach-day-1.jpg` and so on, so you can find them next year. Renaming them by hand is 200 rounds of click, F2, type, Enter. File Explorer can rename a whole selection, but only to its own pattern, `beach-day (1).jpg`. A photographer renaming a shoot, an office assistant renaming a month of scans and a web developer renaming images for a site all have this job, and PowerShell does it in one line.

## The same steps for each one: ForEach-Object

A teacher grading a stack of tests does the same steps to each one: take the top test, mark it, put it on the done pile, take the next. `ForEach-Object` works through a pipeline's objects the same way. It runs the script block in its braces once for each object that arrives, and inside, `$_` is this item, as in *Filtering: Where-Object and Comparisons*:

```powershell
1..3 | ForEach-Object { "Photo number $_" }
```

```text
Photo number 1
Photo number 2
Photo number 3
```

`1..3` (read "one to three") hands on the numbers 1, 2 and 3. Inside double quotes, `$_` is replaced by the item, so each pass makes a different line.

The same trick makes 200 practice photos. These lines make a `camera` folder, go into it, and make `IMG_1.jpg` to `IMG_200.jpg`, all empty. Running them again is safe: `-Force` makes any photo that's already there again, empty:

```powershell
New-Item -ItemType Directory -Force -Path "$HOME\terminal-practice\camera" | Out-Null
Set-Location "$HOME\terminal-practice\camera"
1..200 | ForEach-Object { New-Item "IMG_$_.jpg" -Force } | Out-Null
Get-ChildItem -Name | Select-Object -First 4
```

```text
IMG_1.jpg
IMG_10.jpg
IMG_100.jpg
IMG_101.jpg
```

The names sort as text, so `IMG_10.jpg` comes before `IMG_2.jpg`, as in *Sorting: Sort-Object*.

## Rehearse on three, then rename all 200

Each photo's new name is made from its old one. `.Replace('IMG_', 'beach-day-')` is a method, from *Properties and Methods*: it gives back the name with one piece of text swapped for another. `Rename-Item` takes the old name, then the new one, as in *Renaming: Rename-Item*. Rehearse on the first three with `-WhatIf`:

```powershell
Get-ChildItem IMG_*.jpg | Select-Object -First 3 | ForEach-Object { Rename-Item $_ $_.Name.Replace('IMG_', 'beach-day-') -WhatIf }
```

```text
What if: Performing the operation "Rename File" on target "Item: C:\Users\Sam\terminal-practice\camera\IMG_1.jpg Destination: C:\Users\Sam\terminal-practice\camera\beach-day-1.jpg".
What if: Performing the operation "Rename File" on target "Item: C:\Users\Sam\terminal-practice\camera\IMG_10.jpg Destination: C:\Users\Sam\terminal-practice\camera\beach-day-10.jpg".
What if: Performing the operation "Rename File" on target "Item: C:\Users\Sam\terminal-practice\camera\IMG_100.jpg Destination: C:\Users\Sam\terminal-practice\camera\beach-day-100.jpg".
```

Read it aloud: "for each photo, rename this item to its name with IMG_ replaced by beach-day-, what if." The Destinations look right, so take out `Select-Object -First 3` and `-WhatIf` and run it for real:

```powershell
Get-ChildItem IMG_*.jpg | ForEach-Object { Rename-Item $_ $_.Name.Replace('IMG_', 'beach-day-') }
Get-ChildItem -Name | Select-Object -First 4
```

```text
beach-day-1.jpg
beach-day-10.jpg
beach-day-100.jpg
beach-day-101.jpg
```

Two hundred photos, renamed. The rename prints nothing; the second line shows the first four new names.

`-WhatIf` goes *inside* the braces, on Rename-Item, because Rename-Item is what changes things. Put it at the end, outside, and nothing runs at all:

```powershell
Get-ChildItem IMG_*.jpg | ForEach-Object { Rename-Item $_ $_.Name.Replace('IMG_', 'beach-day-') } -WhatIf
```

```text
ForEach-Object: The -WhatIf and -Confirm parameters are not supported for script blocks.
```

## When you don't need ForEach-Object

Many commands take the pipe themselves, as *The Pipeline: |* showed: `Get-ChildItem *.tmp | Remove-Item` removes each file with no ForEach-Object at all. Reach for ForEach-Object when each item needs its own work, like a new name made from its old one. With only a property's name, `ForEach-Object Name` hands on that one fact of each object, the way `Select-Object -ExpandProperty Name` does.

The short names are `foreach` and `%`, and you'll see `%` in other people's commands. Windows PowerShell 5.1 runs this lesson the same way. On a Mac or Linux, `IMG_*.jpg` doesn't match `img_1.jpg`: capital letters count in file names there.

## Which command for the job?

This chapter's commands each do one thing to the objects that arrive. Pick by what you want to happen to them:

| You want to… | Use | Example |
|---|---|---|
| keep only some | `Where-Object` | `Where-Object Length -gt 1MB` |
| put them in order | `Sort-Object` | `Sort-Object Length -Descending` |
| count or add them up | `Measure-Object` | `Measure-Object Length -Sum` |
| sort them into piles | `Group-Object` | `Group-Object Extension` |
| do something to each | `ForEach-Object` | `ForEach-Object { Rename-Item … }` |
| take a few, or a few columns | `Select-Object` | `Select-Object -First 10 Name, Length` |

**Try it.** Rehearse renaming every `beach-day-` photo to start with `summer-` instead, with `-WhatIf` inside the braces. Then do it for real.

## Check yourself

2 questions to try on the lesson page.

## So what

ForEach-Object turns "do this to every one of them" into one line. Rehearse inside the braces on a few, read the Destinations, and then let it do the 200.

## Key terms

- [ForEach-Object](https://www.ask-codey.com/glossary/foreach-object)
- [Script Block](https://www.ask-codey.com/glossary/script-block)
- [$\_ and $PSItem](https://www.ask-codey.com/glossary/and-psitem)
- [-WhatIf and -Confirm](https://www.ask-codey.com/glossary/whatif-and-confirm)

## Cheat sheets

- [Doing Something to Each: ForEach-Object](https://www.ask-codey.com/cheat-sheets/powershell-foreach-object-cheat-sheet)

## Related

- [Filtering: Where-Object and Comparisons](https://www.ask-codey.com/lessons/powershell-where-object)
- [Renaming: Rename-Item](https://www.ask-codey.com/lessons/powershell-rename-item)
- [Properties and Methods](https://www.ask-codey.com/lessons/powershell-properties-and-methods)
- [Columns You Make Yourself: Calculated Properties](https://www.ask-codey.com/lessons/powershell-calculated-properties)

Source: https://www.ask-codey.com/lessons/powershell-foreach-object
