0

original below. I want to copy each line and paste multiple n times under their own lines separately.

USD
XYZ
ABC
YUT
LMO
.
.
. upto so on

want them to become like this. Any way how can I achieve following in Vim?

USD
USD
USD
USD
USD
XYZ
XYZ
XYZ
XYZ
XYZ
ABC
ABC
ABC
ABC
ABC
YUT
YUT
YUT
YUT
YUT
LMO
LMO
LMO
LMO
LMO
.
.
. upto so on
1
2

Use a tool that makes it easy:

%!awk '{ <c-r>=repeat('print;', 5)<cr> }'

Here I’m using awk with an action to print the current line multiple times, which I created by invoking vim’s repeat function and inserting into the command using the expression register.

This is scalable: you can use a range to select a subset of lines (although for a single line yy{count}p is faster), and it’s adjustable (change the repeat parameter). It is limited to systems where you have awk, though.

Alternately, with an awk-loop:

%!awk '{ for (i = 0; i < 5; ++i) print; }'

More approaches: https://unix.stackexchange.com/questions/81904/repeat-each-line-multiple-times

I can’t resist this very terse polyglot:

%!awk '<c-r>=repeat('1;', 5)<cr>'
4
2

Here is another solution using the :g command.

You can use:

g/^/t.|t.|t.|t.

This basically means mark each line start, then run the :t (copy line) command on it. It uses the . to copy it below and then does the same again 4 times (but need to be separated by |.

The advantage of this command is, you can easily do it only for lines that match a certain pattern. So perhaps you only want to run this command for all lines that are not empty?

g/./t.|t.|t.|t.

(Empty lines would not match the . regular expression).

Or perhaps you want to copy all lines, except for comment lines (assuming those start with a #). In that case, we have to use the :g! or :v command, that runs the following command on all lines that do not match the regular expression:

v/^#/t.|t.|t.|t.

Or you want to only copy each line once:

g/^/t.
1

Following is not very scalable but simple enough for smaller lists: "start recording macro: yank word, insert it in new line below and repeat last ex command n times, stop macro recording. Repeat macro on every word"

  • qq (start recording macro, first q for starting recording, second q is name of the register)
  • yiw (yank word under cursor)
  • :put (paste it on next line without exiting normal mode)
  • 3@: (repeat last ex command three times)
  • q (stop recording macro)
  • j (move to next line)
  • @q (call recorded macro)
  • repeat last 2 steps while necessary
4
  • Simpler would be yy4p4j for the recorded part. You also say "repeat last 2 steps" but you only need to repeat the last one, which is running the macro, with @q. Also good to point out that you can repeat it with @@ and you can also use a count, such as 5@@. Unfortunately this one requires an exact count to work, you can't just use 999@@ since it won't break when it gets to the end of the file (which is sometimes useful when using a macro...)
    – filbranden
    Oct 7 '20 at 17:51
  • 1
    You are absolutely right - there are definitely other and arguably simpler methods to achieve objective. What I like most in Vim is the ability to combine simple pieces you know and achieve desired result without consciously thinking about it. It may not be the best solution but good enough for me because it's based on my immediate knowledge of VIm. In this case I was carried away with thinking about word instead of line :-). It is my understanding that in my particular solution 2 last steps must be repeated (using j to navigate to next line with new word and then apply macro) . Oct 7 '20 at 18:27
  • Ah, I just noticed that you put the movement j after stopping the macro recording. If you invert those two, then you can simply repeat the whole macro and be positioned where it should be executed next. That's very useful, because j@q is hard to type and you can't repeat it with a number, while 5@q or 5@@ to run it 5 times is an easier way to go through a file with hundreds or thousands of lines...
    – filbranden
    Oct 7 '20 at 18:47
  • 1
    As I pointed out earlier this solution is not scalable nor simple. This is something I would do for 5-10 repeats without thinking. In such small scale any quick-and-dirty solution is more time efficient than looking for perfect solution. Thank you for drawing my attention to my overly complicated thought process and for showing simpler solution. Every day one should learn something new and in the future when facing similar situation I will choose better path :-) (and no, j@q is not hard to type and yes, 5@q is much easier way) Oct 7 '20 at 19:24
1

You can use a :substitute command matching the whole line, including the line break at the end (\n) and then use an expression in the replacement, using the repeat() function together with submatch() to interpolate the contents of the matched line.

:%s/.*\n/\=repeat(submatch(0), 5)
1
  • 1
    Agh, I knew there was an elegant way to do it. Nice
    – D. Ben Knoble
    Oct 7 '20 at 18:58
1

I am new to this as well, so my suggestion is:

  • In normal mode:

    1. qa: start recording in a registry
    2. yy: copy the whole line
    3. 5p: paste it 5 times (if 5 is the target number of duplication)
    4. 5j: go down 5 times
    5. q: stop recording
  • I would then undo changes with u, and the just do a:

    150@a: if there is 150 lines you want to do the macro on

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.