How can I reverse the order of lines in a file?

ShellUnixCommand Line

Shell Problem Overview


I'd like to reverse the order of lines in a text file (or stdin), preserving the contents of each line.

So, i.e., starting with:

foo
bar
baz

I'd like to end up with

baz
bar
foo

Is there a standard UNIX commandline utility for this?

Shell Solutions


Solution 1 - Shell

Also worth mentioning: tac (the, ahem, reverse of cat). Part of coreutils.

Flipping one file into another
tac a.txt > b.txt

Solution 2 - Shell

BSD tail:

tail -r myfile.txt

Reference: FreeBSD, NetBSD, OpenBSD and OS X manual pages.

Solution 3 - Shell

There's the well-known sed tricks:

# reverse order of lines (emulates "tac")
# bug/feature in HHsed v1.5 causes blank lines to be deleted
sed '1!G;h;$!d'               # method 1
sed -n '1!G;h;$p'             # method 2

(Explanation: prepend non-initial line to hold buffer, swap line and hold buffer, print out line at end)

Alternatively (with faster execution) from the awk one-liners:

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file*

If you can't remember that,

perl -e 'print reverse <>'

On a system with GNU utilities, the other answers are simpler, but not all the world is GNU/Linux...

Solution 4 - Shell

at the end of your command put: | tac

tac does exactly what you're asking for, it "Write each FILE to standard output, last line first."

tac is the opposite of cat :-).

Solution 5 - Shell

If you happen to be in vim use

:g/^/m0

Solution 6 - Shell

tac <file_name>

example:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1

Solution 7 - Shell

$ (tac 2> /dev/null || tail -r)

Try tac, which works on Linux, and if that doesn't work use tail -r, which works on BSD and OSX.

Solution 8 - Shell

Try the following command:

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"

Solution 9 - Shell

Just Bash :) (4.0+)

