Robert Elder Software Inc.
  • Home
  • Store
  • Blog
  • Contact
  • Home
  • Store
  • Blog
  • Contact
  • #linux
  • |
  • #commandline
  • |
  • #softwareengineering
  • |
  • #embeddedsystems
  • |
  • #compilers
  • ...
  • View All >>

Intro To 'tac' Command In Linux

2023-08-02 - By Robert Elder

     I use the 'tac' command to print files in reverse order:

tac numbers.txt

The 'cat' Command Versus 'tac' Command

     Here, I have a file called 'numbers.txt':

one
two
three
four
five
six
seven
eight

     By default, the 'tac' command works just like the 'cat' command:

cat numbers.txt
one
two
three
four
five
six
seven
eight

     except the 'tac' command prints the lines in a file in reverse order:

tac numbers.txt
eight
seven
six
five
four
three
two
one

     Note that the word 'tac' is the word 'cat' spelled backwards.

An Example Use Case Of The 'tac' Command

     Here, I have a file called 'text-editors.txt' that contains the following text:

emacs
is better than
vim

     This statement is clearly incorrect.  I can use the 'tac' command to correct the statement in the file like this:

tac text-editors.txt
vim
is better than
emacs

     Now it correctly says 'vim is better than emacs' (which is a universally accepted fact).

Using A Custom Separator

     I can also use the '-s' flag to specify a custom separator, like a dash character, and the order of the items between the separators will be reversed:

echo -n "one-two-three-" | tac -s '-'
three-two-one-

Reversing Bytes In A File Doesn't Work

     The documentation of the 'tac' command states that you can use this regular expression based separator to reverse the order of all bytes in a file:

info tac
...
   Example:

     # Reverse a file character by character.
     tac -r -s 'x\|[^x]'
...

     On my machine, this example does correctly reverse the order of these bytes:

echo -en "\x00\x01" | tac -r -s 'x\|[^x]' | xxd
00000000: 0100                                     ..

     but it does not reverse the order of these bytes:

echo -en "\x80\x81" | tac -r -s 'x\|[^x]' | xxd
00000000: 8081                                     ..

Further Investigation Into Byte Reversing

     When I was writing this article, I expected the above 'byte reversing' example to 'just work' for all bytes, but as you can see it did not.  During my tests, I wrote the following script when testing the 'tac' command which I will include here:

for i in {1..100}
do
	head -c 2 /dev/urandom > $i.test;
	cat $i.test |
	tac -r -s 'x\|[^x]' |
	tac -r -s 'x\|[^x]' > $i.test.rev;
	md5sum $i.test >> before;
	md5sum $i.test.rev >> after;
done

     The above script will create 100 files with two random bytes in them.  For each file, it will use the 'tac' command to reverse the file twice.  The md5 hash of both the before and after files is calculated for every file.  Reversing a file twice should (in theory) return its contents to the original value, so the 'before' and 'after' files should have all the same hash values.  Using this 'vim' command, I compare the contents of these files:

vim -d before after

     And here is the result:

Hash Comparison

     Clearly, the 'tac' command is not an appropriate tool if you want to reverse all of the bytes in a file.

Why Doesn't 'tac' Correctly Reverse Bytes?

     Here were the inputs for the test cases that failed:

00000000: 6dcc                                     m.
00000000: 028d                                     ..
00000000: 7da2                                     }.
00000000: 78f2                                     x.
00000000: 3ed9                                     >.
00000000: 18b5                                     ..
00000000: 7089                                     p.
00000000: 50a2                                     P.
00000000: 22c0                                     ".
00000000: 1098                                     ..
00000000: 6bbf                                     k.
00000000: 5996                                     Y.
00000000: 5dce                                     ].
00000000: 62a4                                     b.
00000000: 7983                                     y.
00000000: 34df                                     4.
00000000: 5cd0                                     \.
00000000: 4cfb                                     L.
00000000: 0a8b                                     ..
00000000: 16db                                     ..
00000000: 70ae                                     p.
00000000: 61bf                                     a.
00000000: 5282                                     R.
00000000: 41cc                                     A.

     And here were the inputs for the test cases that passed:

