views:

1572

answers:

5

Is it possible to share the same bash history file instance amongst all the terminal windows in real time? I want commands executed in one window to be available to all other terminal windows without having to restart them.

+4  A: 

You can use history -a to append the current session's history to the histfile, then use history -r on the other terminals to read the histfile.

jtimberman
+13  A: 

So, this is all my history-related .bashrc thing:

export HISTCONTROL=ignoredups:erasedups  # no duplicate entries
export HISTSIZE=100000                   # big big history
export HISTFILESIZE=100000               # big big history
shopt -s histappend                      # append to history, don't overwrite it

# Save and reload the history after each command finishes
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"

Tested with bash 3.2.17 on Mac OS X 10.5, bash 4.1.7 on 10.6.

edit: updated with history -c from lesmana's answer, which you should checkout too.

kch
The PS1 trick didn't work on AIX either, but this did! +1
Davide
See my note on my answer; this answer works better.
Schof
Hmm.. This kills the ability to use $ !34, as the command numbers change every prompt. Is there a workaround @Davide @Schof @kch?
Charles Merriam
Not really a workaround, but I certainly feel less the need to use history replacements by using the arrow keys for history search. That is, it search lines in history starting with whatever you typed so far. In `.inputrc`: "\e[B": history-search-forward "\e[A": history-search-backward
kch
@Charles Merriam see lesmana's answer. I now updated mine with the relevant bit.
kch
+6  A: 

You can edit your BASH prompt to run the "history -a" and "history -r" that Muerr suggested:

savePS1=$PS1

(in case you mess something up, which is almost guaranteed)

PS1=$savePS1`history -a;history -r`

(note that these are back-ticks; they'll run history -a and history -r on every prompt. Since they don't output any text, your prompt will be unchanged.

Once you've got your PS1 variable set up the way you want, set it permanently it in your ~/.bashrc file.

If you want to go back to your original prompt while testing, do:

PS1=$savePS1

I've done basic testing on this to ensure that it sort of works, but can't speak to any side-effects from running history -a;history -r on every prompt.

Schof
kch's solution works better than mine does. I'm now using his solution in my .bashrc.
Schof
+1  A: 

Bash only reads command history once. So history -r is needed in addition to history -a for scenerios if you have two shells running on one terminal and you want them to have the same command history for retriveing the past commands on the fly.

dman777
+3  A: 

here is my attempt at bash session history sharing. this will enable history sharing 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 lucid

export HISTSIZE=9000
export HISTFILESIZE=$HISTSIZE
export HISTCONTROL=ignorespace:ignoredups

history() {
  _bash_history_sync
  builtin history "$@"
}

_bash_history_sync() {
  builtin history -a         #[1]
  HISTFILESIZE=$HISTFILESIZE #[2]
  builtin history -c         #[3]
  builtin history -r         #[4]
}

export PROMPT_COMMAND=_bash_history_sync

explanation:

the function history() overrides the builtin history to make sure that the history is synced before it is displayed. this is necessary for the history expansion by number (more about this later).

[1] append the just entered line to the $HISTFILE (default is .bash_history). this will cause $HISTFILE grow by one line.

[2] setting the special variable $HISTFILESIZE to some value will cause bash to truncate $HISTFILE to be no longer than $HISTFILESIZE by removing the oldest entries.

[3] clear the history of the running session. this will reduce the history counter by the amount of $HISTSIZE.

[4] 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.

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 it's 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 everytime 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 everytime. 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.

lesmana