# For Each Thing: \`foreach\`

The foreach statement runs the steps in its braces once for each item in a list, with a variable you name holding this item, so a script renames 30 files or 200 with one loop and can decide about each one with an if.

Apprentice · PowerShell · Updated November 10, 2026

## After this lesson you can

- Repeat steps for each item in a list with the foreach statement, naming a variable for this item
- Rename or check every file in a folder with foreach, Rename-Item and an if inside the loop
- Choose between the foreach statement and ForEach-Object for a job

## Key takeaways

- foreach ($file in a list) { … } runs the steps in the braces once for each item, and $file holds this item on each pass.
- The list can be values you type, numbers such as 1..10, or the files Get-ChildItem finds.
- Rehearse with -WhatIf on a few items first, then run the loop on all of them.
- An if inside the loop decides about each item, such as keeping only the sensors hotter than 90.
- Use foreach in a script, with several steps for each item. Use ForEach-Object in a pipeline, where each item flows on to the next command.

A program that saves reports crashed in the night, and 200 finished reports sit in a folder still ending in `.tmp`, the ending many programs give a file while they're writing it. Each one has to end in `.log` before anyone can open it. By hand, that's 200 renames. *Doing Something to Each: `ForEach-Object`* did a job like this in one line at the prompt. In a script, where each file may need several steps and a decision, PowerShell has a statement that reads like a sentence: `foreach`. An IT technician uses it to fix every computer on a list, and a data analyst to clean every file in a month of exports.

The examples are set at Mission Control on launch day, where 30 telemetry files, the satellite's report from each orbit, came in with the wrong ending. These lines make the practice folder with 30 empty files. Running them again is safe: `-Force` makes any file that's already there again, empty.

```powershell
New-Item -ItemType Directory -Force -Path "$HOME\terminal-practice\mission-control\telemetry" | Out-Null
Set-Location "$HOME\terminal-practice\mission-control"
1..30 | ForEach-Object { New-Item "telemetry\orbit-$_.tmp" -Force } | Out-Null
Get-ChildItem telemetry -Name | Select-Object -First 3
```

```text
orbit-1.tmp
orbit-10.tmp
orbit-11.tmp
```

The names sort as text, so `orbit-10.tmp` comes before `orbit-2.tmp`.

## foreach: the same steps for each one

A **loop** repeats steps. Picture a flight controller going down the launch checklist: for each system on the list, ask the same question. The `foreach` statement does exactly that:

```powershell
foreach ($system in 'fuel', 'power', 'radio') {
    "Checking $system... go"
}
```

```text
Checking fuel... go
Checking power... go
Checking radio... go
```

Read it aloud: "for each system in fuel, power and radio, do what's in the braces." The parentheses hold a variable you name, `$system`, the word `in`, and the list. Each time round the loop, called a **pass**, `$system` holds the next item and the steps in the braces run once with it. Name the variable for one item: `$file`, `$song`, `$sensor`.

Typed at the prompt, PowerShell starts each new line with `>>` until the closing brace, and Enter after it runs the loop. In a script you write it on several lines, like this (*Your First Script*).

## Rename every telemetry file

Now the list is files. `Get-ChildItem telemetry\*.tmp` finds them, and each new name is the file's `BaseName`, its name without the extension, plus `.log`. Rehearse on three first with `-WhatIf`, as in *Doing Something to Each: `ForEach-Object`*:

```powershell
foreach ($file in Get-ChildItem telemetry\*.tmp | Select-Object -First 3) {
    Rename-Item $file.FullName ($file.BaseName + '.log') -WhatIf
}
```

```text
What if: Performing the operation "Rename File" on target "Item: C:\Users\Sam\terminal-practice\mission-control\telemetry\orbit-1.tmp Destination: C:\Users\Sam\terminal-practice\mission-control\telemetry\orbit-1.log".
What if: Performing the operation "Rename File" on target "Item: C:\Users\Sam\terminal-practice\mission-control\telemetry\orbit-10.tmp Destination: C:\Users\Sam\terminal-practice\mission-control\telemetry\orbit-10.log".
What if: Performing the operation "Rename File" on target "Item: C:\Users\Sam\terminal-practice\mission-control\telemetry\orbit-11.tmp Destination: C:\Users\Sam\terminal-practice\mission-control\telemetry\orbit-11.log".
```

`$file.FullName` is the file's whole path, so `Rename-Item` finds it wherever you run the loop from. The parentheses around `($file.BaseName + '.log')` make PowerShell work out the new name before handing it over. The Destinations look right, so run it on all of them:

```powershell
foreach ($file in Get-ChildItem telemetry\*.tmp) {
    Rename-Item $file.FullName ($file.BaseName + '.log')
}
Get-ChildItem telemetry -Name | Select-Object -First 3
(Get-ChildItem telemetry\*.log).Count
```

