tl;dr
Regex grouping constructs (?<=…)
and (?>…)
serve different purposes and only happen to work the same in your particular scenario; neither is called for in your scenario.
Use '...' -notmatch 'x'
to test if a given string contains any instances of 'x'
(returns $true
if not).
Background information:
The two grouping constructs you reference serve different purposes (enclosed subexpressions are represented with placeholder …
below):
(?<=…)
is a (zero-width, positive) lookbehind assertion:
It is a non-capturing grouping construct that must match the enclosed subexpression immediately before (to the left, i.e. "looking behind") where the remaining expression matches, without capturing what the subexpression matched.
In essence, this means: When what follows this construct matches, also make sure (assert) that what comes before it matches the subexpression inside (?<=…)
; if it doesn't, there's no overall match; if it does, don't capture (include in the results) its match.
Therefore, this construct only makes sense if placed before a capturing construct; e.g.:
# Matches only 'unheard of', because only in it is the match
# for 'hear.' preceded by 'un'
# Captures only 'heard' from the matching string, not the 'un'
'heard from', 'unheard of' -match '(?<=un)hear.'
(?>…)
is an atomic group, aka non-backtracking subexpression:
It is a capturing grouping construct - similar to a regular capture group (matched subexpression), (…)
- except that it never backtracks.
In essence, this means: once the subexpression has found a match, it won't allow backtracking based on the remainder of the expression; this construct is mostly used as a performance optimization when it is known that backtracking wouldn't succeed.
# Atomic group:
# -> $false, because the atomic group fully consumes the string,
# so there's nothing for '.' to match *after* the group.
'abc!' -match '(?>.+).'
# Regular capture group:
# -> $true, with backtracking; the capture group captures 'abc'
'abc!' -match '(.+).'
What you tried:
(?<=^[^x]*)$
- your regex with a lookbehind assertion
As noted above, there's no good reason to use a lookbehind assertion without following it with a capturing expression.
Your regex will by definition not capture anything ($
is itself an assertion).
Since you're matching the whole string, the immediate simplification would be not to use a grouping construct at all (but see the bottom section):
^[^x]*$
As an optimization, if you explicitly want to prevent the capturing that happens by default, use a noncapturing group, (?:…)
:
(?:^[^x]*$)
(?>^[^x]*)$
- your regex with an atomic group
Since you're matching the whole string, there is no reason to use a atomic group, given that there's no backtracking that needs preventing, so this regex is in effect the same as (^[^x]*)$
, i.e. a regular capture group (followed by $
).
As noted, there's no reason to capture anything here, so (?:^[^x]*$)
would prevent that.
In short:
Both your regexes match the input string in full, and therefore require no grouping construct (except, optionally, to explicitly prevent capturing).
Read on for a much simpler solution.
Taking a step back:
The conceptually simplest and most efficient solution is:
'...' -notmatch 'x'
That is, you can let -notmatch
, the negated form of PowerShell's -match
operator look for (at most one) x
, and negate the Boolean result, so that not finding any x
returns $true
.
In other words: the test succeeds if no x
is present in the input string.