1

I have to filter my results by certain strings and tried to do it with -match and -contains.

-match works if I have just one value to filter, but not with an array.

-contains neither works with one string, nor with a string array.

Why isn't it working with several values? Especially the -contains. Or is there another easy way to solve it?

$Folder = 'C:\Test'

$filterArray =  @('2017-05', '2017-08')
$filter =  '2017-05'

## test with -MATCH

## working with one match string
Get-ChildItem -Path $Folder -Recurse -Include *.txt |
    Where { $_.FullName -match $filter } |
    ForEach-Object { $_.FullName }
## NOT working with match string array - no results
Get-ChildItem -Path $Folder -Recurse -Include *.txt |
    Where { $_.FullName -match $filterArray } |
    ForEach-Object { $_.FullName }

## test with -CONTAINS
## NOT working with one contains string - no results
Get-ChildItem -Path $Folder -Recurse -Include *.txt |
    Where { $_.FullName -contains $filter } |
    ForEach-Object { $_.FullName }
## NOT working with contains string array- no results
Get-ChildItem -Path $Folder -Recurse -Include *.txt |
    Where { $_.FullName -contains $filterArray } |
    ForEach-Object { $_.FullName }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Esteban P.
  • 2,789
  • 2
  • 27
  • 43
  • Related (difference between `-Contains` (collection operator), `-match` /`-imatch` (regular expression matching), and `-like` / `-ilike` (SQL-like matching)): *[PowerShell and the -contains operator](https://stackoverflow.com/questions/18877580/powershell-and-the-contains-operator/18877950#18877950)* – Peter Mortensen Jun 09 '21 at 20:44

2 Answers2

4

Using an array as the second operand for the -match or -contains operators doesn't work. There are basically two approaches you could take:

  • Build a regular expression from the array and use that with the -match operator:

    $pattern = @($filterArray | ForEach-Object {[regex]::Escape($_)}) -join '|'
    ... | Where-Object { $_.FullName -match $pattern }
    

This is the preferred approach.

  • Use a nested Where-Object filter and the String.Contains() method:

    ... | Where-Object {
        $f = $_.FullName
        $filterArray | Where-Object { $f.Contains($_) }
    }
    
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
2

Why isn't it working with several values?

Because these operators were designed to test against a single argument, plain and simple.

The ability to match against multiple arguments in a single operation would beg the question: "Does the input need to satisfy all or any of the argument conditions"?


If you want to test for a match against any of an array of regex patterns, you can construct a single pattern from them using a non-capturing group, like so:

$filterPattern = '(?:{0})' -f ($filterArray -join '|')
Get-ChildItem -Path $Folder -Recurse -Include *.txt | Where {$_.FullName -match $filterPattern} | ForEach-Object{ $_.FullName }

You can also drop the Where-Object and ForEach-Object loop completely, since PowerShell 3.0 supports property enumeration:

(Get-ChildItem -Path $Folder -Recurse -Include *.txt).FullName -match $filterPattern
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Why the non-capturing group? Simply joining the strings should work as well. I would recommend escaping the individual strings, though. – Ansgar Wiechers Sep 27 '17 at 12:45
  • You should always escape string when you built regular expressions programmatically. `$filterPattern = '(?:{0})' -f (($filterArray |% { [Regex]::Escape($_) }) -join '|')` – fdafadf Sep 27 '17 at 12:46