1

How can i compare multiple wildcard patterns on a if statement?

if ($file.Name -like "*test*", "*.tmp")  
{
    # do something
}
js2010
  • 23,033
  • 6
  • 64
  • 66
Woody
  • 21
  • 4
  • 2
    `-like` is not a regex operator, it's a _wildcard_ operator. Are you hoping to match files that contain either or _both_ substrings? eg. are `something.tmp` and `test.zip` both matches, or does it need to be `something_test_something.tmp`? – Mathias R. Jessen Nov 09 '22 at 17:28
  • 1
    @MathiasR.Jessen yes, im trying to match something.tmp and test.zip separately – Woody Nov 09 '22 at 17:31
  • 1
    `if ($file.Name -like '*test*' -or $file.name -like '*.tmp')` – js2010 Nov 09 '22 at 17:46
  • 1
    Looks like your expression gets converted to string `'*test* *.tmp'` – js2010 Nov 09 '22 at 17:56
  • `$file.extension -in 'tmp','txt'` if we were just talking extensions – js2010 Dec 26 '22 at 16:35

1 Answers1

0

Unfortunately, neither -like, the wildcard matching operator, nor -match, the regular-expression matching operator, support an array of patterns to match against, as of PowerShell 7.2.x:

  • Having this ability (with any one of the patterns matching being considered an overall match) would be useful; GitHub issue #2132 asks for it to be implemented in a future PowerShell (Core) version.

  • While PowerShell currently reports no error when you try to use an array on the RHS, its current behavior is virtually useless: the array is stringified to form a single string, which means that its elements are joined with spaces; that is, $file.Name -like "*test*", "*.tmp" is the same as $file.Name -like "*test* *.tmp"

However, as a workaround you can use -match with a single regex pattern that uses alternation (|) to match one of multiple values:

if ($file.Name -match 'test|\.tmp$') {
  # do something
} 

Note:

  • -match matches substrings by default, so there's no need for regex equivalents to the * wildcard metacharacter.

    • Conversely, you need to anchor patterns if you want them to occur in a certain position, such as using $ above to assert that subexpression \.tmp matches at the end of the string.
  • regexes, which are much more powerful than wildcards, have many more metacharacters that may require escaping with \; in this case, metacharacter ., which matches any character, must be escaped as \. in order to be matched as a literal .

    • For programmatic escaping of (sub)strings to be interpreted literally in a regex, use [regex]::Escape(), e.g. [regex]::Escape('.tmp'), which yields '\.tmp'.

If you prefer to stick with -like, you need two operations, which you must join with -or, as js2010 shows in a comment on the question:

if ($file.Name -like '*test*' -or $file.name -like '*.tmp') {
  # do something
}

If the number of patterns is large or not known in advance, use a loop, such as via the [intrinsic .ForEach() method](intrinsic .Where() method)):

$patterns = '*test*', '*.tmp'  # Array of patterns
if ($patterns.Where({ $file.Name -like $_ }, 'First')) {
  # do something
}

Finally, it's worth noting that Get-Item and Get-ChildItem support -Include (and -Exclude) parameters that do support multiple patterns (unlike -Filter), so you may be able to pre-filter your files, without needing to examine their names later; e.g.:

Get-Item .\* -Include *test*, *.tmp

Unfortunately, -Include and -Exclude are notoriously finicky, except (mostly) when -Recurse is also used; e.g., using Get-ChildItem . -Include *test*, *.tmp would not work as intended - see this answer for details.

mklement0
  • 382,024
  • 64
  • 607
  • 775