In gui mode, when a user has more than one terminal open, how do the terminals rewrite the history file of that user? the reason I ask is because, it is quite possible that in each of the terminals the user ends up executing different commands. So, does the history file end up saving the commands from all the terminals or only from the first one to be opened? Or is there some other kind of scheme that is employed to tackle this situation?
Mangled history
command history
Related Solutions
Add the following to your ~/.bashrc
:
# Avoid duplicates
HISTCONTROL=ignoredups:erasedups
# When the shell exits, append to the history file instead of overwriting it
shopt -s histappend
# After each command, append to the history file and reread it
PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -r"
This is actually a really interesting behavior and I confess I have greatly underestimated the question at the beginning. But first the facts:
1. What works
The functionality can be achieved in several ways, though each works a bit differently. Note that, in each case, to have the history "transferred" to another terminal (updated), one has to press Enter in the terminal, where he/she wants to retrieve the history.
option 1:
shopt -s histappend HISTCONTROL=ignoredups PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
This has two drawbacks:
- At login (opening a terminal), the last command from the history file is read twice into the current terminal's history buffer;
- The buffers of different terminals do not stay in sync with the history file.
option 2:
HISTCONTROL=ignoredups PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
(Yes, no need for shopt -s histappend
and yes, it has to be history -c
in the middle of PROMPT_COMMAND
)
This version has also two important drawbacks:
- The history file has to be initialized. It has to contain at least one non-empty line (can be anything).
- The
history
command can give false output - see below.
[Edit] "And the winner is..."
option 3:
HISTCONTROL=ignoredups:erasedups shopt -s histappend PROMPT_COMMAND="history -n; history -w; history -c; history -r; $PROMPT_COMMAND"
This is as far as it gets. It is the only option to have both erasedups
and common history working simultaneously.
This is probably the final solution to all your problems, Aahan.
2. Why does option 2 not seem to work (or: what really doesn't work as expected)?
As I mentioned, each of the above solutions works differently. But the most misleading interpretation of how the settings work comes from analysing the output of history
command. In many cases, the command can give false output. Why? Because it is executed before the sequence of other history
commands contained in the PROMPT_COMMAND
! However, when using the second or third option, one can monitor the changes of .bash_history
contents (using watch -n1 "tail -n20 .bash_history"
for example) and see what the real history is.
3. Why option 3 is so complicated?
It all lies in the way erasedups
works. As the bash manual states, "(...) erasedups
causes all previous lines matching the current line to be removed from the history list before that line is saved". So this is really what the OP wanted (and not just, as I previously thought, to have no duplicates appearing in sequence). Here's why each of the history -.
commands either has to or can not be in the PROMPT_COMMAND
:
history -n
has to be there beforehistory -w
to read from.bash_history
the commands saved from any other terminal,history -w
has to be there in order to save the new history to the file after bash has checked if the command was a duplicate,history -a
must not be placed there instead ofhistory -w
, because it will add to the file any new command, regardless of whether it was checked as a duplicate.history -c
is also needed because it prevents trashing the history buffer after each command,and finally,
history -r
is needed to restore the history buffer from file, thus finally making the history shared across terminal sessions.
Be aware that this solution will mess up the history order by putting all history from other terminals in front of the newest command entered in the current terminal. It also does not delete duplicate lines already in the history file unless you enter that command again.
Best Answer
It depends entirely on how the shell chooses to handle it
bash
by default will overwrite the history file with the local history of each shell as it exits, so the last shell to exit wins. Thehistappend
option will cause it to append to the master history instead (shopt -s histappend
).zsh
does the same by default, and has a few options for dealing with it:appendhistory
-- The history of each shell is appended to the master history file as the shell exitsincappendhistory
-- The master history file is updated each time a line is executed in any shell, instead of waiting until that shell exitssharehistory
-- Likeincappendhistory
, but also pulls changes from the master history file into all running shells, so you can run a command in one shell and then hit Up in another shell and see it