828

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?

Good Person
  • 1,437
  • 2
  • 23
  • 42
Scotty Allen
  • 12,897
  • 9
  • 38
  • 51
  • 8
    Important note about reversing the lines: **make sure your file has a trailing newline** first. Otherwise, the last two lines of an input file will be merged into one line in an output file (at least using the `perl -e 'print reverse <>'` but it probably applies to other methods too). – jakub.g Sep 10 '15 at 14:58
  • 1
    possible duplicate of [How to reverse lines of a text file?](http://stackoverflow.com/questions/418280/how-to-reverse-lines-of-a-text-file) – Greg Hewgill Sep 28 '15 at 09:04
  • Also pretty nearly a duplicate (though older) of http://unix.stackexchange.com/questions/9356/how-can-i-print-lines-from-file-backwards-without-using-tac . As in that case, migration to unix.stackexchange.com is probably appropriate. – mc0e Aug 10 '16 at 06:18

24 Answers24

1711

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

Flipping one file into another

tac a.txt > b.txt
rodrigo-silveira
  • 12,607
  • 11
  • 69
  • 123
Mihai Limbășan
  • 64,368
  • 4
  • 48
  • 59
  • 80
    Especially worth mentioning to those using a version of tail with no -r option! (Most Linux folks have GNU tail, which has no -r, so we have GNU tac). – oylenshpeegul Apr 12 '09 at 21:48
  • 11
    Just a note, because people have mentioned tac before, but tac doesn't appear to be installed on OS X. Not that it'd be difficult to write a substitute in Perl, but I don't have the real one. – Chris Lutz Apr 12 '09 at 21:49
  • 5
    You can get GNU tac for OS X from Fink. You might wish to get GNU tail as well, as it does some things that BSD tail does not. – oylenshpeegul Apr 12 '09 at 22:00
  • 33
    If you use OS X with homebrew, you can install tac using `brew install coreutils` (installs as `gtac` by default). – Robert Nov 25 '13 at 21:46
  • 1
    To be more specific: tac oldfile > newfile – Eddy Mar 27 '14 at 16:46
  • Where can I get a complete list of all the coreutils? In the manual :) http://www.gnu.org/software/coreutils/manual/coreutils.html – tommy.carstensen Jan 22 '15 at 11:51
  • Is there any way to do this as an overwrite? i.e. `tac a.txt > a.txt` I don't want a new file, just want the current one flipped. – cadams Jul 28 '16 at 22:02
  • 8
    One of the problems is if the file doesn't have a trailing new line, the first 2 lines may be conjoined as 1 line. `echo -n "abc\ndee" > test; tac test`. – CMCDragonkai Apr 11 '17 at 12:58
  • If you use OS X with macports, you can install tac using port install coreutilrs – enridaga Dec 06 '17 at 21:46
  • The author does not mention anything about second file. We can conclude that the ideal solution should be to replace the contents in-place. e.g. using `ed/sed` – Marinos An Jul 08 '19 at 11:12
  • 2
    Importantly, you can also pipe in to tac: `cat a.txt | tac >b.txt` works! – Cornelius Roemer Sep 09 '21 at 17:22
  • Use `tac file | sponge file` if you want to change the file and don't want both versions. – 12431234123412341234123 Apr 27 '22 at 10:45
525

BSD tail:

tail -r myfile.txt

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

Cristian Ciupitu
  • 20,270
  • 7
  • 50
  • 76
