tail -F

I've been a fan of Unix since I was a little kid with no access to Unix. I'd read books about Unix, borrowed from the "big city" library I'd sometimes get to visit as a treat.

I've used many Unixes since, mainly Linux these days, but I'm old enough to have grown up on commercial Unixes and Plan 9. And although for years now I've found it unbearably frustrating to be stuck on a system that uses some non-GNU variants of commands (Mac OS, mainly, but I sometimes run into undead Solaris zombies), I'll be honest: this GNU stuff is still new to me.

Unlike many of my peers, it wasn't all the fancy extra options that drew me to GNU userspace; it was the absence of fixed-sized buffers. Those old Bell Labs guys loved their fixed-size buffers, and it was a constant pain in my ass that accidentally pushed me into using Perl: I just couldn't trust pre-GNU awk(1) or sed(1) or whatever to cope with "long" lines. (And don't imagine they'd cope gracefully, either. I'm talking buffer overflows, because who could possibly have a line longer than, say, 512 characters?)

One side effect of my non-conversion is that I'm generally ignorant of GNU extensions.

POSIX tail(1) has a -f option ('f' for 'follow') that does its usual job of showing you the last few lines of the file, but then keeps running so it can show you all new content appended to the file. This, as you probably know, is really useful, especially for log files.

My problem is that I keep restarting a program that creates a new log file each time it runs, with the timestamp encoded in the filename. It helpfully creates a symbolic link to the most recent log file, but traditional tail -f opens a file descriptor and, to implement the -f part of its functionality, keeps calling fstat(2) to see if the file pointed to by the file descriptor has changed size, and if it has, it copies the new content to stdout. This, obviously, doesn't work in my case, because I care if the symbolic link changes too, and want to start following the new file.

I finally got sick of "interrupt, up-arrow, return" yesterday, and was about to write a simple script when I realized I should check GNU tail doesn't already have such functionality. And, of course, it does: tail -F. This re-opens the original path if the fstat(2) shows that the file is a different file (recognizable by an inode or device number change).

Why are both useful? Well, you want tail -f if your path might be renamed or unlinked but you still want to read it (remember that an unlinked file continues to exist as long as some process has an open file descriptor for it). And you want tail -F if your path might be renamed or unlinked or replaced by a different symbolic link and you want to switch to reading whatever file (if any) comes along to replace it.

Personally, I think I'm going to start retraining my fingers to use -F because I think that's my more usual use case. YMMV. And apologies to the youngsters who already knew all this, but I'm pretty sure I'm not the only dinosaur still wandering the earth.