Vim :s replace first N < g occurrences on a line

20,462

Solution 1

Building on the :s/pattern/replacement/gc idea from Samus_ (which seems to be the simplest way to ensure correct operation when pattern is contained within the replacement string), to replace the 2nd through 4th occurrences on a single line:

:call feedkeys("nyyyq") | s/pat/string/gc

feedkeys() is a function that stuffs the input string into the keyboard input queue. The point is to do the counting upfront so you don't have to worry about losing count or getting interrupted.

For a more general case, to replace the Mth through Nth occurrences on a single line for N greater than or equal to a very large M:

:call feedkeys(repeat("n", M-1) . repeat("y", N-M+1) . "q") | s/pat/string/gc

Replace M and N with the values you want (you can even let vim do the trivial mental arithmetic if you don't want to do it yourself). Note that . is VimL's string concatenation operator. Obviously this only saves keystrokes for large M. If you use this functionality frequently, it may save you time to put the above in a custom command or function of some sort, as it is quite a bit to type.

Solution 2

For the first question I would do:

:s/a/b
&&

The second is trickier, I don't know a way to do it automatically but you can make vim prompt you on each match like this:

:s/a/b/gc

Then you reply "no" to the first n matches and "yes" to the others.

Solution 3

a a a a a
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a

:3,6g/^/let i=0 | while i<3 | s/a/b/ | let i+=1 | endwhile

a a a a a
a a a a a
b b b a a
b b b a a
b b b a a
b b b a a
a a a a a

Solution 4

I think this might work, replace first, then repeat 2 times:

:s/a/b/
2@:
Share:
20,462

Related videos on Youtube

Kevin
Author by

Kevin

Updated on September 18, 2022

Comments

  • Kevin
    Kevin over 1 year

    In vim, I sometimes have occasion to replace the first few occurrences of a match on a line, but not every one like g would. e.g.:

    a a a a a
    

    to

    b b b a a
    

    I know I could use :s/a/b/[enter]:[up][enter]:[up][enter], but that's tedious enough at three repetitions, I have lines with potentially 10+ substitutions.
    I've tried:

    • :s/a/b/3g: vim complained of trailing characters.
    • :s/a/b/3: changes the first occurrence on this and the following two lines.
    • 3:s/a/b: same as previous.
    • :s/a/b/g3: changes all occurrences on this and the next two lines.
    • :3s/a/b: changes the first occurrence on line 3.
    • :/a/,3/a/s/a/b: changes first occurrence on each line between the next a and the third line containing a in the file (prompting to reverse if necessary).
    • :/a/,/\([^a]*a\)\{3\}/s/a/b/: changes the first occurrence on each line between this and the next with 3 as on it (and this wouldn't have been easily extensible to a multi-character search).

    And various other addressing patterns, none of which worked. I must say, I've learned a fair amount about the :s command trying to find an answer to this problem, but I still haven't solved it.

    Anyone know how to do this?

    (bonus points for specific range, e.g. second through fourth occurrences)

  • Kevin
    Kevin over 12 years
    This is good, but it still suffers from the problem Gilles pointed out (on another post, which since seems to have been deleted), that it only works if the replacement doesn't contain the pattern. Though I do like the scripting idea.
  • Kevin
    Kevin over 12 years
    Hmm, I forgot about c, that might be the best solution proposed yet. I'd still have to count, but I think it's the first option that would work with replacements that contain the search string.
  • Kevin
    Kevin about 12 years
    Hmm. I like this one. I should be able to write a function for it too.
  • bishop
    bishop about 9 years
    A thing of beauty!