11

I want to create a basic Vim script that simply deletes n lines, skips 1 line and repeats until the end of the document. I don't care about specific matches, I just want to delete the line. For example; So I want to repeat the command 3ddj for the entire document.

Text I want to keep.   
Text I want to delete.  
Text I want to delete.  
Text I want to delete.  
Text I want to keep.  
Text I want to delete.  
Text I want to delete.  
Text I want to delete.  
Text I want to keep.  
Text I want to delete.  
Text I want to delete.  
Text I want to delete.   

So it looks like...

Text I want to keep.  
Text I want to keep.  
Text I want to keep.  

Thank you.

3
  • You included the tag regular-expression on your question, but the question does not seem to specify a reason to do this with regular expressions. If you are actually looking for something that does use regular expressions, stackoverflow.com/questions/17735289/… may be of interest. That question was about Notepad++, but the answer uses nothing but a pcre regex. (Disclaimer: I wrote that answer.)
    – Jasper
    Commented Sep 25, 2019 at 14:01
  • Next time you ask a question, there's no need to write "Thanks for this. Great solution!" on every single answer. This is even written out when you try to create a new comment on an answer.
    – pipe
    Commented Sep 25, 2019 at 17:08
  • To add to that, the proper way to show appreciation here is to accept the answer that worked best for you and upvote any answers that you deem helpful or useful. People would much rather see that than a "thanks" comment.
    – B Layer
    Commented Feb 26, 2020 at 5:59

6 Answers 6

20

Very simple approach:

  1. Move to the first line you want to delete.
  2. Record a macro: qa3ddjq
  3. Repeat it with a high number: 1000@a

Step three will repeat the macro a thousand times or until an error is encountered. Hitting end of file (hence no lines to delete) produces an error and repetition of the macro is canceled.

See :help recording.

2
  • 1
    explanations: q (in command mode, and in vim or other recent alternate versions [neovim, etc], but not in the original vi) means "start recording, and put it in the 'next letter' buffer". hence: qa means "start recording into the 'a' buffer". Then every keys typed after this (until the next q in command mode) are recorded (you can even record: going into edit mode, typiong something, Escaping to command mode, etc). Then 12@a would repeat whatever actions were recorded in buffer "a" 12 times. Commented Sep 24, 2019 at 13:28
  • 4
    Additionally, if you don't want to to run something like 1000@q at the end, you can make a recursive macro: qa3ddj@aq@a, although you might need to do qaq first to make sure "a starts out empty.
    – DJMcMayhem
    Commented Sep 24, 2019 at 15:14
18

The easiest solution to me would be:

:%norm j3dd

That is:

  • %: for every line
  • norm: run the following keys as if in normal mode
  • j3dd: go down one line then delete 3 lines

So from the first line, go down to the second one and delete the next 3 lines. The second Text I want to keep. is now on the second line. Go down one line, delete 3. Rinse and repeat.


Note: if you don't want to process the entire file you can replace the %norm with e.g 5,$norm to process from the 5th line to the end of the file.

1
  • 4
    Very nice alternative! Works in vim (not vi). I never heard about "norm" before! (and I am kind of a vi[m] geek) Commented Sep 24, 2019 at 13:33
6

You have some great solutions already available. Here is another one:

:g/^/if line('.')%4!=1|:s/^/DELETE ME/|endif
:g/^DELETE ME/d

First, we perform an action on every line (:g matching against the ^ (start of line)) and for every line number perform the result of linenumber % 4. If the result is unequal to 1, we add DELETE ME at the beginning of the line, marking the lines to be deleted by the next command.

The next command is a regular (:g) command, that matches all lines that start with DELETE ME and does so.

Note, You cannot immediately delete the line using the first :g command, since this would change the resulting modulo calculation and as such such, we have to first mark the lines and in a second step delete it.

0
4

I know this is the vi channel, but to me this is a sed problem.

sed -ne 'p;n;n;n' <file >newfile

So you can wrangle it into a vi solution:

:0
!Gsed -ne 'p;n;n;n'
0
3

An alternative (I suppose we start on the line 1, and delete the lines 2-4, 6-8 and so on, as per an example text):

while line(".") < line("$")
    silent +1delete _ 3
endwhile

If prefer doing this interactively, you can make use of the "command-line register". That is, press

:+1del 3<CR> and then 1000@:

0
1

an alternative: you want to keep any lines: 1, 4, etc. You could use:

awk '!((FNR-1)%3)'  file > newfile
      # awk: when a condition is given without action:
      #        prints the lines for which the condition returns 0.
      # FNR designates the line number in the current file.
      # a%3 : takes the modulus of a and 3. ( 0%3=0, 1%3=1, 2%3=2, 3%3=0, 4%3=1, etc.) 
      # This will be 0 for 3, 6, etc. 
      # But we want it to be 0 (true) for 1, 4, etc : so we substract 1 to NR

# to see that it keeps those lines:
seq 1 20 | awk '!((NR-1)%3)'
1
4
7
10
13
16
19

another alternative: if you know the lines you want match some unique pattern :

# for exemple each line you want contains "some pattern":
fgrep "some pattern" file > newfile
# (or regular grep if you want "some pattern" to be interpreted as a regexp)
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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