3 min read

On Tailing the History

I was watching a demo the other week, and the presenter was tailing his .bash_history file in real time to log all the commands he was inputting into another session.

I thought this was pretty cool, and I wondered how it was done. My understanding was that the history program only wrote the to the file when the shell exited, but here I could see that it was writing to the file as soon as the chosen command returned.

Here is the command:

$ tail -n 0 -f ~/.bash_history

However, when I entered that into a new session and inputted some commands into another, nothing was printed to stdout in the session where I ran tail.

What’s going on? How can I get that to work?


What’s going on?

By design, the commands entered into a shell are only written to the history file when the shell is exited. So, it’s necessary to write to the history file in real time instead of waiting until logging out.

If the terminal session crashes before exit, any commands that you had entered are never written to disk and are lost!

How can I get that to work?

It’s actually quite simple. In short, the following statements need to be present in .bashrc or in a file sourced from it:

shopt -s histappend
export PROMPT_COMMAND="history -a"

The first statement is to shopt, which sets and unsets shell options. This sets the histappend shell option to append to the history file (HISTFILE) rather than overwriting it.

From the Bash man page:

histappend
                      If set, the history list is appended to the file
                      named by the value of the HISTFILE variable when
                      the shell exits, rather than overwriting the file.

By default, HISTFILE is $HOME/.bash_history.

For instance, in my environment, I’m using the default value and not setting a custom location. The first command below verifies that the value of HISTFILE isn’t set in my environment, and the second echoes the default value:

$ printenv | ag histfile\b
$ echo $HISTFILE
/home/btoll/.bash_history

The second statement exports the PROMPT_COMMAND environment variable. Bash will execute any values it finds in this variable, with the result being exactly what I want: it will write the command immediately to HISTFILE rather than waiting for the shell to exit. This has the effect of the entered command being written to stdout by the tail command that is following the .bash_history file.

Of course, this only takes effect after sourcing .bash_profile (or similar) or logging out and back in again.

$ . ~/.bash_profile`
$ shopt histappend
histappend      on
$ echo $PROMPT_COMMAND
history -a

Finally, it’s working as I’d like it to.

Why do this at all?

I’d like to capture each command as I enter it and write it to a file. Then, I can send that to another person or another thing or the New York Times, or I can attach those commands to an article I’m writing for my website.

This is the magical incantation:

$ tail -n 0 -f ~/.bash_history | tee history.cap

Conclusion

You can find more history options by entering the following command into a terminal:

$ help history
or
$ history --help

References