Reputation: 457
This is my favourite operator in Powershell, but sometimes I feel like it despises me !!
Lets say I have a string like so in $Clipboard
:
PowerShell Documentation - PowerShell | Microsoft Learn
https://learn.microsoft.com/en-us/powershell/
Welcome to Python.org
https://www.python.org/
GitHub
https://github.com/
Invoking:
$Clipboard.trim() -split '\n' -NotMatch "^$"
Returns the actual strings that got matched, not booleans:
PowerShell Documentation - PowerShell | Microsoft Learn
https://learn.microsoft.com/en-us/powershell/
Welcome to Python.org
https://www.python.org/
GitHub
https://github.com/
Yet if I do something like this:
$Clipboard.trim() -split '\n' -NotMatch "^$"| % {$Title = $_ -Match "https?:|//www\."
$Title
}
I get booleans:
False
True
False
True
False
True
I am starting to avoid -Match
now, send help please.
Upvotes: 2
Views: 532
Reputation: 438143
This is a general feature of PowerShell's comparison operators:
With a scalar (single) LHS operand, they return a [bool]
result ($true
or $false
).
With an array (collection) LHS operand, they act as filters and return those LHS elements that match, (invariably) as an array.
With respect to the -match
operator, specifically, note that the automatic $Matches
variable, which contains information about what was matched, is only populated with a scalar LHS.
As iRon points out, PowerShell's implicit to-Boolean coercion rules also allow you to use operations with array-valued LHS as conditionals; e.g.:
# -> 'MATCH found',
# because the -match operation returns @('foo'), i.e.
# the subarray of matching items, and [bool] @('foo') is $true.
if (@('foo', 'bar') -match 'f') { 'MATCH found' } else { 'NO match' }
# -> 'NO match',
# because the -match operation returns @(), i.e. an *empty array,
# and [bool] @() is $false
if (@('foo', 'bar') -match 'x') { 'MATCH found' } else { 'NO match' }
However, there are pitfalls, namely when the filtering operation returns a single result that happens to be $false
when coerced to [bool]
Note that even though a single-element array is returned in that case, PowerShell treats it the same as its one and only element in this context:
# !! -> 'NO match', even though a match *was* found:
# !! The -match operation returns @('') and [bool] @('') is the
# !! same as [bool] '' and therefore $false.
if (@('', 'bar') -match 'foo|^$') { 'MATCH found' } else { 'NO match' }
# !! -> 'NO match', even though a match *was* found:
# !! The -eq operation returns @(0) and [bool] @(0) is
# !! the same as [bool] 0 and therefore $false.
if (@(0, 1) -eq 0) { 'MATCH found' } else { 'NO match' }
If two or more results are returned, the coercion result is always $true
, however, irrespective of the value of the array elements (analogous to how no results, i.e. an empty return array, always yields $false
).
A summary of PowerShell's to-Boolean conversion rules is in the bottom section of this answer.
Upvotes: 3
Reputation: 8442
This is by design. In your first example, where you get the strings back, you are passing a collection to -match
and it is performing an implicit enumeration and returning the matched strings. in you second example, you are enumerating the collection yourself and testing each item individually, hence the Boolean
instead.
This behaviour is explicitly stated in the -match operator help:
When the input of these operators is a scalar value, they return a Boolean value. When the input is a collection of values, the operators return any matching members. If there are no matches in a collection, the operators return an empty array.
Upvotes: 2