The not so simplified syntax of Where-Object in PowerShell

PowerShell 3.0 introduced several notable improvements to its cmdlet library, with one of the most useful features in this release being the simplified syntax for the Where-Object cmdlet. This enhancement made filtering objects more efficient and user-friendly by introducing Property and Value parameters and a switch parameter for every comparison operator. This article explores how these changes work, their usefulness, and how to leverage them in your scripts.

Prerequisites

To follow along with the examples in this article, define the following array of hash tables in your PowerShell session:

1$users = @( 2 @{Name = 'Joe'; Age = 31}, 3 @{Name = 'Jane'; Age = 29}, 4 @{Name = 'Steve'; Age = 27} 5) 

Changes to Where-Object in PowerShell 3.0

Before PowerShell 3.0, the Where-Object cmdlet required you to pass a script block using the FilterScript parameter to filter objects. While functional, this approach could be cumbersome, especially for simple property-based filtering. A common example looked like this:

1$users | Where-Object {$_.Name -eq 'Joe'} 

Though this syntax worked, the reliance on script blocks made it more complex than necessary. PowerShell 3.0 introduced a significant change: switch parameters for each comparison operator. This change allows you to filter objects directly using these parameters without the need for script blocks.

To see a list of all the comparison switch parameters for Where-Object, use the following code:

1$commonParams = @( 2 [System.Management.Automation.Cmdlet]::CommonParameters 3 [System.Management.Automation.Cmdlet]::OptionalCommonParameters 4) 5 6(Get-Command -Name Where-Object).Parameters.Values | 7 Where-Object {$_.SwitchParameter -eq $true -and $_.Name -notin $commonParams} | 8 Format-Wide -Property Name -AutoSize 

The output displays 31 comparison parameters (EQ, GT, NotIn, etc.), which you might think are comparison operators. Each of these parameters exists in its own parameter set with a Property parameter that you can use for filtering. Also, notice that these parameters are in upper or Pascal case instead of lower case like the actual comparison operators. They tab expand and show up in IntelliSense this way, so it's easy to determine if you're working with a parameter or a comparison operator.

The only other parameter set is for the FilterScript parameter for a total of 32 parameter sets, which is the maximum number of parameter sets allowed for a command in PowerShell.

Syntax

With PowerShell 3.0, filtering objects became significantly more straightforward. Here's a simplified example using the new syntax:

1$users | Where-Object Name -EQ 'Joe' 

The following example is equivalent, specifying all positional parameters explicitly:

1$users | Where-Object -Property Name -EQ -Value 'Joe' 

You can also specify the parameters in any position. Notice the EQ parameter is last in the following example:

1$users | Where-Object -Property Name -Value 'Joe' -EQ 

When not to use the simplified syntax

While the simplified syntax is convenient, there are situations where you need to revert to using the FilterScript parameter:

  • Filtering on multiple properties: When you need to filter based on more than one property, you must revert to using a script block. For example:

    1$users | Where-Object {$_.Name -like 'J*' -and $_.Age -lt 30} 

    Avoid multiple piped Where-Object statements. When you need to perform multiple filters, use a single Where-Object statement with a compound condition. This approach is more efficient than piping to Where-Object multiple times.

  • Filtering on a nested property: Many objects in PowerShell contain nested objects, as shown in the following example. The simplified syntax can't access these nested properties directly.

    1Get-ChildItem -Path 'C:\Windows\system32\*.exe' | 2 Where-Object {$_.VersionInfo.FileDescription -match 'driver'} 

Summary

While PowerShell 3.0 introduced new parameters and parameter sets to the Where-Object cmdlet, it's not exactly "simplified" syntax. Instead, it improves the syntax by adding flexibility and making filtering more intuitive through the use of specific parameters for each comparison operator.

References

Acknowledgments

Thanks to Sean Wheeler for sharing his insights on this topic, which inspired this article.