# The Tac Command
by Seth Kenlon

The ``tac`` command is the [cat](https://opensource.com/article/19/2/getting-started-cat-command) command in reverse.
Its purpose is to concatenate files in reverse.
Like ``cat``, it has a convenient fallback mode to print to **standard out** if no output file is provided, making it one of those commands that is more often used as a lazy pager, like ``less`` and ``more``, than the function it is named for.

The ``cat`` command is often overused and abused, and ``tac`` is often taken as a joke command like ``ddate`` or ``cowsay``.
It often gets paraded out on April Fool's Day posts detailing stupid terminal tricks.
So it may come as a surprise that ``tac`` actually has a legitimate reason to exist.
It's actually a useful command.

## Print a file in reverse

The ``tac`` man page does a rather poor job of describing its own function:

```
Write each FILE to standard output, last line first.
```

Taking that as it's written, ``tac`` should print the last line of a file, then print the file starting back at line 1.

```
$ cat metasyntactic.list
foobar
foo
bar
baz

$ tac metasyntactic.list
baz
foobar
foo
bar
```

That's not what it does, though.
Its info page is much clearer:

```
copies each FILE (‘-’ means standard input), or standard input if none are given, to standard output, reversing the records (lines by default) in each separately.
```

For example:

```
$ tac metasyntactic.list
baz
bar
foo
foobar
```

Ignoring the fact that ``tac`` gives you everything in reverse, it has a few surprisingly useful and unique options.

## Separators

As the info page indicates, the file doesn't have to be delimited by line, though, meaning that tac is equally as effective with, for example, a CSV file.
You define a file's separator character with the ``--separator`` or ``-s`` option, along with the delimiter used in the file.
For a CSV file, the character is probably a comma (``,``) but you can define any character.

If a file doesn't terminate with the separator character, then you get an unexpected result:

```
$ tac --separator="," metasyntactic.csv
bazbar,foo,foobar
```

The first two items aren't separated by the separator character.
This is because the final record of the file (the string following te final separator, in this case a comma) is not itself followed by a comma, so it's treated as a non-record by ``tac``.
To account for this, use the ``--before`` or ``-b`` option, which places the separator character before each record:

```
$ tac --separator="," --before metasyntactic.csv
baz,bar,foo,foobar
```

The separator character doesn't have to be a single character.
It can also be a regular expression.

## Regex

A full explanation of regex is out of scope for this article, but it's worth mentioning that extended [POSIX](https://opensource.com/article/19/7/what-posix-richard-stallman-explains) is supported by means of an [environment variable](environment-variable-bash article).
Extended regex greatly enhances the readability of a regular expression, and for the sake of simplicity that's what this example uses.

Assumie you have a file containing strings separated predictably by integers:

```
$ cat metasyntactic.txt
foobar123foo456bar789baz898
```

You can reliably predict that the strings you care about are separated by integers, but you cannot reliably predict what those integers will be.
That's exactly the problem regex is meant to solve.
To use regex in your ``tac`` command, use the ``--regex`` or ``-r`` option before your ``--separator`` definition.
Also, unless it's already set in your environment, you must activate the ``REG_EXTENDED`` environment variable.
You can set this variable to anything but 0 to activate it, and you can do that in all the usual ways: you can export the variable for the shell session you're using, you can set it in your shell configuration file (such as ``~/.bashrc``, or you can prepend it to the ``tac`` command (in Bash, Zsh, and similar).

```
$ REG_EXTENDED=1 tac --regex \
--separator='[0-9]+' metasyntactic.txt
89baz898bar765foo432foobar1
```

The regex option doesn't handle non-terminated records well, even using the ``--before`` option, so you may have to adjust your source file if that's important to you.


## Use cases

These simple yet extremely useful parsing options make ``tac`` worth using as an uncomplicated, minimalist parsing command.
For those simple jobs that aren't quite worth writing an ``awk`` or ``perl`` expression for, ``tac`` just might be a sensible solution.

The ``tac`` command is limited, obviously, because it doesn't manipulate records in any way aside from reversing them.
But sometimes that's the only list manipulation you need.
For instance, if you're packaging up software for distribution, it's not unusual to have a list of dependencies that are required for installation.
Depending on how you gathered this list, you may have the list in the order you established that they were required instead of the order in which they must be installed.
This is relatively common, because compiler errors hit the high-level dependencies first.
That is, if your system is missing ``libavcodec`` then GCC stops and alerts you, but since it hasn't gotten a chance to probe your system for ``libvorbis`` and ``libvpx``, for example, it can't tell you that those are also missing (and, often, required to exist on your system before compiling ``libavcodec``).
So your list of dependency grows, in top-down form, as you discover what libraries your system needs to build the libraries that the libraries need (and so on).
At the end of such a process, ``tac`` is the quick and easy way to reverse that list.

Another common annoyance are log files.
Entries are generally appended to a log file, so admins use ``tail`` to see the latest errors.
That works well, but there are times you want to see a "chunk" of entries without knowing how far back you need to go.
The ``tac`` command piped to ``less`` or ``more`` puts the latest entries at the top of your screen.

Finally, many configuration files have no clear termination marker for a given section.
You can look up very good ``awk`` and ``sed`` commands to devise a way to determine when a block in a config file ends, or you can use ``tac`` to reverse the order such that once your parser has found the first relevant entry in that block, it also knows when to *stop* reading because what used to be the header is now a footer.

## tac

There are plenty of other great uses for ``tac``, and probably a bunch of reasons that ``tac`` is too rudimentary to be a solution.
Your system likely has it installed, however, so remember it the next time you find that edge case in your workflow that really really needs to be at*tac*ked in reverse.