If you saw yesterday’s Command of the Day about head then you prolly saw this coming. This is the command you’ll commonly see when reading constant output from a file or from a log. Hell, the heroku CLI has a --tail option for logs that continues to display output. It is by far one of my most commonly used commands. Especially when I’m doing server work.


As head views the top of the file, tail shows the bottom. The copyrite header of tail.c show’s 1989. I’m assuming that they were designed and released around the same time. A common author between them is one David MacKenzie.


There is definaetly some usage commonality between head and tail. They both default to 10 lines of output; you can specify the number of lines displayed; you can specify the number of bytes; and can use zero-terminated input. The table below shows some of the common flags you’ll see.

option long-form description
-c --bytes output the last x bytes
-f --follow continute to output as file gets bigger
-F same as --follow=name --retry
-n --lines output the last x lines
--pid usues -f; stopes when pid is killed
--retry keep trying to output file
-z --zero-terminated line delimiter is NUL, not newline


The default output.

tail ~/.zshrc

# zsh-completions
fpath=($HOME/.zsh/zsh-completions $fpath)
fpath=($fpath $HOME/.zsh/func)
typeset -U fpath

# Prompt #
source $HOME/.zsh/zsh-drt-prompt/drt-prompt.zsh

Similar to head -n -x you can use tail -n +x to display all except the first x lines of data. If it works out in your favour to use +x instead of -y. For instance the following two commands produce the same output with my .vimrc file.

tail -n 9 .vimrc
tail -n +85 .vimrc
"nerdtree config
map <C-n> :NERDTreeToggle<CR>
let NERDTreeShowHidden=1                                                     "show hidden files
let NERDTreeIgnore=['\.swp']                                                 "ignore swap files
let NERDTreeMapOpenInTab='<ENTER>'                                           "open files from NERDTree in new tab
autocmd VimEnter * if argc() == 0 && !exists("s:std_in") | NERDTree | endif  "open a NERDTree automatically when vim starts up if no files were specified

"javacomplete2 config
autocmd FileType java setlocal omnifunc=javacomplete#Complete

Using the -f flag allows you to follow files as data is being written to it. Try running tail -f /var/log/system.log. You’ll see some data but the console is still waiting. As data is written to the log, it will continue to output. Let’s execute a shutdown command by executing shutdown -h +20. Now we should see it written to the system.log. You can abort the shutdown by running kill -9 PID where PID is the process id for the shutdown command. It would have been displayed in the terminal after you ran the previous command.

tail -f /var/log/system.log
Feb 26 19:26:15 localhost login[838]: DEAD_PROCESS: 838 ttys003
Feb 26 19:26:16 localhost timed[78]: settimeofday({0x5e570ca8,0xb2e00}) == 0
Feb 26 19:27:32 localhost syslogd[38]: ASL Sender Statistics
Feb 26 19:27:32 localhost login[862]: USER_PROCESS: 862 ttys003

***System shutdown message from user@localhost***
System going down in 6 minutes

The --pid option is a pretty interesting concept. It’s best used in conjunction with the -f option. This wiill constantly read a particiular file while PID is currently available. If for whatever reason you needed to block and you have the PID of a long running process, you can use tail --pid. Here’s a trivial example. I’ll read from /dev/null to keep the output to nothing.

sleep 20 &
[1] 1010
tail --pid=1010 -f /dev/null
[1]  + done       sleep 20

Lastly, let’s look at the NULL terminated option. In this completely random scenerio, a program returns a bunch of numbers that are NULL terminated. The output of that program is 9930045001073 and you need to know the last number that was outputted to the terminal. Is it 3? 73? 1073? 5001073?

echo -ne $'\0'99$'\0'300$'\0'4$'\0'500$'\0'10$'\0'73$'\0' | tail --zero-terminated -n 1

It’s rare for me to see strings NULL terminated these days, but there are times where it might be necessary. C strings are terminated by the \0 character. You might find it in the wild someday, and on that day, you’ll be ready.


Going into system administration? Mostly likely you’ll be getting pretty comfortable using tail -f. Hopefully the next script you use will use some handy tricks involving head and tail.