How to make the command line history apply across all terminals
Solution 1:
Thanks to the link provided by gertvdijk above I am posting my favorite solution from it.
Here is my attempt at Bash session history sharing. This will enable history sharing between bash sessions in a way that the history counter does not get mixed up and history expansion like !number will work with some constraints.
Using Bash version 4.1.5 under Ubuntu 10.04 LTS (Lucid Lynx).
HISTSIZE=9000
HISTFILESIZE=$HISTSIZE
HISTCONTROL=ignorespace:ignoredups
history() {
_bash_history_sync
builtin history "$@"
}
_bash_history_sync() {
builtin history -a #1
HISTFILESIZE=$HISTSIZE #2
builtin history -c #3
builtin history -r #4
}
PROMPT_COMMAND=_bash_history_sync
Explanation: The function history() overrides the builtin history to make sure that the history is synchronised before it is displayed. This is necessary for the history expansion by number (more about this later).
Append the just entered line to the $HISTFILE (default is .bash_history). This will cause $HISTFILE to grow by one line.
Setting the special variable $HISTFILESIZE to some value will cause Bash to truncate $HISTFILE to be no longer than $HISTFILESIZE lines by removing the oldest entries.
Clear the history of the running session. This will reduce the history counter by the amount of $HISTSIZE.
Read the contents of $HISTFILE and insert them in to the current running session history. this will raise the history counter by the amount of lines in $HISTFILE. Note that the line count of $HISTFILE is not necessarily $HISTFILESIZE.
More explanation: Step 1 ensures that the command from the current running session gets written to the global history file.
Step 4 ensures that the commands from the other sessions gets read in to the current session history.
Because step 4 will raise the history counter, we need to reduce the counter in some way. This is done in step 3.
In step 3 the history counter is reduced by $HISTSIZE. In step 4 the history counter is raised by the number of lines in $HISTFILE. In step 2 we make sure that the line count of $HISTFILE is exactly $HISTSIZE (this means that $HISTFILESIZE must be the same as $HISTSIZE).
About the constraints of the history expansion: Generally, once you have more than one Bash session, there is no guarantee whatsoever that a history expansion by number will retain its value between two Bash prompt displays. Everytime PROMPT_COMMAND is executed some command from another Bash session may snuck in your current session history and then the history numbers will be different. That means you always have to look up the number immediately before using it. I find this constraint reasonable. I have to look the number up every time anyway because I can't remember arbitrary history numbers.
Usually I use the history expansion by number like this
$ history | grep something #note number
$ !number
I recommend using the following Bash options.
## reedit a history substitution line if it failed
shopt -s histreedit
## edit a recalled history line before executing
shopt -s histverify
Strange bugs: Running the history command piped to anything will result that command to be listed in the history twice. For example:
$ history | head
$ history | tail
$ history | grep foo
$ history | true
$ history | false
All will be listed in the history twice. I have no idea why.
Ideas for improvements: Modify the function _bash_history_sync() so it does not execute every time. For example it should not execute after a CTRL+C on the prompt. I often use CTRL+C to discard a long command line when I decide that I do not want to execute that line. Sometimes I have to use CTRL+C to stop a Bash completion script.
Commands from the current session should always be the most recent in the history of the current session. This will also have the side effect that a given history number keeps its value for history entries from this session.