```text
orbit-1.log
orbit-10.log
orbit-11.log
30
```

Thirty files or 200, it's the same three lines. Run the loop a second time and nothing happens, with no error: no file matches `*.tmp` any more, so the loop has no passes to make.

## A decision on every pass

An `if` inside the loop (*If This, Then That: `if`, `elseif` and `else`*) decides about each item. Mission Control needs to know which sensors are running hot. Each sensor writes one line, such as `temp=97`. Make four:

```powershell
New-Item -ItemType Directory -Force -Path sensors | Out-Null
Set-Content sensors\sensor-01.txt 'temp=72'
Set-Content sensors\sensor-02.txt 'temp=97'
Set-Content sensors\sensor-03.txt 'temp=90'
Set-Content sensors\sensor-04.txt 'temp=101'
```

For each sensor, read its line, take off `temp=` with the `.Replace()` method (*Properties and Methods*), turn what's left into a number with `[int]` (*Numbers, Dates and Types*), and output the sensor's name if it's over 90:

```powershell
foreach ($sensor in Get-ChildItem sensors\*.txt) {
    $temp = [int](Get-Content $sensor.FullName).Replace('temp=', '')
    if ($temp -gt 90) {
        $sensor.BaseName
    }
}
```

```text
sensor-02
sensor-04
```

`sensor-03` is exactly 90, which isn't more than 90. Leave out `[int]` and `sensor-04` goes missing: compared as text, `'101'` comes before `'90'`, the way words sort in a dictionary. Save these lines as `check.ps1` and you have a small application: a sensor check you can run every morning.

## foreach or ForEach-Object?

Both repeat steps for each item, and you'll use both. Choose by where the work happens:

| | The `foreach` statement | `ForEach-Object` |
|---|---|---|
| Fits best | a script, several steps for each item | a pipeline at the prompt |
| This item | a name you choose, such as `$file` | `$_` |
| How it works | gets the whole list, then goes round | works on each item as it arrives |
| Its output | can't be piped straight on | flows on to the next command |

That last row catches people. A pipe can't follow a foreach loop's closing brace:

```powershell
foreach ($file in Get-ChildItem sensors) { $file.Name } | Sort-Object
```

```text
ParserError:
Line |
   1 |  foreach ($file in Get-ChildItem sensors) { $file.Name } | Sort-Object
     |                                                          ~
     | An empty pipe element is not allowed.
```

Save the loop's output in a variable first, `$names = foreach (…) { … }`, and pipe `$names`, or use ForEach-Object. One more trap: `foreach` is also a short name for ForEach-Object. After a pipe, `| foreach { … }` is ForEach-Object; at the start of a line, `foreach (…)` is the statement.

Windows PowerShell 5.1 runs every loop in this lesson the same way. On a Mac or Linux, paths use `/`, and `*.tmp` doesn't match `ORBIT-1.TMP`: capital letters count in file names there.

## Try it in the terminal

[Terminal Quest: Mission Control, goals 3 to 4](https://www.ask-codey.com/games/terminal-quest/mission-control/powershell/step): played right on the lesson page.

Your turn at Mission Control: rename all thirty telemetry files from `.tmp` to `.log` with one loop, then write `check.ps1`, a loop with an `if` that outputs every sensor hotter than 90, and send its output to the uplink. The game's hint reads the number with `-replace 'temp=', ''`, an operator that makes the same swap as `.Replace()`. One telemetry file hides from the wildcard: can you find it?

## Check yourself

2 questions to try on the lesson page.

## So what

`foreach` turns "do this to every one of them" into a few readable lines in a script, and with an `if` inside, a script can decide about each file, sensor or song on its own.

## Key terms

- [foreach Loop](https://www.ask-codey.com/glossary/foreach-loop-powershell)
- [Loop](https://www.ask-codey.com/glossary/loop-software-development)
- [ForEach-Object](https://www.ask-codey.com/glossary/foreach-object)
- [Rename-Item](https://www.ask-codey.com/glossary/rename-item)

## Cheat sheets

- [For Each Thing: \`foreach\`](https://www.ask-codey.com/cheat-sheets/powershell-foreach-loop-cheat-sheet)

## Related

- [Doing Something to Each: ForEach-Object](https://www.ask-codey.com/lessons/powershell-foreach-object)
- [If This, Then That: \`if\`, \`elseif\` and \`else\`](https://www.ask-codey.com/lessons/powershell-if-statements)
- [Until Something Is True: \`while\`, \`do\` and \`for\`](https://www.ask-codey.com/lessons/powershell-while-and-for)
- [Stopping Early: \`break\` and \`continue\`](https://www.ask-codey.com/lessons/powershell-loop-control)

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