Jason Cohen
  • 81,399
  • 26
  • 107
  • 114
  • 144
    Just remember that the '-r' option isn't POSIX-compliant. The sed and awk solutions below are going to work even in the wonkiest systems. – guns Apr 27 '09 at 07:59
  • (if there is a perl on that wonky system, that is :)) – Karel Bílek May 12 '11 at 02:37
  • 45
    Just tried this on Ubuntu 12.04, and discovered there is no -r option for my version of tail (8.13). Use 'tac' instead (see Mihai's answer below). – odigity Sep 21 '12 at 16:50
  • 18
    The checkmark should move below to tac. tail -r fails on Ubuntu 12/13, Fedora 20, Suse 11. – rickfoosusa Jan 31 '14 at 19:50
  • 3
    tail -r ~/1 ~ tail: invalid option -- r Try `tail --help' for more information. look like its new option – Bohdan May 05 '14 at 20:20
  • 9
    The answer should certainly mention that this is BSD-only, particularly since the OP asked for a "standard UNIX" utility. This isn't in GNU tail so it's not even a de facto standard. – DanC May 26 '14 at 01:27
  • @DenTheMan how come when I do this `tac text.txt > text.txt` it wipes out the files? – CMCDragonkai Jun 13 '14 at 07:52
  • 1
    On Mac Homebrew and perhaps some other environments this it's going to be gtac, not tac. – rfay Mar 21 '16 at 20:02
  • 1
    See https://www.gnu.org/software/coreutils/manual/html_node/tail-invocation.html#index-BSD-tail for further details of the `-r` option of tail – m13r Aug 05 '16 at 12:02
  • 3
    while useful to some, this is not an answer to the question as posed. i.e. it is not a "standard UNIX commandline utility". – mc0e Aug 08 '16 at 10:58
  • @CMCDragonkai you can't pipe data from a file to itself. You need to use a temporary file. – shadowtalker Dec 19 '16 at 17:54
  • 1
    When I tried piping into `tail -r`, it seems like the original last line is being concatenated with the original second to last line to form the new first line. Probably because the text stream doesn't have a newline at the end? – Terrence Feb 15 '17 at 23:35
  • I disagree that this isn't an answer to the question. BSD isn't as common as GNU these days, but it was once pervasive and is still common. BSD tail is the default on MacOS, at least in El Capitain. – Mars Jun 07 '17 at 20:38
  • reverse a file order is not the same as order it in alphabetical descending order – useless Oct 10 '17 at 15:00
  • Ubuntu 18 doesn't have tail with -r. – Shital Shah Apr 07 '20 at 21:01
  • Even on OS/X, `tail -r blah > blah` empties the file. Unlike `tac` below. – Rich Jun 03 '20 at 22:14
  • not posix compliant, so not commonly available on *nix systems. – Dev Null Mar 24 '21 at 21:35
  • This doesn't work on Ubuntu 22.04. – Raleigh L. Oct 18 '22 at 01:47
187

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...

iolsmit
  • 383
  • 2
  • 13
ephemient
  • 198,619
  • 38
  • 280
  • 391
  • 4
    From the same source: awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file* Both the sed and awk versions work on my busybox router. 'tac' and 'tail -r' do not. – guns Apr 27 '09 at 08:00
  • 8
    I wish this one is the accepted answer. coz sed is always available, but not `tail -r` and tac. – ryenus Nov 28 '12 at 02:43
  • @ryenus: `tac` is expected to handle arbitrary large files that do not fit in memory (line length is still limited though). It is unclear whether `sed` solution works for such files. – jfs Dec 19 '13 at 05:45
  • Only problem though : be prepared to wait :-) – Antoine Lizée Oct 03 '14 at 04:56
  • 1
    More precisely: the sed code is in O(n^2), and can be VERY slow for big files. Hence my upvote for the awk alternative, linear. I didn't try the perl option, less piping-friendly. – Antoine Lizée Oct 03 '14 at 05:03
  • Just wanted to point out the Perl version is quite piping friendly. `print` just prints to `STDOUT` so you can easily pipe the output or write it to a new file. Of course its worth mentioning that you will be placing the entire file in memory with this version. – jimpudar Apr 12 '18 at 04:06
  • The advantage of sed is that it can perform the reversal in-place with `-i` without making a temporary file. – fuzzyTew Sep 11 '22 at 08:24
  • that awk caches the whole file in mem. might not be good for large files. But good for smaller ones. – KFL Sep 28 '22 at 16:36
148

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 :-).

Dror
  • 2,370
  • 1
  • 18
  • 13
Yakir GIladi Edry
  • 2,511
  • 2
  • 17
  • 16
70

If you happen to be in vim use

:g/^/m0

Explanation from @Ronopolis below:

g means "do this globally.
^ means "the beginning of a line".
m means "move the line to a new line number. 0 is which line to move to.
0 means "top of the file, before the current line 1".
So: "Find every line that has a beginning, and move it to line number 0."

You find line 1, and move it to the top. Does nothing. Then find line 2 and move it above line 1, to the top of the file. Now find line 3 and move it to the top. Repeat this for every line. At the end you finish by moving the last line to the top. When you are done, you've reversed all the lines.

P Varga
  • 19,174
  • 12
  • 70
  • 108
DerMike
  • 15,594
  • 13
  • 50
  • 63
  • 5
    Related: [How to reverse the order of lines?](http://vi.stackexchange.com/q/2105/467) at Vim SE – kenorb Feb 23 '15 at 13:01
  • 4
    I'd vote that up if you briefly explained what it did. – mc0e Aug 08 '16 at 13:22
  • 2
    Yeah, I get that bit, but I meant breaking down what the various bits of the vim command are doing. I've now looked at the answer @kenorb linked, which provides the explanation. – mc0e Aug 10 '16 at 06:05
  • 9
    g means "do this globally. ^ means "the beginning of a line". m means "move the line to a new line number. 0 is which line to move to. 0 means "top of the file, before the current line 1". So: "Find every line that has a beginning, and move it to line number 0." You find line 1, and move it to the top. Does nothing. Then find line 2 and move it above line 1, to the top of the file. Now find line 3 and move *it* to the top. Repeat this for every line. At the end you finish by moving the last line to the top. When you are done, you've reversed all the lines. – Ronopolis Apr 19 '18 at 18:52
  • It should be noted that the :g global command behaves in a very particular way vs simply using ranges. For example, the command ":%m0" will not reverse the order of the lines, while ":%normal ddggP" will (as will ":g/^/normal ddggP"). Nice trick and explanation... Oh yea, forgot the token "see :help :g for more information"... – Nathan Chappell Feb 07 '19 at 10:24
61
tac <file_name>

example:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1
Daniel Alder
  • 5,031
  • 2
  • 45
  • 55
jins
  • 681
  • 6
  • 11
45
$ (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.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • 4
    Why not `tac myfile.txt` - what am I missing? – sage Jan 24 '13 at 17:43
  • 8
    @sage, to fall back to `tail -r` in case `tac` is not available. `tac` is not POSIX compliant. Neither is `tail -r`. Still not foolproof, but this improves the odds of things working. – slowpoison Apr 09 '13 at 20:55
  • I see - for instances when you are not able to manually/interactively change the command when it fails. Good enough for me. – sage Apr 10 '13 at 17:50
  • 3
    You need a proper test to see if tac is available. What happens if `tac` is available, but runs out of RAM and swap half way through consuming a giant input stream. It fails, and then `tail -r` succeeds in processing the remainder of the stream giving an incorrect result. – mc0e Aug 08 '16 at 13:25
  • @PetrPeller See answer above comment by Robert for OSX use homebrew. `brew install coreutils` and use `gtac` in place of `tac` and if your prefer add tac as an alias to `gtac` if for example you wanted a shell script that used it cross platform (Linux, OSX) – lacostenycoder Mar 28 '17 at 16:43
  • Worked for me in Git Bash – MrMesees May 28 '18 at 20:33
26

Try the following command:

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
kenorb
  • 155,785
  • 88
  • 678
  • 743
  • instead of the gawk statement, I'd do something like this: `sed 's/^[0-9]*://g'` – bng44270 Nov 21 '11 at 21:49
  • 2
    why not use "nl" instead of grep -n ? – Good Person Dec 04 '12 at 13:14
  • 3
    @GoodPerson, `nl` by default will fail to number empty lines. The `-ba` option is available on some systems, not is not universal (HP/UX comes to mind, though I wish it wouldn't) whereas `grep -n` will always number *every* line that matches the (in this case empty) regex. – ghoti Apr 17 '15 at 16:37
  • 2
    Instead of gawk I use `cut -d: -f2-` – Alexander Stumpf May 09 '16 at 16:17
17

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
konsolebox
  • 72,135
  • 12
  • 99
  • 105
  • 2
    +1 for answer in bash and for O(n) and for not using recursion (+3 if I could) – nhed Mar 08 '14 at 23:16
  • 3
    Try this with a file containing the line `-nenenenenenene` and witness the reason why people recommend always using `printf '%s\n'` instead of `echo`. – mtraceur Sep 15 '17 at 22:17
  • @mtraceur I would agree with that this time since this one's a general function. – konsolebox Oct 09 '17 at 12:43
16

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
lacostenycoder
  • 10,623
  • 4
  • 31
  • 48
11

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

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file
Vikdor
  • 23,934
  • 10
  • 61
  • 84
Tim Menzies
  • 641
  • 5
  • 14
11

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 
Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
Yekatandilburg
  • 187
  • 1
  • 2
  • 2
    not sure why this answer shows up before the one below, but it's a dupe of http://stackoverflow.com/a/742485/1174784 - which was posted years before. – anarcat Nov 13 '15 at 21:47
8

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.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Mark Booth
  • 7,605
  • 2
  • 68
  • 92
4

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]))")
Yauhen Yakimovich
  • 13,635
  • 8
  • 60
  • 67
3

This will work on both BSD and GNU.

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
R. Kumar
  • 103
  • 5
2

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.

driver
  • 21
  • 1
1

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.

Marius Hofert
  • 6,546
  • 10
  • 48
  • 102
1

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.

youkaichao
  • 1,938
  • 1
  • 14
  • 26
1

You may use Perl on the commandline:

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

0

Best solution:

tail -n20 file.txt | tac
Pang
  • 9,564
  • 146
  • 81
  • 122
ITNM
  • 33
  • 1
  • Welcome to Stack Overflow! While this code snippet may solve the question, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations! – kayess Dec 01 '16 at 08:37
  • The tools are documented: people have to `man tail`, `man tac` at some point rather than being spoonfed. +1 – Kaz Feb 02 '23 at 18:41
-1

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
dosentmatter
  • 1,494
  • 1
  • 16
  • 23
-1

Not sure if I missed something. How about pipe into sort

i.e. cat file | sort -r

Sorry if I missed the point of this question. I often use this to scan syslogs.

In linux : watch " tail /var/log/syslog | sort -r "

Hope it helps someone Best wishes

-7

tail -r works in most Linux and MacOS systems

seq 1 20 | tail -r

Bohdan
  • 16,531
  • 16
  • 74
  • 68
-10
sort -r < filename

or

rev < filename
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
  • 7
    `sort -r` only works if the input is already sorted, which is not the case here. `rev` reverses the characters per line but keeps the line order intact which is also not what Scotty asked for. So this answer is actually no answer at all. – Alexander Stumpf May 09 '16 at 16:22