I was trying to search for a pattern in a string, it would match when “bar” is in present but not when “foo” also appears. In the initial try, I wrote this pattern, for half the goal:

bar.*(?!foo)

I thought it would not match for text bar foo, but it did, because .* would match all the rest of string. To put it short, for this kind of situation, you need to have a more restricted pattern; or, in this case, move the .* inside the negative lookahead assertion. The following is a test using Python, I would use grep with Perl regular expression extension, but my grep isn’t compiled with that.

>>> import re
>>> text = 'bar foo'
>>> print(re.search('bar.*(?!foo)', text))
<_sre.SRE_Match object at 0x7fc6d94678b8>
>>> print(re.search('bar(?!.*foo)', text))
None

The first test returns a match, which we don’t want; the second returns no match, that’s the correct result because “foo” is in the string.

As for negative lookbehind assertion, in similar fashion, I could make it to be:

>>> text = 'foo bar'
>>> print(re.search('(?!foo).*bar', text))
<_sre.SRE_Match object at 0x7fc6d94678b8>
>>> # print(re.search('(?<!foo.*)bar', text))
... # Traceback (most recent call last):
... #   ...
... # sre_constants.error: look-behind requires fixed-width pattern
... print(re.search('(?<!foo )bar', text))
None

As you can see, since Python can only look behind with fixed-width pattern, so, I use (?<!foo ) since the “foo” would always before “bar” in my actual case. In fact, it would only be right after “bar” as well for lookahead pattern. I don’t know about the implementations in libraries of other languages, this fixed-width would not affect me, but you might need to change up somehow if you need variable-width pattern and it’s not supported.

The final pattern would be like:

(?<!foo )bar(?!.*foo)

or simply:

(?<!foo )bar(?! foo)

1   References