alt text

Have you ever hit a surprising result in PowerShell where functions inside a loop don’t behave like you expect? Let’s dig into this classic gotcha.

Here’s the snippet:

$funcs = @()

foreach ($i in 1..3) {
    $funcs += { $i }
}

Write-Host (($funcs | ForEach-Object { &$_ }) -join ', ')

The quiz gives four possible outcomes:

A. 0, 1, 2  
B. 1, 2, 3  
C. 3, 3, 3  
D. Error  

🔍 Step-by-Step Breakdown

Step 1: Initializing the array

$funcs = @()

We’re setting up an empty array to hold script blocks (PowerShell’s anonymous functions).


Step 2: The loop

foreach ($i in 1..3) {
    $funcs += { $i }
}

This adds three script blocks to the array. Each of them references the variable $i.

But here’s the catch: PowerShell closures capture the variable, not its current value. By the time the loop finishes, $i equals 3. So all three script blocks return 3 when invoked.


Step 3: Executing the script blocks

$funcs | ForEach-Object { &$_ }

We run each script block. Since they all reference the same $i, they all return 3.


Step 4: Joining the output

-join ', '

Result:

3, 3, 3

✅ Correct Answer: C. 3, 3, 3


🛠 The Fix: Capture the value, not the reference

PowerShell gives you a built-in way to capture the current value of a variable into a closure:

foreach ($i in 1..3) {
    $funcs += { $i }.GetNewClosure()
}

Now each script block holds onto its own snapshot of $i.

So this:

Write-Host (($funcs | ForEach-Object { &$_ }) -join ', ')

will output:

1, 2, 3

🧠 Takeaway

Closures are powerful, but they can surprise you. Remember:

In PowerShell, { $i } captures the variable — not the value. Use .GetNewClosure() to freeze the value at that moment.

Got more PowerShell puzzles? Drop them in the comments or join the NYC PowerShell Meetup!