32

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?

2
  • possible duplicate of How can I reverse the order of lines in a file?
    – tripleee
    Commented Sep 28, 2015 at 9: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
    Commented May 17, 2016 at 18:40

9 Answers 9

60

In GNU coreutils, there's tac(1)

3
  • Examples of use thegeekstuff.com/2009/10/… Commented Feb 8, 2012 at 17:13
  • It is also available on MacOS but under the name gtac (install coreutils using brew first of course)
    – Cyberwiz
    Commented Jan 28, 2019 at 14:56
  • Oh god, that made me laugh so hard
    – gpoussif
    Commented Mar 6, 2023 at 1:22
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.
9
  • 7
    the POSIX specification for tail does not mention a -r option Commented Feb 21, 2012 at 4:50
  • 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
    Commented Jul 24, 2012 at 4:22
  • 2
    I think the -r option is also available in BSD variants like FreeBSD, but not in gnu tail.
    – mc0e
    Commented Aug 10, 2016 at 6: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 Commented Jan 23, 2019 at 9:35
  • 1
    This is NOT standard, you should tell on which systems it works, or at least do not say it is standard.
    – zezollo
    Commented Sep 20, 2022 at 13:33
25

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<>'
4
  • Cryptic, but that's what I was looking for. Thanks! Commented Jan 6, 2009 at 21:43
  • 2
    'tac' isn't cryptic: it's 'cat' in reverse. ;-) Commented Jan 6, 2009 at 23:14
  • 1
    That's true, but I sure wouldn't have guessed that without knowing beforehand. You could call the sort program "abc" which makes sense in the same way but wouldn't help somebody guess its name! Commented Jan 7, 2009 at 1:23
  • 1
    In msysgit, I don't have tac, but I do have sed. Thanks! Commented Nov 28, 2012 at 23:04
3

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

perl -e 'print reverse <>'
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?

1
  • 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. Commented Jan 20, 2009 at 21:01
2

In this case, just use --reverse:

$ git log --reverse --pretty=oneline work...master | grep -v DEBUG: | cut -d' ' -f1
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...

5
  • 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. Commented Jan 7, 2009 at 8: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. Commented Jan 7, 2009 at 15:54
  • But there is sed on Solaris I guess, thus you can use more effective sed 'x;1!H;$!d;x' Commented Jan 19, 2009 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. Commented Jan 19, 2009 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 ;-) Commented Jan 20, 2009 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.

0
rev <name of your text file.txt>

You can even do this:

echo <whatever you want to type>|rev
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
    Commented Dec 18, 2018 at 14:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.