31

I'm writing a small shell script that needs to reverse the lines of a text file. Is there a standard filter command to do this sort of thing?

My specific application is that I'm getting a list of Git commit identifiers, and I want to process them in reverse order:

git log --pretty=oneline work...master | grep -v DEBUG: | cut -d' ' -f1 | reverse

The best I've come up with is to implement reverse like this:

... | cat -b | sort -rn | cut -f2-

This uses cat to number every line, then sort to sort them in descending numeric order (which ends up reversing the whole file), then cut to remove the unneeded line number.

The above works for my application, but may fail in the general case because cat -b only numbers nonblank lines.

Is there a better, more general way to do this?

kenorb
  • 155,785
  • 88
  • 678
  • 743
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • possible duplicate of [How can I reverse the order of lines in a file?](http://stackoverflow.com/questions/742466/how-can-i-reverse-the-order-of-lines-in-a-file) – tripleee Sep 28 '15 at 09:00
  • Not specific to reversing lines of a text file, but specifically for your case, you can print the commits in chronological order: `git log --reverse` – 13k May 17 '16 at 18:40

9 Answers9

57

In GNU coreutils, there's tac(1)

Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
32

There is a command for your purpose:

tail -r file.txt
  • Prints the lines of file.txt in reverse order!
  • The -r flag is non-standard, may not work on all systems, works e.g. on macOS.
  • Beware: Amount of lines limited. Works mostly, but when working with huge files be careful and check.
porg
  • 1,079
  • 1
  • 11
  • 17
  • 7
    the [POSIX specification for tail](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/tail.html) does not mention a `-r` option – Richard Hansen Feb 21 '12 at 04:50
  • I am using the tail supplied with Mac OS X 10.6. Its manpage is dated to the year 1993 (!) and mentions "The tail command appeared in PWB UNIX." So it definitely has been around for a while… though I don't know, whether it's POSIX compliant. – porg Jul 09 '12 at 15:32
  • 3
    The man page on OS X 10.7 says, "The tail utility is expected to be a superset of the IEEE Std 1003.2-1992 (``POSIX.2'') specification. In particular, the -F, -b and -r options are extensions to that standard." – Wes Jul 24 '12 at 04:22
  • @Wes: Ok ok! So it is an extension. But I hope it is useful to our questioner nevertheless. What do you say? – porg Jul 24 '12 at 07:56
  • Oh yeah certainly. I came here looking to do the same thing and tail -r is a great solution. Was just clarifying. – Wes Jul 24 '12 at 11:24
  • If it helped, I'd appreciate if you vote up my hint, so that I receive some reputation points. Thanks! – porg Nov 28 '12 at 17:46
  • 2
    I think the `-r` option is also available in BSD variants like FreeBSD, but not in gnu tail. – mc0e Aug 10 '16 at 06:22
  • 1
    just for information, the quantity of line is limited so does not work on big files/stream but very usefull on small and medium one. So just make a test if you work on a certain size of file – NeronLeVelu Jan 23 '19 at 09:35
  • 1
    This is NOT standard, you should tell on which systems it works, or at least do not say it is standard. – zezollo Sep 20 '22 at 13:33
24

Answer is not 42 but tac.

Edit: Slower but more memory consuming using sed

sed 'x;1!H;$!d;x'

and even longer

perl -e'print reverse<>'
Hynek -Pichi- Vychodil
  • 26,174
  • 5
  • 52
  • 73
3

Similar to the sed example above, using perl - maybe more memorable (depending on how your brain is wired):

perl -e 'print reverse <>'
Gavin Brock
  • 5,027
  • 1
  • 30
  • 33
2

In this case, just use --reverse:

$ git log --reverse --pretty=oneline work...master | grep -v DEBUG: | cut -d' ' -f1
OJFord
  • 10,522
  • 8
  • 64
  • 98
2
cat -b only numbers nonblank lines"


If that's the only issue you want to avoid, then why not use "cat -n" to number all the lines?

  • Good point, I guess I didn't read far enough through the man page to find -n. In any case, tac is what I really wanted. – Greg Hewgill Jan 20 '09 at 21:01
1
:   "@(#)$Id: reverse.sh,v 1.2 1997/06/02 21:45:00 johnl Exp $"
#
#   Reverse the order of the lines in each file

awk ' { printf("%d:%s\n", NR, $0);}' $* |
sort -t: +0nr -1 |
sed 's/^[0-9][0-9]*://'

Works like a charm for me...

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • O(N.log(N)) vs O(N) `tac` and `sed` = Less effective approach you can't think up? I think that there can be O(N!), try found it. – Hynek -Pichi- Vychodil Jan 07 '09 at 08:40
  • I use it a couple of times a month, typically on a up to a few hundred lines of code. It's fine for that. If I needed it every day, on gigabyte files, then I'd reconsider. I wrote it in 1989; I altered the ID string notation in 1997. It's stable code. And 'tac' is not standard on Solaris. – Jonathan Leffler Jan 07 '09 at 15:54
  • But there is `sed` on Solaris I guess, thus you can use more effective sed 'x;1!H;$!d;x' – Hynek -Pichi- Vychodil Jan 19 '09 at 10:52
  • Probably - I don't use it to process such large files that it matters that sort spills the data to disk if needed but the sed version crashes if there's no memory space left for it to grab. If it was a performance problem, I'd consider moving; it isn't (for me) so I'm probably too lazy to change. – Jonathan Leffler Jan 19 '09 at 23:23
  • It's true. sed is not cache data to disk as sort does. I don't know if tac is not affected with same bug ;-) – Hynek -Pichi- Vychodil Jan 20 '09 at 20:14
1
awk '{a[i++]=$0}END{for(;i-->0;)print a[i]}'

More faster than sed and compatible for embed devices like openwrt.

martian
  • 519
  • 1
  • 6
  • 16
0
rev <name of your text file.txt>

You can even do this:

echo <whatever you want to type>|rev
kcidkcus
  • 1
  • 1
  • This is a good answer to a different question. The `rev` command reverses the text within each line; the OP is looking for `tac`, which prints the last line first, then the second last, etc. – tripleee Dec 18 '18 at 14:02