nowox
nowox

Reputation: 29096

How to match all for a second pattern that follows a particular pattern only?

Using , I would like to replace all the foo with bar only on the lines that starts with foobar?

Input:

foobar foo   foo                  foo       foo
foobaz    foo   foo            foo   foo foo

Output:

foobar bar   bar                  bar       bar
foobaz    foo   foo            foo   foo foo

I naively tried \K like:

s/^foobar\Kfoo/bar/g

Unfortunately it does not work. For example, with Perl I could do:

s/^foobar(.*)/$&=~s#\bfoo\b#bar#gr/gme

Is there a way to do it, for example in https://regex101.com/?

Upvotes: 1

Views: 40

Answers (2)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626845

You may combine \K with a \G operator in a regex:

(?m)(?:(?!^)\G|^foobar\b)(?:(?!\bfoo\b).)*\K\bfoo\b

where (?:(?!\bfoo\b).)* tempered greedy token is synonymic to .*?. If there is not substring to exclude in between foobar and foo, the .*? will perform better, however, in case you want to further restrict the text between foobar and foo, you will need the token.

See the regex demo

Word boundaries might turn out redudant if you are not interested in a whole word search.

Details:

  • (?m) - multiline mode on
  • (?:(?!^)\G|^foobar\b) - match only the end of the previous successful match (with (?!^)\G where \G matches the start of string or end of the previous successful match, so the (?!^) is necessary to exclude position at the start of the string) or the whole word foobar at the beginning of a line (^ matches the line start position due to (?m) flag)
  • (?:(?!\bfoo\b).)* - match any char other than linebreak symbols, 0+ times, if the char is not starting the foo sequence that is a whole word (or instead just replace this part with .*? since there is no text to exclude in between foobar and foo).
  • \K - omit the text matched so far
  • \bfoo\b - a whole word foo

Upvotes: 1

Casimir et Hippolyte
Casimir et Hippolyte

Reputation: 89557

yes, with this \G (position after the previous match or start of the string) based pattern:

(?:\G(?!\A)|(?m)^foobar\b).*?\K\bfoo\b

demo

Upvotes: 1

Related Questions