I have a file with the following pattern:

foo 111
baz
foo 222
baz
foo 333
baz

which needs to be turned into

foo 111
bar 111
baz
foo 222
bar 222
baz
foo 333
bar 333
baz

So, duplicate all the lines beginning with foo, and change only foo in the duplicate but leave the rest of the line (which differs in each case) intact. How to do this?

  • If you aren't hell-bound on a regexp, a macro will do just fine. Doesn't really matter much in simple cases like this, but if you have something a bit more complicated it's a very valid alternative. – PhilippFrank Apr 14 '16 at 13:31
up vote 19 down vote accepted

Here you go:

:g/foo/t.|s//bar

Decomposing:

:g/foo/    " start a global command applied on all lines matching 'foo'
t.         " duplicate the current line (the cursor is now on the new line)
|          " chain a new command
s//bar     " substitute the last searched element with 'bar'

Because the g command will update the search pattern, so you can omit the pattern to replace in the substitute command. (ref: :h :g, search for search pattern).


Older version:

:g/foo/norm! yyp:s/foo/bar^M

Decomposing:

:g            start a global command
/foo/         apply only on lines having 'foo'
norm!         execute a normal command
yyp           duplicate the line
:s//bar       replace foo with bar on the line (the duplicated one)
^M            add enter to execute the substitution

To insert the ^M press Ctrl+v and enter.

Note: I originally came up with the "older" version, before I learned about the t command. I'll leave it but I won't recommend using it. The first one is cleaner more straightforward.

  • Solved, the 'simpler version' worked perfectly. The first version however, resulted in the duplicate line still beginning with foo.... – LB7979 Apr 14 '16 at 13:23
  • strange, did you entered ^M as I explained ? – nobe4 Apr 14 '16 at 13:24
  • My bad, didn't read that well. First solution works as well! – LB7979 Apr 14 '16 at 13:25
  • I don't know why you bother including the complicated version at all. The simpler version is...simpler. ;) (Not to mention being POSIX compliant.) Why not put it first? – Wildcard Apr 15 '16 at 0:05
  • 1
    Initially I came up with the first version, which was more natural to me, because at the time I was not aware of t, and then I added the second one. I'll change the order, you are right. How about the first one being POSIX ? – nobe4 Apr 15 '16 at 5:54

I do stuff like this all the time and just do something like %s/^foo \(.*\)/foo \1\rbar \1/ (partially because I also do a lot of similar tasks that can't be turned into something clever using a copy function, and \(.*\) is part of my muscle memory by now).

I suspect it's not POSIX-compliant (it doesn't work on other vi clones), but using ^M (C-VReturn) instead of \r seems to work on most other vi clones.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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