How do I match a pattern only when it is preceded or followed by another pattern using Vim regex?
Answer
\@<= and \@=
Explanation
Vim's regex engine supports zero-width lookahead and lookbehind assertions using the \@ atom. These let you match text based on what surrounds it, without including the surrounding text in the match itself. This is essential for complex search-and-replace operations where context determines what should change.
How it works
The four assertion atoms:
\(pat\)\@<=— positive lookbehind: match position preceded bypat\(pat\)\@<!— negative lookbehind: match position NOT preceded bypat\(pat\)\@=— positive lookahead: match position followed bypat\(pat\)\@!— negative lookahead: match position NOT followed bypat
The pattern being asserted must be grouped in \(\) and the \@... atom follows immediately.
Example
Replace foo only when it appears before bar (positive lookahead):
:%s/foo\(bar\)\@=/FOO/g
In foobar foobaz, only the first foo becomes FOO.
Match bar only when NOT preceded by foo (negative lookbehind):
/\(foo\)\@<!bar
In foobar barbell, only barbell's bar matches.
Tips
- In
\v(very magic) mode the syntax shortens:@<=,@<!,@=,@! - Lookbehind with variable-length patterns has performance implications — keep them as specific as possible
- For simple cases,
\zsand\ze(which reposition match boundaries) are often easier; reach for\@<=when you need true zero-width context conditions