8

I had a situation where I wanted to replace FOO with BAR through out a file. However, I only want to do it in certain places, say, between lines 68–104, 500–537, and 1044–1195. In practice, I dropped markers at the lines of interest (via ma, mb, mc, etc.) and ran the following:

:'a,'b s/FOO/BAR/g | 'c,'d s/FOO/BAR/g | 'e,'f s/FOO/BAR/g

I had to repeat this dozens of times with different search and replace terms s/CAT/DOG, etc., and it became a pain to have to rewrite the command line each time. I was lucky in that I had only three places that I needed to confine my search to (imagine how messy the command line would get if there were 30 or 40).

Short of writing a function, is there any neater way of doing this?

On a related note. I copied FOO to the s (search) register, and BAR to the r (replace) and tried running

:'a,'b s/\=@s/\=@r/ | 'c,'d s/\=@s/\=@r/ | 'e,'f s/\=@s/\=@r/

This would have saved me having to rewrite the command line each time, but, alas, it didn’t work. The replace bit \=@r was fine, but the \=@s bit in the search pattern gave me an error.

Any tips would be appreciated.

ib.
  • 27,830
  • 11
  • 80
  • 100
Dave Doran
  • 311
  • 2
  • 5

7 Answers7

5

If you need to perform a set of line-wise operations (like substitutions) on a bunch of different ranges of lines, one trick you can use is to make those lines look different by first adding a prefix (that isn't shared by any of the other lines).

The way I usually do this is to indent the entire file with something like >G performed on the first line, and then use either :s/^ /X/ commands or block-visual to replace the leading spaces with X on the lines I want.

Then use :g in conjunction with :s. eg:

:%g/^X/s/FOO/BAR/g
:%g/^X/s/BAZ/QUUX/g

Finally, remove the temporary prefixes.

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • I see what you're at - putting a temporary mark in the first column for each line in my range. That's a great idea thanks. (Unfortunately it wouldn't work in this case as the text file was source code, and I wanted to compile as I went along.) – Dave Doran Jun 22 '12 at 20:21
  • 1
    You could have put an comment at every line ending to distinguish the code lines, which should not interfere with compilation. – epsilonhalbe Jun 22 '12 at 22:29
4

In order to get rid of the necessity to retype the same search pattern, substitution string and flags, one can simply use the :& command with the & flag:

:'a,'bs/pat/str/g | 'c,'d&& | 'e,'f&&

(See :help :& for details.)

ib.
  • 27,830
  • 11
  • 80
  • 100
  • :& is a new one on me. Thanks, it will come in very handy. – Dave Doran Jun 23 '12 at 13:27
  • @Dave: It is handy indeed. If I understand the question correctly, using the `:&` command solves the stated issue, doesn't it? – ib. Jun 23 '12 at 13:59
  • In the example I gave, yes it is perfect thanks. However, if I had many locations that I wanted to search I think that @eipson-2-halbe's answer might be more practical. Say I had 40 locations, then sourcing an external text file might be more manageable that putting 40 'xxx,'yyy&& entries on the command line. A combination of both of your methods is probably the way to go! – Dave Doran Jun 23 '12 at 15:01
  • @Dave: I see. By the way, are said locations completely arbitrary? Or they could be derived somehow from the surrounding text? – ib. Jun 23 '12 at 15:10
  • In this case it was easier to manually mark the boundries (or make a mental note of the line numbers). I could have written a regex to seek out the boundaries, but that would have been overkill. – Dave Doran Jun 23 '12 at 19:19
  • @Dave: I would recommend you to make particular details about that boundaries known. It may be that there is a simple method for automatically detecting them. – ib. Jun 24 '12 at 02:30
3

Instead of using marker use this one :

:68,104s/FOO/BAR/g << substitue from line 68 to 104

This should make your job a little bit easier and clearer.

Dzung Nguyen
  • 9,152
  • 14
  • 65
  • 104
3

inspired by @Vdt's answer:

I am not sure but you could write all the substitutions down in a file and source that file i think. substitutions.vim:

68,104s/FOO/BAR/g
168,204s/FOO/BAR/g
618,644s/FOO/BAR/g
681,1014s/FOO/BAR/g
.
.
.
68,104s/BAZ/BOOO/g
168,204s/BAZ/BOOO/g

and then :so substitutions.vim maybe you can also use this for multiple files of same structure. you can add an e to add an ignore error message, if it is not clear that the substitutions are found on the corresponding line blocks.

epsilonhalbe
  • 15,637
  • 5
  • 46
  • 74
2

With q:, you can recall previous command lines and edit them as a normal Vim buffer, so you can quickly replace FOO and BAR with something else, then re-execute the line with Enter.

The s/\=@s/\=@r/ doesn't work; as you said, this only works in the replacement part. But for the pattern, you can use Ctrl + R Ctrl + R s to insert the contents of register s, instead of \=@s. Preferably use the default register, then it's a simple s//, but you probably know that already.

Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
2

When performed over a closed fold, substitutions are limited to that fold.

  1. fold each region
  2. put the cursor on one closed fold
  3. perform the substitution: :s/foo/bar<CR>
  4. move to the next closed fold with zj or zk
  5. use the command-line history: :<C-p><CR> or :<Up><CR> to perform the same substitution
  6. repeat…

You can also add the c flag at the end of your substitution so that Vim asks you for a confirmation before actually performing it. This can be tedious if you have lot of matches.

romainl
  • 186,200
  • 21
  • 280
  • 313
1

Here's the simplest way to do it

:5,10s/old/new/g

5,10 : startlinenum,endlinenum