4

I've got this 4-line content:

foo
baa
baz
quz

and I'd like to replace baa to bar only when quz is found.

I've tried these commands (which highlights baa correctly, but says it's not found):

:/quz/s/baa/bar/
:/quz/1,$s@baa@bar@

E486: Pattern not found: baa

and this:

:/quz/%s/baa/bar/
:/baa/,/quz/%s/baa/bar/

E488: Trailing characters

and this:

/quz/s/baa/bar/
/quz/%s/baa/bar/

search hit BOTTOM, continuing at TOP

What I am doing wrong? What is the correct syntax?

  • Are you sure that every line having quz will have its predecessor line having baa? – SibiCoder Jul 9 '16 at 15:58
  • Yes, let assume 'baa' is always before 'quz'. – kenorb Jul 9 '16 at 16:24
  • By before, I mean not only in the previous line (one line up), but it could be two lines before as well or in any other line before. – kenorb Jul 9 '16 at 16:57
  • OK. I will delete my answer then and write it when I come up with good answer. – SibiCoder Jul 9 '16 at 17:08
4

:/baa/,/quz/s/baa/bar/ works, but that will only work with the first occurrence of baa. If you want to replace all occurrences of baa with bar that come before quz:

,/quz/s/baa/bar

If your scenario calls for replacing all occurrences of baa if quz is found:

 /quz/,%s/baa/bar

That will replace all occurrences of baa with bar if quz is found. This is basically using the range /quz/,1,$ which is permitted (% is equivalent to 1,$).

From :h cmdline-ranges

If more line specifiers are given than required for the command, the first one(s) will be ignored.

So if the first line specifier /quz/ fails, it generates an error and causes the rest of the command to not execute. But, if it does find a match, the 1,$ range is used since substitution only needs 2 range specifiers.

Conditional substitution

If you want to conditionally replace baa with bar, you'll want to use the :global command.


If you want replace baa with bar if it comes before quz:

:g/quz/?baa?,s//bar

Explanation:

g/quz/   - Moves to the next 'quz'
?baa?,   - Create a range from the last 'baa' to the current line
s//bar   - Reuse the previous pattern (baa) and substitute with 'bar'

However, that only matches the first baa that comes before quz.


If you want to replace all occurrences of baa that comes before quz:

g/quz/?quz\|\%^?,s/baa/bar

Explanation:

g/quz/       - Moves to the next 'quz'
?quz\|\%^?,  - Create a range from the previous 'quz' or the
               beginning of the buffer.
s/baa/bar    - Replace 'baa' with 'bar'

If you want to replace all occurrences of baa with bar if they're between two quz lines:

g/quz/silent! ??,s/baa/bar

Explanation:

g/quz/      - Moves to the next 'quz'
silent!     - The first occurence of 'quz' won't have a previous
              'quz' match. So, ignore the error about an invalid
              range.
??,         - Reuse 'quz' pattern to search for the previous occurrence
s/baa/bar   - Replace 'baa' with 'bar'
2

Ok, it was very close and this actually worked:

:/baa/,/quz/s/baa/bar/
  • this is an interesting solution, I was thinking of \ns in :%s – Sundeep Jul 9 '16 at 15:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.