A high-performance, cross-platform tac rewrite

If you haven’t heard of tac, it’s a pretty nifty command-line utility that ships with the GNU utils and it’s used to print a file backwards, line-by-line. It’s especially useful when analyzing things like log files, and judicious use of tac can speed up commands considerably.

Take the example of a 30GiB webserver access log and you want to see the last request to a certain resource or that triggered a particular HTTP status code. You could run the following to get the last such request… which would take quite awhile on anything larger than a few hundred MiB:

> egrep "GET /path/to/resource " access.log | tail -n1

Or you could be smart about it and use tac instead, and not even have time to blink before the result comes back:

> tac access.log | egrep "GET /path/to/resource " | head -n1

The only problem is that tac isn’t readily available on all systems; it’s oftentimes just Linux machines that have access to this handy utility. Even on other posix systems like FreeBSD, installing coreutils (the parent package for tac) doesn’t give you access to tac as you’d expect. So what do you do?

The answer, of course, is to write your own. We’re pleased to announce the immediate availability of a safe, high-performance, and cross-platform tac port that runs on Windows, Linux, FreeBSD, macOS, and more.

For the developers in the crowd, here’s a real tickler: what’s the most efficient way of searching for the last occurrence of a character in a document then print from there to the end of the file, without loading the entire thing into memory? The natural approach would be to buffer the last n bytes of the file, n bytes at a time, searching for the character you want to find. But what happens when you go through an entire n-byte buffer and don’t find what you’re looking for? Do you just load the next n bytes to find your starting point, then… load the buffer you just discarded in order to print its contents to the terminal?

Of course not. You used memory-mapped files and leave the whole thing to the OS to micromanage. In exchange, you get a high-performance view of the file, you get to pretend that you have its entire contents loaded into memory without chunking/buffering, and you get to search its contents at will, jumping as you please from index to index without a care in the world. And you do so knowing that you’re not taxing system resources beyond their limits, because (hopefully!) the operating system is smart enough to page the contents of the file in and out of memory as needed to balance the requests of both your application and whatever else may be running on the machine at the same time.

So without any further ado, we present to you our high-performance, cross-platform rewrite of tac, unencumbered by the GPL v2/v3,1 freely available for download for both personal and commercial use:

Download tac 0.2 (GitHub)

tac is written in rust,2 and has no requirements apart from the latest version of the rust compiler.3 Unfortunately we are not able to provide binaries for all platforms at this time, but precompiled versions for select ABI-stable platforms are provided below. Package maintainers are, of course, welcome to build the binary for distribution for their respective platforms.

The precompiled tac binary for Windows is digitally signed with a Microsoft authenticode certificate.

There’s no guarantee that the precompiled versions above represent the latest version of the software; for best results you should compile your own copy of tac, if at all possible.

Image credit: Ben Davis, RO

  1. tac is MIT-licensed and open-source. 

  2. I bet you’re surprised to find a tool written in rust that didn’t make it a point to let you know before you’d finished reading the title! 😁 

  3. rustc 1.19.0 finally provides the eprintln!() macro, which we’ve taken to using liberally. 

Leave a Reply

Your email address will not be published. Required fields are marked *