foreach confusion

One of the biggest sources of confusion to people learning PowerShell is foreach.

Don’t worry – it is confusing. Having just read the help file about_foreach its doubly confusing. That help file desperately needs a re-write

Foreach is used in two separate ways with two separate meanings

Firstly there is the foreach language statement or foreach loop. This is used to iterate over a collection of items

$disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" 
foreach ($disk in $disks){
  $fp = [math]::Round(($disk.FreeSpace / $disk.Size) * 100, 2)
  if ($fp -lt 60){
     Write-Warning -Message "$($disk.DeviceId) has $fp % free space" 
  }
  else {
     Write-Information -MessageData "$($disk.DeviceId) has $fp % free space" -InformationAction Continue
  }
}

In this case the Win32_LogicalDisk CIM class is used to create a collection of disk information. The foreach language statement (loop) is used to work through that collection. For each disk in the disks collection we calculate the percentage free space. If that percentage is less than 60 a warnign message is displayed otherwise an information message is displayed – for versions of PowerShell before 5.0 use Write-Host instead of Write-Information.

PLEASE DON’T COMMENT THAT YOU WOULD DO THIS ANOTHER WAY – SO WOULD I. I’M USING IT TO ILLUSTRATE FOREACH!

Alternatively you could do this

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
foreach {
  $fp = [math]::Round(($psitem.FreeSpace / $psitem.Size) * 100, 2)
  if ($fp -lt 60){
     Write-Warning -Message "$($psitem.DeviceId) has $fp % free space" 
  }
  else {
     Write-Information -MessageData "$($psitem.DeviceId) has $fp % free space" -InformationAction Continue
  }
}

Get the disk information but this time pass it into foreach on the pipeline

AGAIN PLEASE DON’T COMMENT THAT YOU WOULD DO THIS ANOTHER WAY – SO WOULD I. I’M USING IT TO ILLUSTRATE FOREACH!

This is where the confusion arises – foreach is used in two different ways.

In the second set of code – foreach is actually an alias for Foreach-object so the code could be written

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
 ForEach-Object -Process {
 $fp = [math]::Round(($psitem.FreeSpace / $psitem.Size) * 100, 2)
 if ($fp -lt 60){
 Write-Warning -Message "$($psitem.DeviceId) has $fp % free space"
 }
 else {
 Write-Information -MessageData "$($psitem.DeviceId) has $fp % free space" -InformationAction Continue
 }
 }

which makes more sense and is less confusing

Couple of tips:

Foreach in the pipeleine is an alias for foreach object and foreach on its own is the language statement

When writing code use Foreach-Object rather than foreach to reduce confusion

Advertisements
This entry was posted in Powershell. Bookmark the permalink.

3 Responses to foreach confusion

  1. cavallogolooso says:

    hallo… did you read my question on another post?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s