Bash History
Importance: 4 | # | bash, shell, mine
TLDR
Tired of losing your command history? Add this to your .bashrc
:
# Ensure history is appended, not overwritten on exit
shopt -s histappend
# After every command, append new history lines from this session to the history file
export PROMPT_COMMAND="history -a"
# Manually sync in new history lines from the file (added by other sessions)
alias s='history -n'
Now you won't lose your history in case your system powers-off unexpectedly or your bash process crashes. And you can use s
to sync histories from other shell sessions into the current shell.
I use tmux (oh-my-tmux) with bash along with fzf. ctrl-r is an indispensible feature - how are you going to get anything done if you cannot access your command history without friction.
More recently I've noticed that the utility of ctrl-r has gone down significantly for me. It never seems to work!? The commands from any session (not the tmux session - I mean a single instance of a shell) only seem to be available within the session and I magically lose the history sometimes. I put off looking into this for a while now, and I've finally found some time.
Bash History Storage
So what happens when you press up-arrow or use ctrl-r? Where's it stored? How's it synced between shell sessions (sometimes).
There's two levels of storage
- in-memory buffer
- specific to each running shell process (bash)
- accessible with
history
or ⬆️ or Ctrl-R - lost when shell crashes
- persistent file
- shared at the user level (usually ~/.bash_history)
- like a shared archive
- written when a shell closes cleanly
The in-memory buffer per shell session is appended each time a command is run. This buffer can be accessed with history
, and this is also how ⬆️ and ctrl-r are implemented.
But a new shell session's history does not start out empty. It's loaded from the persistent file - usually ~/.bash_history
. By default, ~/.bash_history
is read once at the initialization of shell session and written when the shell exits cleanly. It takes the new commands in history
and appends to ~/.bash_history
.
Since I use tmux, you can see how this would not be great for me. Roughly I think of my shell sessions in two categories -
- short term - quick stuff, execute a few commands, exit
- long term - kept running for weeks/months
Once created the long term sessions will not recieve any commands from other sessions and will also not write to ~/.bash_history
until they are closed - so these commands are not available in my short-terms sessions. And although the short-term commands are written out, it's usually not read by long-term sessions as they are usally initialized before most short sessions.
Solving sync
history
provides a couple of useful options -
history -a
to write out new commands from the current session to~/.bash_history
history -n
to read new commands from~/.bash_history
to the current session's in-memory buffer
Running these manually would be tedious, so add the following to ~/.bashrc
:
shopt -s histappend
- this is to ensure that commands are appended to
~/.bash_history
rather than overwritten
- this is to ensure that commands are appended to
export PROMPT_COMMAND="history -a"
PROMPT_COMMAND
is run each time a fresh command prompt is shown to you. If you type in a command and press enter, or just hit enter with an empty command -PROMPT_COMMAND
is executed and~/.bash_history
is updated.- Once updated, it could be read in by other shell processes.
- History saved after each command
alias s='history -n'
- an alias to sync commands from other sessions to the current session.
- you could add
history -n
toPROMPT_COMMAND
as well, but it isn't great for me - I expect up-arrow, ctrl-p/n to work only on the current session's commands. This behaviour would be lost if history is synced every chance there is. So I instead like to do the sync manually as and when necessary.
So now I type away commands on different shells and only use the current session's history until I need commands from other sessions - in which case I sync with a s
.
Additional Options
You can specify a few additional options:
export HISTSIZE=10000
- This tells bash the maximum number of commands in the in-memory history
export HISTFILESIZE=20000
- Maximum commands in
~/.bash_history
- Maximum commands in
export HISTCONTROL=ignoredups:erasedups
- Avoids duplicate commands
export HISTFILE=~/.my_bash_history
- If you want to write the history to some file other than
~/.bash_history
- If you want to write the history to some file other than
The sourcecode for bash contains the implementation for history and everything discussed above and it's less complicated than one might expect. In case you're interested, most of the stuff related to history can be found in lib/readline/hist*.c/h
and builtins/history.def