Reputation: 191
I'm trying to work out if a string exists in an array, even if it's a substring of a value in the array.
I've tried a few methods and just can't get it to work, not sure where I'm going wrong.
I have the below code, you can see that $val2 exists within $val1, but I always get a FALSE when I run it.
$val1 = "folder1\folder2\folder3"
$val2 = "folder1\folder2"
$val3 = "folder9"
$val_array = @()
$val_array += $val1
$val_array += $val3
$null -ne ($val_array | ? { $val2 -match $_ }) # Returns $true
I also tried:
foreach ($item in $val_array) {
if ($item -match $val2) {
Write-Host "yes"
}
}
Upvotes: 1
Views: 2237
Reputation: 440556
iRon's helpful answer offers the best solution to your problem, using wildcard matching via the -like
operator.
Note:
The need to escape select characters in a search pattern in order for the pattern to be taken verbatim in principle also applies to the wildcard-based -like
operator, not just to the regex-based -match
operator, but since wildcard expressions have far fewer metacharacters than regexes - namely just *
, ?
, and [
- the need for such escaping doesn't often arise in practice; whereas regexes require \
as the escape characters, wildcards use `
, and programmatic escaping can be achieved with [WildcardPattern]::Escape()
Unfortunately, as of PowerShell 7.2, there is no dedicated operator for verbatim substring matching:
A workaround for this limitation is to call the [string]
.NET type's .Contains()
method (on a single input string only), however, this performs case-sensitive matching, whereas PowerShell operators are case-insensitive by default, but offer case-sensitive variants simply by prefixing the operator name with c
(e.g., -clike
, -cmatch
).
In Windows PowerShell, .Contains()
is invariably case-sensitive, but in PowerShell (Core) 7+ an additional overload is available that offers case-insensitive matching:
'Foo'.Contains('fo') # -> $false, due to case difference
# PowerShell (Core) 7+ *only*:
'Foo'.Contains('fo', 'InvariantCultureIgnoreCase') # -> $true
Caveat: Despite the name similarity, PowerShell's -contains
operator does not perform substring matching; instead, it tests whether a collection contains a given element (in full).
As for what you tried:
Your primary problem is that you've accidentally swapped the -match
operator's operands: the search pattern - which is invariably interpreted as a regex (regular expression) - must be on the RHS.
As iRon points out, in order for your search pattern to be taken verbatim (literally), you need to escape regex metacharacters with \
, and the robust, programmatic way to do this is with [regex]::Escape()
.
Therefore, the immediate fix would have been (?
is a built-in alias of the Where-Object
cmdlet):
# OK, but SLOW.
$val_array | ? { $_ -match [regex]::Escape($val2) }
However, this solution is inefficient (it involves the pipeline and a cmdlet).
Fortunately, PowerShell's comparison operators can be applied to arrays (collections) directly, in which case they act as filters, i.e. they return the sub-array of matching elements - see the docs.
iRon's answer uses this technique with -like
, but it equally works with -match
, so that your expression can be simplified to the following, much more efficient form:
# MUCH FASTER.
$val_array -match [regex]::Escape($val2)
Upvotes: 2
Reputation: 23862
The -Match
operator does a regular expression comparison. Where the backslash character (\
) has a special meaning (it escapes the following character).
Instead you might use the -Like
operator:
$val_array -Like "*$val2*"
Yields:
folder1\folder2\folder3
Upvotes: 3
Reputation: 2300
Try the string method Contains
:
$null -ne ($val_array | ? { $_.Contains($val2) })
Upvotes: 0