function print_reversed {
    local lines i
    readarray -t lines

    for (( i = ${#lines[@]}; i--; )); do
        printf '%s\n' "${lines[i]}"
    done
}

print_reversed < file

Solution 10 - Shell

For cross OS (i.e. OSX, Linux) solution that may use tac inside a shell script use homebrew as others have mentioned above, then just alias tac like so:

Install lib

For MacOS

brew install coreutils

For linux debian

sudo apt-get update
sudo apt-get install coreutils 

Then add alias

echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt

Solution 11 - Shell

The simplest method is using the tac command. tac is cat's inverse. Example:

$ cat order.txt
roger shah 
armin van buuren
fpga vhdl arduino c++ java gridgain
$ tac order.txt > inverted_file.txt
$ cat inverted_file.txt
fpga vhdl arduino c++ java gridgain
armin van buuren
roger shah 

Solution 12 - Shell

I really like the "tail -r" answer, but my favorite gawk answer is....

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file

Solution 13 - Shell

EDIT the following generates a randomly sorted list of numbers from 1 to 10:

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**

where dots are replaced with actual command which reverses the list

tac

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)

python: using [::-1] on sys.stdin

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(python -c "import sys; print(''.join(([line for line in sys.stdin])[::-1]))")

Solution 14 - Shell

If you want to modify the file in place, you can run

sed -i '1!G;h;$!d' filename

This removes the need to create a temporary file and then delete or rename the original and has the same result. For example:

$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$

Based on the answer by ephemient, which did almost, but not quite, what I wanted.

Solution 15 - Shell

This will work on both BSD and GNU.

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename

Solution 16 - Shell

I see lots of interesting ideas. But try my idea. Pipe your text into this:

> rev | tr '\n' '' | rev | tr '' '\n'

which assumes that the character '~' is not in the file. This should work on every UNIX shell going back to 1961. Or something like that.

Solution 17 - Shell

For Emacs users: C-x h (select the whole file) and then M-x reverse-region. Also works for only selecting parts or the lines and reverting those.

Solution 18 - Shell

It happens to me that I want to get the last n lines of a very large text file efficiently.

The first thing I tried is tail -n 10000000 file.txt > ans.txt, but I found it very slow, for tail has to seek to the location and then moves back to print the results.

When I realize it, I switch to another solution: tac file.txt | head -n 10000000 > ans.txt. This time, the seek position just needs to move from the end to the desired location and it saves 50% time!

Take home message:

Use tac file.txt | head -n n if your tail does not have the -r option.

Solution 19 - Shell

You may use Perl on the commandline:

perl -e 'my @b=(); while(<>) {push(@b, $_);}; print join("", reverse(@b));' orig > rev

Solution 20 - Shell

Best solution:

tail -n20 file.txt | tac

Solution 21 - Shell

You can do it with vim stdin and stdout. You can also use ex to be POSIX compliant. vim is just the visual mode for ex. In fact, you can use ex with vim -e or vim -E (improved ex mode). vim is useful because unlike tools like sed it buffers the file for editing, while sed is used for streams. You might be able to use awk, but you would have to manually buffer everything in a variable.

The idea is to do the following:

  1. Read from stdin
  2. For each line move it to line 1 (to reverse). Command is g/^/m0. This means globally, for each line g; match the start of the line, which matches anything ^; move it after address 0, which is line 1 m0.
  3. Print everything. Command is %p. This means for the range of all lines %; print the line p.
  4. Forcefully quit without saving the file. Command is q!. This means quit q; forcefully !.

# Generate a newline delimited sequence of 1 to 10
$ seq 10
1
2
3
4
5
6
7
8
9
10

# Use - to read from stdin.
# vim has a delay and annoying 'Vim: Reading from stdin...' output
# if you use - to read from stdin. Use --not-a-term to hide output.
# --not-a-term requires vim 8.0.1308 (Nov 2017)
# Use -E for improved ex mode. -e would work here too since I'm not
# using any improved ex mode features.
# each of the commands I explained above are specified with a + sign
# and are run sequentially.
$ seq 10 | vim - --not-a-term -Es +'g/^/m0' +'%p' +'q!'
10
9
8
7
6
5
4
3
2
1
# non improved ex mode works here too, -e.
$ seq 10 | vim - --not-a-term -es +'g/^/m0' +'%p' +'q!'

# If you don't have --not-a-term, use /dev/stdin
seq 10 | vim -E +'g/^/m0' +'%p' +'q!' /dev/stdin

# POSIX compliant (maybe)
# POSIX compliant ex doesn't allow using + sign to specify commands.
# It also might not allow running multiple commands sequentially.
# The docs say "Implementations may support more than a single -c"
# If yours does support multiple -c
$ seq 10 | ex -c "execute -c 'g/^/m0' -c '%p' -c 'q!' /dev/stdin

# If not, you can chain them with the bar, |. This is same as shell
# piping. It's more like shell semi-colon, ;.
# The g command consumes the |, so you can use execute to prevent that.
# Not sure if execute and | is POSIX compliant.
seq 10 | ex -c "execute 'g/^/m0' | %p | q!" /dev/stdin

How to make this reusable

I use a script I call ved (vim editor like sed) to use vim to edit stdin. Add this to a file called ved in your path:

#!/usr/bin/env sh

vim - --not-a-term -Es "$@" +'%p | q!'

I am using one + command instead of +'%p' +'q!', because vim limits you to 10 commands. So merging them allows the "$@" to have 9 + commands instead of 8.

Then you can do:

seq 10 | ved +'g/^/m0'

If you don't have vim 8, put this in ved instead:

#!/usr/bin/env sh

vim -E "$@" +'%p | q!' /dev/stdin

Solution 22 - Shell

rev
text here

or

rev <file>

or

rev texthere

Solution 23 - Shell

tail -r works in most Linux and MacOS systems

seq 1 20 | tail -r

Solution 24 - Shell

sort -r < filename

or

rev < filename

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionScotty AllenView Question on Stackoverflow
Solution 1 - ShellMihai LimbășanView Answer on Stackoverflow
Solution 2 - ShellJason CohenView Answer on Stackoverflow
Solution 3 - ShellephemientView Answer on Stackoverflow
Solution 4 - ShellYakir GIladi EdryView Answer on Stackoverflow
Solution 5 - ShellDerMikeView Answer on Stackoverflow
Solution 6 - ShelljinsView Answer on Stackoverflow
Solution 7 - ShellDigitalRossView Answer on Stackoverflow
Solution 8 - ShellDGView Answer on Stackoverflow
Solution 9 - ShellkonsoleboxView Answer on Stackoverflow
Solution 10 - ShelllacostenycoderView Answer on Stackoverflow
Solution 11 - ShellYekatandilburgView Answer on Stackoverflow
Solution 12 - ShellTim MenziesView Answer on Stackoverflow
Solution 13 - ShellYauhen YakimovichView Answer on Stackoverflow
Solution 14 - ShellMark BoothView Answer on Stackoverflow
Solution 15 - ShellR. KumarView Answer on Stackoverflow
Solution 16 - ShelldriverView Answer on Stackoverflow
Solution 17 - ShellMarius HofertView Answer on Stackoverflow
Solution 18 - ShellyoukaichaoView Answer on Stackoverflow
Solution 19 - ShellGeorg FischerView Answer on Stackoverflow
Solution 20 - ShellITNMView Answer on Stackoverflow
Solution 21 - ShelldosentmatterView Answer on Stackoverflow
Solution 22 - Shelluser13575069View Answer on Stackoverflow
Solution 23 - ShellBohdanView Answer on Stackoverflow
Solution 24 - ShellYoker RekoyView Answer on Stackoverflow