2

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

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('.').

| improve this answer | |
  • 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 '18 at 0:41
  • @D.BenKnoble That's a nice optimization. Will note it. Thanks. – B Layer Aug 11 '18 at 0:52
  • Even better: :g/pattern1/ if line('.')% 2 == 0 | s//bar/ | endif Should also work with older Vims. – Christian Brabandt Aug 11 '18 at 6:58
  • @ChristianBrabandt Noted. Thanks. Do you know offhand when getcurpos() was added? – B Layer Aug 14 '18 at 3:17
  • 1
    looks like patch 7.4.313 introduced getcurpos() – Christian Brabandt Aug 14 '18 at 5:47
3

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

| improve this answer | |
  • 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 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 at 0:32
  • @BLayer Good point! – Len Nov 4 at 1:10

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.