-contains or -match several values

2019-01-20 20:03发布

问题:

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 }

回答1:

Using an array as the second operand for the -match or -contains operators doesn't work. There are basically 2 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($_) }
    }
    


回答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