← back to latest

Reverse the order of every line in the file

COMMAND:g/^/m0Reverse the order of every line in the file

anatomy

:
Enter command-line mode.
g
The global command. With no range it scans the whole file, marks every line that matches the pattern, then runs the command on each marked line in order from top to bottom.
/^/
The pattern. ^ is start of line, which every line has, blank ones included. This is the idiom for marking all lines in the file.
m0
The Ex move command with target line 0, meaning above the first line. Each marked line, taken in turn from top to bottom, moves to the top of the file and lands on top of the ones moved before it.

In the buffer

Before:

v1.0.0  first public release
^
v1.1.0  add JSON output
v1.2.0  drop Python 3.8
v1.2.1  fix crash on empty input
v1.3.0  add --quiet flag

After typing :g/^/m0:

v1.3.0  add --quiet flag
^
v1.2.1  fix crash on empty input
v1.2.0  drop Python 3.8
v1.1.0  add JSON output
v1.0.0  first public release

The oldest release is now at the bottom, and the cursor is on line 1. To see why the lines come out reversed, follow the moves. :g marks all five lines first, then visits them from top to bottom. v1.0.0 moves to the top, which leaves it where it was. v1.1.0 moves to the top and lands above v1.0.0. v1.2.0 lands above that. Each line ends up on top of the ones moved before it, so the last line visited finishes first.

When you would reach for it

You have a list in the wrong direction. Maybe a changelog with the oldest entry first, log output you want to read newest first, or a stack of notes you appended to the bottom for a week. Sorting does not help, because the order you want is “the opposite of what is here,” not alphabetical. :g/^/m0 reverses the lines in place without a shell and without a plugin, and it does so in any vi you are likely to find on a server.

Gotchas

  • :sort! is not the same thing. It sorts in descending order, which matches a reversal only when the lines were already sorted. On pear, apple, fig, :sort! gives pear, fig, apple, while :g/^/m0 gives fig, apple, pear. The changelog above is the trap: it was already in order, so both commands appear to agree.
  • If you add a range and keep m0, the lines in that range get reversed and moved to the top of the file. :2,4g/^/m0 on the buffer above puts lines 4, 3, 2 above line 1. To reverse a block in place, change the target to the line directly above the block: :2,4g/^/m1.
  • One u undoes the whole thing. :g counts as a single change even though it ran five moves, so you do not have to press u once per line.
  • ^ marks every line, blank ones included, so blank separator lines get reversed with everything else. A paragraph structure of text, blank, text, blank comes back as blank, text, blank, text if the file ends with a blank line.

Variants

:2,4g/^/m1

Reverses only lines 2 through 4 and leaves them where they were. The target is always the line immediately above the first line in the range.

:%!tac

Same result, but it filters the buffer through the external tac command. Fast on huge files. It needs tac on your system, though: GNU coreutils has it, and stock macOS does not.

:g/^/t.

The same :g loop with t. (copy below the current line) in place of m0. Every line gets duplicated directly beneath itself, which shows how :g visits each line in turn.

lineage

Both halves come from ed. Ken Thompson's ed already had g for global and m for move, and g/^/m0 reversed a file in ed long before vi existed. Bill Joy's ex kept both commands, vim kept ex, and the idiom has worked unchanged the whole way down. The vim user manual still uses it as its example in section 12.4, "Reverse line order".

-- INSERT --vim-hacks.com026-026-reverse-every-line-in-the-buffer.html
utf-81,1All