how do I search/substitute in every other line. :s/pattern1/pattern2{2}/g

file looks like

1 pattern1 text 2 pattern1 text 3 pattern1 text 4 pattern1 text

Output file should look like 1 pattern1 text 2 pattern2 text 3 pattern1 text 4 pattern2 text

2 Answers 2


First thing I thought of is to just inject a conditional in a :global command. Something like this...

:g/./ if getcurpos()[1] % 2 == 0 | s/foo/bar/g | endif

The "global" command :g/./ will stop at every line that has at least one character and run the following command.

getcurpos() returns a list where the second element is the current line number. We do a modulo operation on that number and if the result is 0 we know we're on an even numbered line. In that case we do the substitution.

If you want this to substitute on odd numbered lines instead change the == to !=.

Update: @D.BenKnoble identified an optimization:

:g/foo/ if getcurpos()[1] % 2 == 0 | s//bar/g | endif

We put the pattern to be replaced in the global command. That way we don't bother processing lines that wouldn't result in a substitution. As an extra touch we can then do s//bar because leaving the pattern empty will do the substitution against the most recently used search expression.

Update 2: @ChristianBrabandt notes that getcurpos() is relatively new (v7.4.313) and offers a legacy function for fetching the current line number for those with earlier versions of Vim: replace getcurpos()[1] with line('.').

  • 1
    You can replace the . with pattern and then leave it out in s//bar if its every other line containing the pattern.
    – D. Ben Knoble
    Aug 11, 2018 at 0:41
  • @D.BenKnoble That's a nice optimization. Will note it. Thanks.
    – B Layer
    Aug 11, 2018 at 0:52
  • Even better: :g/pattern1/ if line('.')% 2 == 0 | s//bar/ | endif Should also work with older Vims. Aug 11, 2018 at 6:58
  • @ChristianBrabandt Noted. Thanks. Do you know offhand when getcurpos() was added?
    – B Layer
    Aug 14, 2018 at 3:17
  • 1
    looks like patch 7.4.313 introduced getcurpos() Aug 14, 2018 at 5:47

Here is an alternative. Let Vim run a :s command only on those specific lines:

for i in range(2, line('$'),2)| :exe i.'s/pattern1/pattern2/g'|endfor

This creates a loop, and iterating over each line and for each even line number executes a :<nr>s/pattern1/pattern2/g command, replacing pattern1 by pattern2. If you have pattern1 in your search register already, you can even further shorten it to for i in range(2, line('$'),2)| :exe i.'s//pattern2/g'|endfor

  • This is particularly good because it's easy to see how to adapt it for only a specific range. Which is what I needed it for.
    – Len
    May 31, 2020 at 23:57
  • 1
    @Len I'd argue that using a range is even easier with the other answer. :) Just put it in front: :100,$ g/foo/ if ....
    – B Layer
    Nov 3, 2020 at 0:32
  • @BLayer Good point!
    – Len
    Nov 4, 2020 at 1:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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