Tail I lose
July 5, 2022 at 6:19 PM by Dr. Drang
I’ve been using the tail
command for about 25 years, so you might think I’d know something about it. But last week, as I was putting together a short shell script (not this one), I opened the man page for tail
and learned something new. Two things, actually, which surprised me, as tail
doesn’t really do that much.
In the following, assume we have a file called lines.txt
with 15 lines of text that look like this:
Line 01
Line 02
Line 03
Line 04
Line 05
Line 06
Line 07
Line 08
Line 09
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
The first thing I learned had to do with the -n
option. I’ve known forever that
tail -n 6 lines.txt
would return the last six lines:
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
I’ve also known that, for historical reasons, this can be shortened to
tail -6 lines.txt
These are probably the most common ways people use tail
and are analogous to the most common ways we use head
to get the first bunch of lines. I learned about tail
after learning head
and sort of assumed that tail
was—apart from its -f
option for following log files—just as elementary.1
But tail
has a trick up its sleeve: if you give the numerical argument to the -n
option a leading +
sign, the output will start that many lines from the beginning of the file and continue to the end. Like this:
tail -n +6 lines.txt
returns
Line 06
Line 07
Line 08
Line 09
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
This is really useful. If I wanted to process a file that had 5 lines of header information that I needed to skip over, tail -n +6
would be perfect as the first process in a pipeline. Too bad I didn’t know that a couple of decades ago.
How does tail
works if -n
is given a negative argument? Just as if the argument were unsigned.
tail -n -6 lines.txt
returns
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
just like
tail -n 6 lines.txt
This is mathematically perverse, but it fits well in the context of tail
. The normal use is to count back from the end of the file, so that’s what an unsigned argument does. Putting a sign in front of the argument is a directional signal, and it makes sense to use +
when counting from the beginning of a file and -
when counting from the end.
The second thing I learned is the -r
option. When used with no other options, it reverses the file.
tail -r lines.txt
returns
Line 15
Line 14
Line 13
Line 12
Line 11
Line 10
Line 09
Line 08
Line 07
Line 06
Line 05
Line 04
Line 03
Line 02
Line 01
which works more or less like the tac
command.2 Since tac
doesn’t come with macOS, you can use tail -r
as a replacement. I wouldn’t normally do this, since I have tac
installed on my system (it’s part of the coreutils
bundle in Homebrew), but it’s handy to have when writing a shell script—or a Shortcut or Keyboard Maestro macro—for someone who doesn’t feel comfortable installing command line tools.
You can combine -r
with -n
like this:
tail -r -n 6 lines.txt
returns
Line 15
Line 14
Line 13
Line 12
Line 11
Line 10
As you can see, the reversal takes place on the 6 lines extracted from the end of the file. It doesn’t reverse the lines and then take the last 6 after the reversal.
What about using the +
with -r
?
tail -r -n +6 lines.txt
returns
Line 15
Line 14
Line 13
Line 12
Line 11
To me, this result makes no sense. For consistency with the previous, non-signed, command, I would expect it to extract from Line 6 through the end and then reverse them.
The manual says
Additionally, this option [-r] changes the meaning of the -b, -c and -n options. When the -r option is specified, these options specify the number of bytes, lines or 512-byte blocks to display, instead of the bytes, lines or blocks from the beginning or end of the input from which to begin the display.
OK. That suggests the sign doesn’t matter, but clearly the sign does matter because we got different output when we included the +
. My takeaway is to avoid using -n
with -r
.
By the way, the GNU version of tail
—which, like tac
is part of the coreutils
bundle—bypasses this mess by simply having no -r
option. I’m going to use this as my excuse for not knowing about -r
. My formative Unix years were with Linux, which uses GNU utilities like the -r
less tail
. Because OS X/macOS is a descendant of BSD, its utilities are just a little different.