PowerShell -in, -notin and -contains Operators (With Examples)

The PowerShell -in operator checks whether a value is in a collection, and -contains runs the same check with the collection written first. Both return True or False:

'Denver' -in 'Austin', 'Denver', 'Seattle'          # True
'Austin', 'Denver', 'Seattle' -contains 'Denver'    # True

They compare whole items, never parts of text, and that trips up a lot of scripts. I ran each example below in PowerShell 7 and Windows PowerShell 5.1.

How the -in and -notin operators work

Put the single value on the left and the list on the right. -notin returns the opposite answer, and adding a c (-cin, -cnotin) makes the match case-sensitive:

$cities = 'Austin', 'Denver', 'Seattle'

"'Denver' -in:     $('Denver' -in $cities)"
"'denver' -in:     $('denver' -in $cities)"
"'denver' -cin:    $('denver' -cin $cities)"
"'Boston' -notin:  $('Boston' -notin $cities)"
"'denver' -cnotin: $('denver' -cnotin $cities)"

Output:

'Denver' -in:     True
'denver' -in:     True
'denver' -cin:    False
'Boston' -notin:  True
'denver' -cnotin: True
PowerShell -in, -notin, -cin and -cnotin operator examples
-in ignores case, -cin doesn’t (PowerShell 7)

Microsoft added -in and -notin in PowerShell 3.0, so they work the same in 5.1 and 7. They sit next to the other PowerShell comparison operators but always return a Boolean.

-in vs -contains: what’s the difference?

There’s no difference in the result, only in the order. With -contains, the collection goes on the left and the value on the right:

$cities = 'Austin', 'Denver', 'Seattle'

"-contains 'Denver':     $($cities -contains 'Denver')"
"-contains 'denver':     $($cities -contains 'denver')"
"-ccontains 'denver':    $($cities -ccontains 'denver')"
"-notcontains 'Boston':  $($cities -notcontains 'Boston')"

Output:

-contains 'Denver':     True
-contains 'denver':     True
-ccontains 'denver':    False
-notcontains 'Boston':  True

Both operators stop at the first match, so they’re quick even on long lists. Pick the one that reads naturally; the guide on checking if an array contains a value has more array checks.

Why does -in return False when the array is on the wrong side?

PowerShell doesn’t throw an error when you swap the operands. It turns the misplaced array into one string, joined with spaces, and compares that instead:

$cities = 'Austin', 'Denver', 'Seattle'

"'Austin' -in `$cities:       $('Austin' -in $cities)"
"`$cities -in 'Austin':       $($cities -in 'Austin')"
"'Austin' -contains `$cities: $('Austin' -contains $cities)"
"`$cities -in 'Austin Denver Seattle': $($cities -in 'Austin Denver Seattle')"

Output:

'Austin' -in $cities:       True
$cities -in 'Austin':       False
'Austin' -contains $cities: False
$cities -in 'Austin Denver Seattle': True

The last line proves it: the three cities became ‘Austin Denver Seattle’. If a check returns False and you’re sure the value is there, look at the operand order first.

That’s the same join you get when you convert an array to a string in a double-quoted string.

Filter objects with -in and -notin in Where-Object

-in shines in filters. Where-Object has an -In parameter, so you can keep the people whose City is one of your offices without a script block:

$people = @(
    [pscustomobject]@{ Name = 'Maria Lopez'; City = 'Austin' }
    [pscustomobject]@{ Name = 'Tom Baker';   City = 'Boston' }
    [pscustomobject]@{ Name = 'Dana Reyes';  City = 'Denver' }
)
$offices = 'Austin', 'Denver'

$people | Where-Object City -In $offices | Select-Object -ExpandProperty Name

Output:

Maria Lopez
Dana Reyes

For the reverse, -notin inside a script block drops every server that’s in the maintenance list. Here it is in Windows PowerShell 5.1:

$servers     = 'WEB01', 'WEB02', 'DB01', 'APP01'
$maintenance = 'WEB01', 'DB01'

$servers | Where-Object { $_ -notin $maintenance }

Output:

WEB02
APP01
PowerShell -notin with Where-Object excluding servers in Windows PowerShell 5.1
-notin removes the servers on the maintenance list (Windows PowerShell 5.1)

Both forms give the same result in 7 and 5.1. Filtering with Where-Object covers the full syntax, and Where-Object contains handles partial text.

Check if several values are in an array

-in and -contains take one value at a time. Pass two and PowerShell joins them into one string, so the answer is False. Check each value with Where-Object instead:

$cities = 'Austin', 'Denver', 'Seattle'
$wanted = 'Austin', 'Boston'

"Both at once with -in: $($wanted -in $cities)"
$missing = @($wanted | Where-Object { $_ -notin $cities })
"Missing:     $($missing -join ', ')"
"All present: $($missing.Count -eq 0)"
"Any present: $(@($wanted | Where-Object { $_ -in $cities }).Count -gt 0)"

Output:

Both at once with -in: False
Missing:     Boston
All present: False
Any present: True

Missing lists the values that aren’t there, which also answers “are all of them present?” To match two full lists, compare two arrays for matches with Compare-Object.

Why does -contains return False for a substring?

-