8

Occasionally I want to move all lines in a file to the top of that file. :g/PATTERN/m0 almost does what I want, but because :g operates one line at a time in order, it will also reverse the affected lines.


Example:

Consider this file. Say I want to separate lines containing a number and move them to the top of the file. :g/\d/m0 does almost what I want (fr4nk, car0l and b0b are moved to the top) -- but it reverses the order of the matched lines.

alice
b0b
car0l
dan
eve
fr4nk

Actual output:

fr4nk
car0l
b0b
alice
dan
eve

Desired output:

b0b
car0l
fr4nk
alice
dan
eve

One way of doing what I want is to use :g/PATTERN/m$ (which won't reverse lines) and then move the lines from the bottom of the file to the top. Is there anything simpler?

1
  • 7
    well, if nothing else works, you can always run the command twice, or type @:
    – Mass
    Sep 2 at 2:39
21

Instead of moving the target lines up (and reversing them), move all the non-target lines down. Since lines are processed top-to-bottom, the order of non-target lines will be preserved. Also, the target lines will remain in order at the top of the buffer.

:v/\d/m$

:v is the opposite of :g, it applies to all lines not matching a pattern.

2
  • That's really awesome, and seems very powerful. Where is the related documentation? :help v shows a lot of information, but it doesn't seem to described the above command. All I could find is vim.fandom.com/wiki/Power_of_g Sep 6 at 12:45
  • 1
    @EricDuminil :help :v which refers to :g! in the previous paragraph, where it is described.
    – Mass
    Sep 6 at 13:22
4

It's not as easy as I would like it to be, but you can do something like this:

:let @a='' | exe 'g/\d/d A' | 0put a

The breakdown is that it clears register a, executes a delete of all matching lines into register a (uppercase appends to the register instead of replacing it), then puts the contents of register a before the first line of the file.

1
  • If I were doing this, I'd probably just run the :global command in command-line mode, and perform the first and third steps with normal mode commands: qaq, and gg"aP
    – Rich
    Sep 2 at 13:25
3

One more possibility :)

Add an empty line to the top. This will mark that line with the . mark. Then use the :g command and move all lines below that mark. Since the :g is making changes, the '.' mark will be updated to point to the latest moved line (and finally remove that empty line again).

0put=[]|g/\d/m '.
1d
2

Here is another solution which is more complex. On the other hand, it leaves a nice separator between the target lines and the rest of the buffer.

:call append(0, '####')|1mark a|g/\d/m'a-1

It works like this:

  1. add a new line at the top of the buffer.
  2. create a mark called a on the first line (the newly added one)
  3. move matching lines before mark a

One thing to understand about vim marks is that when lines are added above, vim adjusts the mark's position lower. So address 'a-1 always refers to the line right above the #### line.

1

Personally, I would go with the :v approach mentioned by @Mass. Here is another solution. Move the matching lines to the bottom of the file and afterwards move those lines to the top:

exe 'g/\d/m$' | '.,$m0

You need to use exehere, because the | is otherwise seen as argument to the :g command. It makes use of the . mark for moving those lines of the latest change.

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.