user671162
user671162

Reputation: 51

Move last n lines of a text file to the top with Bash

How can I move last n lines of a text file to the top without knowing number of lines of the file? Is it possible to achieve this with single command line (with sed for example)?

From:

...
a
b
c
d

To:

a
b
c
d
...

Upvotes: 4

Views: 2544

Answers (3)

dibery
dibery

Reputation: 3470

Updated 2021:

Using vim's ex mode is even simpler: (inspired by this answer)

vim -        -esc '$-3,$m0|%p|q!' --not-a-term  # when processing a pipe
vim file.txt -esc '$-3,$m0|%p|q!' --not-a-term  # when processing a file to stdout
vim file.txt -esc '$-3,$m0|wq'    --not-a-term  # when editing a file inline

This will move last 4 lines (last line + 3 lines before the last line) to top. Change 3 to your desired number.

A MWE:

seq 10 | vim - '-esc$-3,$m0|%p|q!' --not-a-term

Output:

7
8
9
10
1
2
3
4
5
6

Updated 2020:

You may find it hard if the input source is from pipe. In this case, you can use

awk '{a[NR-1]=$0}END{for(i=0;i<NR;++i)print(a[(i-4+NR)%NR])}'

This will store all lines in memory (which may be an issue) and then output them. Change 4 in the command to see different results.


Display the last n lines and then display the rest:

tail -n 4 file; head -n -4 file

From man head:

-n, --lines=[-]NUM

print the first NUM lines instead of the first 10; with the leading '-', print all but the last NUM lines of each file

tail -n 4 will display the last 4 lines of a file.

If you wish to pipe this data, you need to put them together like this:

( tail -n 4 file; head -n -4 file ) | wc

Or maybe you can use vim to edit file inline:

vim +'$-3,$m0' +'wq' file

The + option for vim will run the (Ex) command following it. $-3,$m0 means move lines between 3 lines above the last line and the last line to the beginning of the file. Note that there should be no space between + and the command.


Or using commands in vim normal mode:

vim +'norm G3kdGggPZZ' file

G go to file end; 3k move up 3 lines; dG deletes to file end; gg go to file top; P pastes the deleted lines before this line; ZZ saves and quits.

Upvotes: 6

Jetchisel
Jetchisel

Reputation: 7831

It is trivial to do with ed

Move the last 30 lines on top of the file.

printf '%s\n' '30,$m0' ,p Q | ed -s file.txt

Move 10 to 50 lines on top of the file.

printf '%s\n' '10,50m0' ,p Q | ed -s file.txt

Move 1 to 10 to the last line/buffer.

printf '%s\n' '1,10m$' ,p Q | ed -s file.txt

Move 40 to 80 at the last line/buffer

printf '%s\n' '40,80m$' ,p Q | ed -s file.txt
  • Line address 0 is the first and m means move

  • $ is the last line of the buffer/file.

  • Change Q to w to actually edit the file.txt

Upvotes: 0

potong
potong

Reputation: 58488

This might work for you (GNU sed):

sed '$!H;1h;$!d;G' file

Append every line but the last to the hold space and then append the hold space to the the last line.

Upvotes: 1

Related Questions