I can remove the last 10 lines like this:

ex -sc '-9,d|x' f.txt

However, I would like to invert the selection, or remove all lines except the last 10. Is this possible?


Here is a solution which comes from here. Note that might not be exactly what you're looking for since it's implies to use a visual (or normal) mode mapping instead of just an ex command.

The idea is to create the following mapping which will allow you to keep in your buffer only the visually selected lines:

vnoremap <key> :<C-U>1,'<-1:delete<CR>:'>+1,$:delete<CR>

The mapping can be decomposed like this:

  • : enter ex mode.
  • <C-U> empty the lines from the first one to the current one.
  • 1,'<-1 Specify the range from the first line of the file to the line before the start of current selection.
  • :delete<CR> delete the lines previously sectioned by the range.
  • :'>+1,$ create a range from the end of the selection to the end of the line/
  • :delete<CR> delete the new range.

You can then visually select the last 10 lines (or whatever lines you want to keep) and press <key> used in the mapping to delete all the line not selected.

Now based on this solution I have created the following function:

function! InvertRange(command) range
    execute (a:lastline+1) . ",$ " . a:command
    execute "1," . (a:firstline-1) . " " . a:command

You should provide a command as argument and a range and the function will apply this command on the lines which are not in the range. Note that here the range under the selection is treated first otherwise if you provide "delete" as argument the line position will change and the deletion will not happened on the line you wanted.

An example call of the function is

:20,30 call InvertRange("delete")

Which will delete all lines excepted the ones between the 20th and the 30th.

Finally as a bonus here is a command that you can use to call the InvertRange function more easily:

command! -range -nargs=1 -complete=command IR <line1>,<line2>call InvertRange(<f-args>)

The command accept a range (if no one is provided it takes the current line) and allows the completion on the ex commands. Thus you can use:

:20,30 IR delete

To call the InvertRange function with delete as a command to execute and the range 20,30 to invert.

  • Thanks for the answer, but you completely disregarded the Ex mode requirement in the question – Steven Penny Apr 13 '16 at 12:13
  • 2
    @StevenPenny You're totally right the answer isn't applicable with the ex mode that's what I meant by the second sentence of my answer (after the Note). I think that could be useful to other users ending on a question titled "invert range selection". – statox Apr 13 '16 at 12:18
  • Maybe you could use silent! to take into account edge cases such as 1,2 and 7,$, but if you do so the range will always be swapped (see :h 493) and this could delete the fist line with a range such as 1,2. – Jair López Apr 13 '16 at 19:55
  • @JairLopez: My code was just a hint and wasn't extensively tested so you're right using silent could be an improvement but the fact that the range is swapped with no warning could be a drawback in some cases. I think one should adapt it to fit his needs :-) – statox Apr 14 '16 at 8:41

Here is a workaround:

ex -sc '1,-10d|x' f.txt
  1. 1,-10 select all but last 10 lines

  2. d delete

  3. x save and close

Your Answer