00000000: fccb                                     ..
00000000: da2d                                     .-
00000000: 5b12                                     [.
00000000: 435f                                     C_
00000000: 3667                                     6g
00000000: a7eb                                     ..
00000000: f17b                                     .{
00000000: c4e2                                     ..
00000000: 3d2b                                     =+
00000000: c4d0                                     ..
00000000: 2d69                                     -i
00000000: 5e1c                                     ^.
00000000: ca24                                     .$
00000000: e6ff                                     ..
00000000: 8944                                     .D
00000000: 371e                                     7.
00000000: e6f6                                     ..
00000000: 96da                                     ..
00000000: 2f47                                     /G
00000000: b237                                     .7
00000000: b00a                                     ..
00000000: a2fd                                     ..
00000000: ee89                                     ..
00000000: d856                                     .V
00000000: 3327                                     3'
00000000: b52f                                     ./
00000000: 520d                                     R.
00000000: a4e2                                     ..
00000000: cedd                                     ..
00000000: b0fa                                     ..
00000000: f6c1                                     ..
00000000: f0ca                                     ..
00000000: 8921                                     .!
00000000: 8eea                                     ..
00000000: ab77                                     .w
00000000: ff99                                     ..
00000000: 3d0b                                     =.
00000000: a4d4                                     ..
00000000: bd92                                     ..
00000000: 8d37                                     .7
00000000: 284e                                     (N
00000000: b9b2                                     ..
00000000: 9c28                                     .(
00000000: e463                                     .c
00000000: 1e03                                     ..
00000000: 889b                                     ..
00000000: 6c6a                                     lj
00000000: 97bb                                     ..
00000000: 7a1f                                     z.
00000000: e5d0                                     ..
00000000: 8681                                     ..
00000000: bf82                                     ..
00000000: 1801                                     ..
00000000: 040c                                     ..
00000000: 4b09                                     K.
00000000: 8c2d                                     .-
00000000: eb9c                                     ..
00000000: 8e1c                                     ..
00000000: f7f9                                     ..
00000000: 4b57                                     KW
00000000: ba7d                                     .}
00000000: 1053                                     .S
00000000: 9772                                     .r
00000000: eb48                                     .H
00000000: f978                                     .x
00000000: c1b3                                     ..
00000000: e038                                     .8
00000000: d01c                                     ..
00000000: 9cb7                                     ..
00000000: d9de                                     ..
00000000: 193a                                     .:
00000000: 3015                                     0.
00000000: 6340                                     c@
00000000: 978a                                     ..
00000000: 84a2                                     ..
00000000: 9a0e                                     ..

     The common theme above appears to be that the failing cases had an ASCII character in the first byte and a non-ASCII character in the second byte.  Perhaps this is a bug?  Or, perhaps this is expected behaviour (or potentially undefined behaviour) in the flavour of regular expression that the 'tac' command uses?  I am not sure, and I've decided that I don't really care.

     And that's why the 'tac' command is my favourite Linux command.

Intro To 'stty' Command In Linux
Intro To 'stty' Command In Linux
Published 2023-10-04
Terminal Block Mining Simulation Game
$1.00 CAD
Terminal Block Mining Simulation Game
Intro To 'nproc' Command In Linux
Intro To 'nproc' Command In Linux
Published 2023-07-15
Intro To 'comm' Command In Linux
Intro To 'comm' Command In Linux
Published 2023-09-06
How To Force The 'true' Command To Return 'false'
How To Force The 'true' Command To Return 'false'
Published 2023-07-09
A Surprisingly Common Mistake Involving Wildcards & The Find Command
A Surprisingly Common Mistake Involving Wildcards & The Find Command
Published 2020-01-21
A Guide to Recording 660FPS Video On A $6 Raspberry Pi Camera
A Guide to Recording 660FPS Video On A $6 Raspberry Pi Camera
Published 2019-08-01
Intro To 'chroot' Command In Linux
Intro To 'chroot' Command In Linux
Published 2023-06-23
Join My Mailing List
Privacy Policy
Why Bother Subscribing?
  • Free Software/Engineering Content. I publish all of my educational content publicly for free so everybody can make use of it.  Why bother signing up for a paid 'course', when you can just sign up for this email list?
  • Read about cool new products that I'm building. How do I make money? Glad you asked!  You'll get some emails with examples of things that I sell.  You might even get some business ideas of your own :)
  • People actually like this email list. I know that sounds crazy, because who actually subscribes to email lists these days, right?  Well, some do, and if you end up not liking it, I give you permission to unsubscribe and mark it as spam.
© 2025 Robert Elder Software Inc.
SocialSocialSocialSocialSocialSocialSocial
Privacy Policy      Store Policies      Terms of Use