2

I have never got the -contains operator to work in Powershell I don't know why.

Here's an example of where it isn't working. I use -like in its place but I'd love it if you could tell me why this isn't working.

PS HKLM:\Software\Microsoft\Windows NT\CurrentVersion> (gp . P*).ProductName
Windows 10 Enterprise

PS HKLM:\Software\Microsoft\Windows NT\CurrentVersion> (gp . P*).ProductName -contains "Windows"
False

PS HKLM:\Software\Microsoft\Windows NT\CurrentVersion> (gp . P*).ProductName | gm | select TypeName | Get-Unique

TypeName     
--------     
System.String
Water Cooler v2
  • 32,724
  • 54
  • 166
  • 336
  • you're looking for `-match 'Windows'` or `-like '*Windows*'`, contains is for arrays only. – colsw Sep 13 '17 at 10:29
  • The `-match` operator, as far as I understand, is for working with regular expressions, which include the smaller subset of wildcards, so that will work as well. But if I wanted to just use the `contains` operator properly, how would I do that? My question is not *how do I do this?* but rather *why does this never work for me and what am I doing wrong with this operator here?* There are a 100 ways to do the thing. I could just call `"The string Value".Contains()` from the .NET Base Class Library. – Water Cooler v2 Sep 13 '17 at 10:36
  • Possible duplicate of [PowerShell and the -contains operator](https://stackoverflow.com/questions/18877580/powershell-and-the-contains-operator) – Mark Wragg Sep 13 '17 at 10:39

2 Answers2

12

The -contains operator is not a string operator, but a collection containment operator:

'a','b','c' -contains 'b' # correct use of -contains against collection

From the about_Comparison_Operators help topic:

Type         Operator      Description
Containment  -contains     Returns true when reference value contained in a collection
             -notcontains  Returns true when reference value not contained in a collection
             -in           Returns true when test value contained in a collection
             -notin        Returns true when test value not contained in a collection

Usually you would use the -like string operator in PowerShell, which supports Windows-style wildcard matching (* for any number of any characters, ? for exactly one of any character, [abcdef] for one of a character set):

'abc' -like '*b*' # $true
'abc' -like 'a*' # $true

Another alternative is the -match operator:

'abc' -match 'b' # $true
'abc' -match '^a' # $true

For verbatim substring matching, you would want to escape any input pattern, since -match is a regex operator:

'abc.e' -match [regex]::Escape('c.e')

An alternative is to use the String.Contains() method:

'abc'.Contains('b') # $true

With the caveat that, unlike powershell string operators, it's case-sensitive.


String.IndexOf() is yet another alternative, this one allows you to override the default case-sensitivity:

'ABC'.IndexOf('b', [System.StringComparison]::InvariantCultureIgnoreCase) -ge 0

IndexOf() returns -1 if the substring is not found, so any non-negative return value can be interpreted as having found the substring.

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Ah! As I suspected. Your first line answered my question. Thank you. I should make some time for a thorough reading of the documentation. – Water Cooler v2 Sep 13 '17 at 10:37
  • @WaterCoolerv2 There's a lot of good stuff in the `about_*` help topics, I updated the answer with a table of containment operators from the `about_Comparison_Operators` document – Mathias R. Jessen Sep 13 '17 at 10:43
  • That's right. I'm aware of them, @Mathias. Just haven't made the time to read through all of it carefully yet. – Water Cooler v2 Sep 13 '17 at 11:27
2

The '-contains' operator is best used for comparison to lists or arrays, e.g.

$list = @("server1","server2","server3")
if ($list -contains "server2"){"True"}
else {"False"}

output:

True

I'd suggest using '-match' instead for string comparisons:

$str = "windows"
if ($str -match "win") {"`$str contains 'win'"}
if ($str -match "^win") {"`$str starts with 'win'"}
if ($str -match "win$") {"`$str ends with 'win'"} else {"`$str does not end with 'win'"}
if ($str -match "ows$") {"`$str ends with 'ows'"}

output:

$str contains 'win'
$str starts with 'win'
$str does not end with 'win'
$str ends with 'ows'
kayjay
  • 66
  • 2