≡ Menu

Difference between foreach loop and Foreach-Object in PowerShell

I used foreach loop statement and foreach-Object via piping many times but never cared to check what is the difference between them unless I noticed a bug in one of my scripts which impacted the script flow.

By referring foreach loop statement and Foreach-Object cmdlets throughout my post here, I mean the below.

Foreach loop statement

foreach($i in 1..10) {            
    Write-Host $i            
}

Foreach-Object

"a","b","c","d" | Foreach-Object {            
    Write-host $_            
}

Before jumping on to bug I noticed in my code, I just want to brief what is the difference between Foreach loop statement and Foreach-Object that we generally use via pipe line. If you look at the aliases for Foreach-Object cmdlets, you will see both foreach and %. That means foreach is nothing but an alias to Foreach-Object.

But wait, there is a difference depending on the place you use. If you use foreach after a pipe line, then it is treated as alias for foreach-object cmdlets.

When you use foreach as a looping statement, the shell recognizes it as a loop control like the way we have in any other programing language.

Difference#1

Now you might ask, what is the exact difference?. I would first start with the reasons Bruce Payette quoted in PowerShell in Action book. The Foreach-Object is more efficient in terms of memory utilization but lacks the performance and the foreach loop control is more efficient in terms of performance but might utilize more memory depending on the objects you are looping through. You can easily understand this because, the Foreach-Object is used via Pipe line that means one object is passed to this at a time so there is no need to store anything when you are processing huge data. When it comes to foreach loop control statement, data should be in one variable which you will loop through one at a time so requires some space in memory.

Difference#2

Ok, now let me come to the difference I noticed. The Foreach-Object doesn’t support branching statements like Break and Continue but the foreach loop statement does.

Look at the below example. There it should print all combinations of 1 : a, 1: b, 1:d and similarly for other numbers. But you will notice that after 1:b it is proceeding to 2:a without processing 1:d. That means continue statement inside a Foreach-Object will reach the foreach loop statement which is above the foreach-Object cmdlets.

foreach($i in 1..3) {             
    "a","b","c","d" | foreach-object {             
        if($_ -eq "c") {             
        continue             
        }            
    write-host "$i : $_"             
    }             
}

Conclusion

So, my conclusion is, MS should have avoided this ambiguity from beginning by naming foreach-Object cmdlets as Each-Object or something similar which doesn’t collide with foreach loop statement. Where there is a need for looping, I would suggest using foreach loop statement instead of Foreach-Object cmdlets because that is not actually loop but rather a cmdlets which processes one object at a time that is passed via pipeline.

Hope my understanding is clear here and if anyone thinks that my assumptions are wrong or not correctly interpreted, please feel free to comment. I am open for learning.