DEV Community

Cover image for PowerShell -like vs -match: The Real Difference (and Why Your Filter Matches Too Much)
arnostorg
arnostorg

Posted on Edited on

PowerShell -like vs -match: The Real Difference (and Why Your Filter Matches Too Much)

You wanted only the .log files. You wrote Where-Object Name -match 'log', and back came catalog.txt, backup.log.zip, and error-log.txt too. No error, just too many results.

The bug is mixing up two operators that look alike. -like uses wildcards and has to match the whole string. -match uses regular expressions and is happy to match any part of the string. Once that clicks, most "my filter matches too much" problems go away.

Everything below was run in PowerShell 7.4 (7.4.6), and the output shown is the real output. The same rules apply in Windows PowerShell 5.1.

The shortest honest comparison

-like -match
Pattern type Wildcard (*, ?, [ ]) Regular expression (regex)
How much must match The whole string Any part of the string
. means A literal dot Any single character
Anchors needed No Yes, ^ and $ if you want start/end
Sets $Matches No Yes (for a single string)
Case-sensitive version -clike -cmatch
Negated version -notlike -notmatch

The trap in three lines

'catalog.txt' -like 'log'
'catalog.txt' -like '*log*'
'catalog.txt' -match 'log'
Enter fullscreen mode Exit fullscreen mode
False
True
True
Enter fullscreen mode Exit fullscreen mode

-like 'log' asks "is the whole string exactly log?" No. To get a "contains" check with -like, you need stars on both sides. -match 'log' asks "does log appear anywhere?" Yes, inside catalog.

Why -match '.log' matches almost everything

Many people translate *.log into regex by just dropping the star. Here is what happens:

$names = 'app.log', 'catalog.txt', 'backup.log.zip', 'error-log.txt', 'applog'

$names -like '*.log'
'---'
$names -match '.log'
Enter fullscreen mode Exit fullscreen mode
app.log
---
app.log
catalog.txt
backup.log.zip
error-log.txt
applog
Enter fullscreen mode Exit fullscreen mode

-like '*.log' gave exactly one file. -match '.log' matched all five, because in regex:

  • . is not a dot. It means "any one character", so alog in catalog.txt and plog in applog both count.
  • There is no anchor, so the match can sit anywhere, like the middle of backup.log.zip.

The regex that means the same thing as *.log escapes the dot and anchors the end:

$names = 'app.log', 'catalog.txt', 'backup.log.zip', 'error-log.txt', 'applog'

$names -match '\.log$'
Enter fullscreen mode Exit fullscreen mode
app.log
Enter fullscreen mode Exit fullscreen mode

\. is a literal dot, and $ means "end of string". If you also want "must start with", add ^ at the front, for example '^app\.log$'.

Wildcards cheat sheet for -like

Wildcards only have three special characters:

  • * is zero or more characters
  • ? is exactly one character
  • [ ] is one character from a set or range, like [abc] or [0-5]
'report1.csv' -like 'report?.csv'
'report10.csv' -like 'report?.csv'
'report7.csv' -like 'report[0-5].csv'
'server-b' -like 'server-[abc]'
Enter fullscreen mode Exit fullscreen mode
True
False
False
True
Enter fullscreen mode Exit fullscreen mode

report10.csv fails because ? is exactly one character, and 10 is two. report7.csv fails because 7 isn't in [0-5].

-match gives you $Matches for free

When -match runs against a single string and finds something, it fills the $Matches automatic variable. Index 0 is the whole match, and each pair of parentheses becomes a numbered group:

'srv-web-01' -match 'web-(\d+)'
$Matches
'---'
$Matches[1]
Enter fullscreen mode Exit fullscreen mode
True

Name                           Value
----                           -----
1                              01
0                              web-01
---
01
Enter fullscreen mode Exit fullscreen mode

Named groups are easier to read in scripts:

'user=alice; role=admin' -match 'user=(?<user>\w+).*role=(?<role>\w+)'
"$($Matches.user) is $($Matches.role)"
Enter fullscreen mode Exit fullscreen mode
True
alice is admin
Enter fullscreen mode Exit fullscreen mode

-like never touches $Matches. It only tells you True or False.

On arrays, both operators filter (and $Matches lies)

Put an array on the left and both operators stop returning True/False. They return the items that match. That's handy, but there's a catch: -match on an array does not update $Matches.

$null = 'warm-up' -match 'warm'   # sets $Matches
$servers = 'srv-web-01', 'srv-db-02', 'srv-web-03'

$servers -match 'web'
'---'
$Matches[0]               # still the old value
Enter fullscreen mode Exit fullscreen mode
srv-web-01
srv-web-03
---
warm
Enter fullscreen mode Exit fullscreen mode

The filtering worked, but $Matches[0] is still warm from the earlier single-string match. If you need capture groups for each item, loop over the items and run -match on one string at a time, or use Select-String.

