PowerShell Where-Object filters objects in the pipeline. You give it a condition in a script block, and it passes on only the objects where the condition is true. Here it keeps orders over $900:
$orders = @(
[pscustomobject]@{ Id = 1001; Customer = 'Alice Johnson'; State = 'TX'; Status = 'Shipped'; Total = 1250 }
[pscustomobject]@{ Id = 1002; Customer = 'Bob Smith'; State = 'CO'; Status = 'Pending'; Total = 980 }
[pscustomobject]@{ Id = 1003; Customer = 'Carol Davis'; State = 'WA'; Status = 'Shipped'; Total = 15000 }
[pscustomobject]@{ Id = 1004; Customer = 'Dan Miller'; State = 'FL'; Status = 'Cancelled'; Total = 425 }
[pscustomobject]@{ Id = 1005; Customer = 'Alice Johnson'; State = 'TX'; Status = 'Pending'; Total = 310 }
)
$orders | Where-Object { $_.Total -gt 900 } | Select-Object -Property Id, Customer, Total
Output:
Id Customer Total
-- -------- -----
1001 Alice Johnson 1250
1002 Bob Smith 980
1003 Carol Davis 15000

$_ is the object being checked. The other examples in this guide use the same five sample orders in $orders, so run the $orders = @(...) part of the script above once in your session.
I ran each example in PowerShell 7.6 and Windows PowerShell 5.1. Apart from the timings, the only difference was one count, which I show below.
Script block vs simplified syntax
For a single condition, you can skip the braces and $_. This is the simplified syntax:
$orders | Where-Object Status -EQ 'Pending' | Select-Object -Property Id, Customer, Status
Output:
Id Customer Status
-- -------- ------
1002 Bob Smith Pending
1005 Alice Johnson Pending
It’s shorter, but it only takes one condition. For -and, -or or any calculation, use the script block. The Where-Object documentation lists every operator parameter.
For more than one condition, you’ll need the script block form, as shown in Where-Object with multiple conditions.
Common Where-Object operators
| Operator | Keeps objects where | Example |
|---|---|---|
-eq / -ne | The value is equal / not equal | $_.Status -ne 'Cancelled' |
-gt / -lt | The value is greater / less than | $_.Total -gt 900 |
-like / -notlike | The value matches a wildcard | $_.Customer -like 'A*' |
-match | The value matches a regex | $_.Customer -match 'son$' |
-in / -notin | The value is in a list | $_.State -in 'TX', 'FL' |
Where-Object not equal
To exclude values, use the “not” version of each operator:
"Not cancelled: $(($orders | Where-Object { $_.Status -ne 'Cancelled' }).Id -join ', ')"
"Not like 'A*': $(($orders | Where-Object { $_.Customer -notlike 'A*' }).Id -join ', ')"
"Not in TX or FL: $(($orders | Where-Object { $_.State -notin 'TX', 'FL' }).Id -join ', ')"
Output:
Not cancelled: 1001, 1002, 1003, 1005
Not like 'A*': 1002, 1003, 1004
Not in TX or FL: 1002, 1003
Case-sensitive matching
Text comparisons ignore case by default. Add a c to the operator, like -ceq, when case matters:
"-eq 'shipped': $(@($orders | Where-Object { $_.Status -eq 'shipped' }).Count) orders"
"-ceq 'shipped': $(@($orders | Where-Object { $_.Status -ceq 'shipped' }).Count) orders"
Output:
-eq 'shipped': 2 orders
-ceq 'shipped': 0 orders
Filter on multiple conditions
Combine conditions with -and and -or inside one script block:
$orders | Where-Object { $_.State -eq 'TX' -and $_.Status -ne 'Cancelled' } | Select-Object -Property Id, Customer, Status
Output:
Id Customer Status
-- -------- ------
1001 Alice Johnson Shipped
1005 Alice Johnson Pending