tags:

views:

350

answers:

7

How to do it in Vim:

BEFORE:

aaa
bbb
ccc
ddd
eee
fff

AFTER:

aaa
ccc
eee
+3  A: 

You should be able to use a macro:

http://www.oreillynet.com/mac/blog/2006/07/more%5Fvim%5Fsave%5Ftime%5Fwith%5Fmacros%5F1.html

Mark Wilkins
+18  A: 

You can use a macro for this. Do the following.

  • Start in command mode.
  • Go to the beginning of the file by pressing gg.
  • Press qq.
  • Click arrow down and press dd after.
  • Press q.
  • Press 10000@q

PS: To go to command mode just press Escape a couple of times.

ruibm
Assuming your file is less than 20,000 line long of course :-)
paxdiablo
+! to diablo, That's important otherwise you will go on deleting lines.
Michael Krelin - hacker
Actually you are wrong Michael. This works flawlessly as VI will stop running the macro as soon as it hits end of file. You can very easily try it out with the sample above.
ruibm
rui, my bad. twice as bad, actually - I also misread paxdiablo's comment. But, honestly, I still strongly dislike your answer for being unnecarily "complex" wrt keystrokes and non-universal.
Michael Krelin - hacker
Well, like any macro you need to get it right the first time but after that you can apply to any file by just typing 10000@q. Either way I believe it's just a matter of tastes so I'm definitively not going to start a philosophical debate. :)
ruibm
I prefer to use 'macro'. Using macro, you 'describe' how you would solve one particular task and let VIM repeat it for you. RUI, I would prefer to use 'j' instead of arrow key.
SolutionYogi
Lol a valid point SolutionYogi I did think about that (cursors might not work if VI isn't configured properly), it's just that I never really got used to using h,j,k,l (this might sound like heresy). :)
ruibm
rui, definitely. I actually revoked my downvote which was based on my mistake, not taste diffrences. I couldn't revoke it before because you had to edit your answer to allow it ;-) And yes, if I were to do it your way I'd also use 'j'. After all arrow escape sequence is so huge :) (I wonder is there a difference while *executing* recorded macro, not recording it?)
Michael Krelin - hacker
Solution Yogi, actually, you can describe it in different ways. Like OP did - "delete every second row" (and this answer matches it perfectly) or "delete all even/odd rows". And it matters how you put it. That's why I think eloquence is one of the most important things for programmers. (and yes, I know I'm in danger now as I'm not writing my native language).
Michael Krelin - hacker
To run a macro reliably across all lines of a file regardless of length you can use the :norm command:- 1,$norm! @q The norm runs normal mode commands from the command line and allows you to specify a line range which in this case is the entire file.
Andrew O'Reilly
+6  A: 

from vim mail archive:

:let i=1 | while i <= line('$') | if (i % 2) | exe i . "delete" | endif | let i += 1 | endwhile

(To be typed on one line on the vim command line, will delete row 1,3,5,7,...)

Fortega
Then I'd prefer `!perl -ne 'print unless $. % 2'` :-)
Alex Brasetvik
Alex, we think the same.
slim
slim you think exactly the opposite!! (if vs. unless)
Michael Krelin - hacker
And, Alex, I'd definitely prefer my solution to yours, but this one worth considering since this is the only sensible solution using only vim itself here.
Michael Krelin - hacker
+5  A: 

You can always pipe though a shell command, which means you can use any scripting language you like:

:%!perl -nle 'print if $. % 2'

(or use "unless" instead of "if", depending on which lines you want)

slim
You don't need the `-l` switch to Perl... also, I'd say that `awk` is a little more widespread than Perl, though it's not really that much more these days, and can do this shorter: `:%!awk NR\%2`, or `:%!awk 1-NR\%2` for the other lines.
ephemient
+2  A: 
:%!awk -- '++c\%2'

alternatively

:%!awk -- 'c++\%2'

depending on which half you want to weed out.

Michael Krelin - hacker
Umm, why not use the existing `NR` variable?
ephemient
Right. Because I forgot about it. ;-) But actually, the "proper" variant will be even longer with NR. And there would be no two *nice-looking* counterparts.
Michael Krelin - hacker
`:%!awk NR\%2` or `:%!awk 1-NR\%2`, as I posted in another comment. That's hardly longer.
ephemient
`1-NR` is longer than `c++`. Yes, I know it's one character. Like I said — the real reason is that I forgot about it. Anyway, looks like people do not appreciate the beauty of awk here.
Michael Krelin - hacker
And `NR` is shorter than `++c` :)
ephemient
;-) True, that makes total the same. But like I said - I like the fearful symmetry of counterparts.
Michael Krelin - hacker
+1  A: 

You can use Vim's own search and substitute capabilities like so: Put your cursor at the first line, and type in normal mode:

:.,/fff/s/\n.*\(\n.*\)/\1/g
  • The .,/fff/ is the range for the substitution. It means "from this line to the line that matches the regex fff (in this case, the last line).
  • s/// is the substitute command. It looks for a regex and replaces every occurrence of it with a string. The g at the end means to repeat the substitution as long as the regex keeps being found.
  • The regex \n.*\(\n.*\) matches a newline, then a whole line (.* matches any number of characters except for a newline), then another newline and another line. The parentheses \( and \) cause the expression inside them to be recorded, so we can use it later using \1.
  • \1 inserts the grouped newline and the line after it back, because we don't want the next line gone too - we just want the substitution mechanism to pass by it so we don't delete it in the next replacement.

This way you can control the range in which you want the deletion to take place, and you don't have to use any external tool.

Daniel Hershcovich
Or `$` instead of `/fff/`, for end-of-file regardless of the file's contents.
ephemient
A: 
:map ^o ddj^o
^o

Here ^ stand for CTRL. Recursive macro to delete a line every two line. Choose well your first line and it's done.

Raoul Supercopter