Case-insensitive by default

Both operators ignore case unless you ask for the c version:

'ERROR.LOG' -like '*.log'
'ERROR.LOG' -clike '*.log'
'ERROR: disk full' -match 'error'
'ERROR: disk full' -cmatch 'error'
Enter fullscreen mode Exit fullscreen mode
True
False
True
False
Enter fullscreen mode Exit fullscreen mode

There are also -ilike and -imatch, which spell out the default case-insensitive behavior.

-notlike and -notmatch

The negated versions follow the same rules, so the same anchoring advice applies:

$names = 'app.log', 'catalog.txt', 'backup.log.zip'

$names -notlike '*.log'
'---'
$names -notmatch '\.log$'
Enter fullscreen mode Exit fullscreen mode
catalog.txt
backup.log.zip
---
catalog.txt
backup.log.zip
Enter fullscreen mode Exit fullscreen mode

Without the anchor, -notmatch 'log' would throw away catalog.txt too, which is the same trap in reverse.

Escaping user input: [WildcardPattern]::Escape and [regex]::Escape

If the pattern comes from a variable (a file name, a search box, a CSV column), special characters will bite you. Square brackets are wildcards for -like. Dots, dollar signs, and parentheses are regex for -match. Escape the text first:

'file[1].txt' -like 'file[1].txt'
'file[1].txt' -like ([WildcardPattern]::Escape('file[1].txt'))
[WildcardPattern]::Escape('file[1].txt')
'---'
'price: $5.00 (USD)' -match '$5.00 (USD)'
'price: $5.00 (USD)' -match [regex]::Escape('$5.00 (USD)')
[regex]::Escape('$5.00 (USD)')
Enter fullscreen mode Exit fullscreen mode
False
True
file`[1`].txt
---
False
True
\$5\.00\ \(USD\)
Enter fullscreen mode Exit fullscreen mode

file[1].txt treats [1] as "one character from the set 1", so it doesn't match the literal brackets. '$5.00 (USD)' as a regex means "end of string, then 5, any character, 00, space, a group containing USD", which can never match. After escaping, both behave like plain text.

Real use: filtering Get-ChildItem with Where-Object

Here is the original bug and two correct fixes. The demo creates its own temp folder and cleans up after itself:

$dir = Join-Path ([IO.Path]::GetTempPath()) 'like-vs-match-demo'
New-Item -ItemType Directory -Path $dir -Force | Out-Null
'app.log', 'catalog.txt', 'backup.log.zip', 'error-log.txt' |
    ForEach-Object { New-Item -Path $dir -Name $_ -ItemType File -Force | Out-Null }

# Too much: regex 'log' matches anywhere in the name
Get-ChildItem $dir | Where-Object Name -match 'log' | Select-Object -ExpandProperty Name
'---'
# Exactly .log files: wildcard...
Get-ChildItem $dir | Where-Object Name -like '*.log' | Select-Object -ExpandProperty Name
# ...or anchored regex
Get-ChildItem $dir | Where-Object Name -match '\.log$' | Select-Object -ExpandProperty Name

Remove-Item $dir -Recurse -Force
Enter fullscreen mode Exit fullscreen mode
app.log
backup.log.zip
catalog.txt
error-log.txt
---
app.log
app.log
Enter fullscreen mode Exit fullscreen mode

Both fixes return only app.log. For simple file name filters, -like is usually the clearer choice. Reach for -match when you actually need regex features like \d+, alternatives (error|warn), or capture groups.

If you want to try these patterns without setting up anything, CMD Master's free interactive PowerShell course in the browser gives you a real terminal to experiment in.

Quick checklist

  • Matching file names like *.log or report-2026-*.csv: use -like.
  • Need digits, alternatives, or pieces of the text: use -match and read $Matches.
  • Using -match for a whole value: anchor it with ^ and $.
  • Remember the regex . is any character. Write \. for a real dot.
  • Running -match on an array: you get filtered items, not a fresh $Matches.
  • Need case to matter: -clike / -cmatch.
  • Pattern comes from a variable: [WildcardPattern]::Escape() for -like, [regex]::Escape() for -match.

Practice in a real terminal

Wildcards and regex are much easier to learn by typing them than by reading tables. CMD Master is a free, interactive browser terminal for learning CMD, PowerShell, and Bash, with instant feedback on every command and nothing to install.

Sources: Microsoft Learn: about_Comparison_Operators, about_Wildcards, about_Regular_Expressions, about_Automatic_Variables ($Matches), Where-Object, WildcardPattern.Escape, Regex.Escape.

Quick quiz

Without running it: what does this line output in PowerShell 7.4?

'app.log' -match '*.log'
Enter fullscreen mode Exit fullscreen mode

Is it True, False, or something else? Drop your answer (and why) in the comments.

Top comments (0)