ForEach vs foreach with PowerShell

ForEach vs foreach with PowerShell

Q. Understand the difference between ForEach and foreach?

A. PowerShell has two different ForEach's. The first is a cmdlet named ForEach-Object that is part of the Microsoft.PowerShell.Core module. Thie cmdlet loops through the objects and performs code in the scriptblock and references the passed objects as $_. For example:

$names = @("Julie","Abby","Ben","Kevin")
$names | ForEach-Object -Process { Write-Output $_}

Note that two aliases exist for ForEach-Object; ForEach and % and you may see either. The equivalent of the above could be:

$names | ForEach -Process { Write-Output $_}

$names | ForEach { Write-Output $_}

$names | % { Write-Output $_}

The other type of foreach is a statement that once again processes each object in a collection  and performs the code in the scriptblock for each item but this time a specific name is used to reference each item in the collection. An example is:

foreach ($name in $names) { Write-Output $name}

Note that the outcome is the same, the objects in a collection are looped through one at a time. So what is the difference? Typically the foreach statement is faster since it gets compiled to a single expression where as foreach-object compiles to multiple expressions however a potential downside of the foreach statement is because it is a single expression the entire collection must be stored in memory whereas with the foreach-object data is loaded into memory as needed. Loading everything into memory first may still be faster overall but means more memory will be used and required.

You can see this in action that foreach statement loads everything into memory first by trying to recurse every file in the Windows folder. When you run the code below there is an initial delay as every item is loaded before output to screen.

foreach ($file in (Get-ChildItem C:\Windows -Recurse)) {$file.Name}

This contrasts with the same code using the ForEach-Object which starts output to screen straight away.

Get-ChildItem C:\Windows -Recurse | ForEach-Object {$_.Name}

The total time though is still faster with foreach statement (but would use more memory).

$Time1 = (Measure-Command {Get-ChildItem C:\Windows -Recurse | ForEach-Object {$_.Name}}).TotalMilliseconds
$Time2 = (Measure-Command {foreach ($file in (Get-ChildItem C:\Windows -Recurse)) {$file.Name}}).TotalMilliseconds
Write-Output "Time using ForEach-Object is $Time1"
Write-Output "Time using ForEach statement is $Time2"
Time using ForEach-Object is 16617.7766
Time using ForEach statement is 15433.7096

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish