Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
858 views
in Technique[技术] by (71.8m points)

powershell - Arrays and -contains - test for substrings in the elements of an array

I am trying to filter out users that are in a specific group.

I got the following output in a variable: Group1 Group2 etc...

One group for each line saved in an array. Im trying to filter out only one specific group. But when I use -contains it always says $false, even tho the group is there.

My Code:

$group = get-aduser -identity name -properties memberof |
  select-object -expandproperty memberof | %{ (get-adgroup $_).name }

$contains = $group -contains "string"

$contains is $false even if the array has elements that contain the string...

What am I missing?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It looks like your misconception was that you expected PowerShell's -contains operator to perform substring matching against the elements of the LHS array.
Instead, it performs equality tests - as -eq would - against the array's elements - see this answer for details.

In order to perform literal substring matching against the elements of an array, use:

# With non-literal search strings:
[bool] $contains = $group -match ([regex]::Escape($someString))

# With a string literal that doesn't contain regex metachars.,
# escaping isn't needed.
[bool] $contains = $group -match 'foo'

# With a string literal with metachars., you must individually -escape them.
[bool] $contains = $group -match 'foo.bar'

Note:

  • The above shows a robust, generic way of ensuring that your search string is treated as a literal value using [regex]::Escape(), which is necessary because -match expects a regex (regular expression) as its RHS (the search pattern).

  • Escaping isn't always necessary; specifically, only the presence of so-called metacharacters (those with special meaning in a regex, such as .) requires it, and when you're using a string literal, you can opt to directly -escape them; e.g., to search for literal substring a.b, you can pass 'a.b'.

    • Chances are that AD group names do not require escaping, but it's important to be aware of the need for it in general.
  • As with all operators in PowerShell, by default the matching is case-insensitive; use the -cmatch variant for case-sensitive matching.

  • The [bool] type constrained above is used to ensure that the result of the -match operation is converted to a Boolean:

    • While -match directly returns a Boolean with a scalar (non-array) LHS, with an array LHS it acts as a filter, and returns the matching array elements instead; interpreted in a Boolean context, such as in an if conditional, that usually still gives the expected result, because a non-empty array is interpreted as $true, whereas an empty one as $false; again, however it's important to know the difference.
  • This will rarely be a performance concern in practice, but it is worth noting that -match, due to acting as a filter with arrays, always matches against all array elements - it doesn't stop once the first match is found, the way that the -contains and -in operators do.

    • On the plus side, you can use -match to obtain the matching elements themselves.

The mistaken expectation of -contains performing substring matching may have arisen from confusion with the similarly named, but unrelated String.Contains() method, which indeed performs literal substring matching; e.g., 'foo'.Contains('o') yields $true. Also note that .Contains() is case-sensitive by default.

PowerShell has no operator for literal substring matching.

However, you could combine PowerShell's generic array-filtering features with the .Contains() string method - but note that this will typically perform (potentially much) worse than the -match approach.

A reasonably performant alternative is to use the PSv4+ .Where() array method as follows:

# Note: Substring search is case-sensitive here.
[bool] $contains = $group.Where({ $_.Contains("string") }, 'First')

On the plus side, this approach stops matching once the first match is found.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...