7

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

Rich
  • 31,891
  • 3
  • 72
  • 139
user18562
  • 83
  • 1
  • 4

2 Answers2

7

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

B Layer
  • 19,834
  • 2
  • 30
  • 57
  • 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 00:41
  • @D.BenKnoble That's a nice optimization. Will note it. Thanks. – B Layer Aug 11 '18 at 00:52
  • Even better: :g/pattern1/ if line('.')% 2 == 0 | s//bar/ | endif Should also work with older Vims. – Christian Brabandt Aug 11 '18 at 06:58
  • @ChristianBrabandt Noted. Thanks. Do you know offhand when getcurpos() was added? – B Layer Aug 14 '18 at 03:17
  • 1
    looks like patch 7.4.313 introduced getcurpos() – Christian Brabandt Aug 14 '18 at 05:47
  • This is great but it won't work as expected if you're replacing newlines on every other line. The best I could come up with is a two-step process where you first append a delimiter to the end of each line you want to modify (in my situation I needed to modify every odd line): :g/$/ if getcurpos()[1] % 2 != 0 | s//^I/g | endif ... and then replace the delimiter: :%s/^I\n/^I/g – musashiXXX Feb 15 '22 at 22:15
6

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

Christian Brabandt
  • 25,820
  • 1
  • 52
  • 77
  • 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 '20 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 03 '20 at 00:32
  • @BLayer Good point! – Len Nov 04 '20 at 01:10