Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I consistently have more than one terminal open. Anywhere from two to ten, doing various bits and bobs. Now let's say I restart and open up another set of terminals. Some remember certain things, some forget.

I want a history that:

  • Remembers everything from every terminal
  • Is instantly accessible from every terminal (eg if I ls in one, switch to another already-running terminal and then press up, ls shows up)
  • Doesn't forget random things if there are spaces at the front of the command.

Anything I can do to make bash work more like that?

share|improve this question
    
I know zsh handles the first point fine, but I've never seen anything handle the second point. What do you mean by the last one? I'm not sure I understand what you want. –  durin42 Aug 26 '10 at 13:38
13  
I can see the advantage of that, but personally i would HATE that in my shell. I usually keep 3 or 4 tabs open in my terminal for very specific uses: one for running 'make', one with vi, one to run stuff, etc. So when I compile, I go to tab 1, hit up and 'make' comes up, and so on. This is extremely productive to me. So if suddenly I go to my 'make' tab and hit up and some random grep command shows up, I'd get really pissed off! Just a personal note though –  axel_c Aug 26 '10 at 13:42
    
@axel_c that's true enough. I can't think of an intelligent way to do it where existing terminals only see their own history but new ones see a chronologically accurate list of commands. –  Oli Aug 26 '10 at 15:01
    
@durin42 actually... zsh handles the second point to if you set up the history stuff right. –  xenoterracide Aug 26 '10 at 20:27
2  
Do you want the history all stored separately or all merged into a single history file? –  kbyrd Nov 25 '11 at 15:11

11 Answers 11

up vote 76 down vote accepted
# Avoid duplicates
export 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
export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -r"
share|improve this answer
8  
here is a more elaborate way to share history between bash sessions: stackoverflow.com/questions/103944/… –  lesmana Aug 27 '10 at 0:19
    
The problem with this PROMPT_COMMAND solution is that the numbers for each history item changes after each command :(. For example if you type history and 1) ls 2) rm, then you do !1 to repeat 1, the history number might change and might run the rm command... –  Chris Kimpton Nov 7 '12 at 12:28
    
When I do this, other already-open terminals don't have the last entered command when I press 'Up' until after I issue a command in that terminal - is this expected? If so, is there a way to truly modify other terminals' history instantly? –  Suan Dec 19 '12 at 2:15
    
@Suan, this seems right to me based on the commands. I found that we can issue a null command (just press enter key) to get the history to update. –  sage Apr 10 '13 at 18:13
    
Could I put this setting globally in global bashrc so that it overrides settings for all users ? –  user01 Mar 14 at 22:06

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. 

share|improve this answer

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.

share|improve this answer
    
The PS1 trick didn't work on AIX either, but this did! +1 –  Davide Oct 13 '09 at 17:39
    
See my note on my answer; this answer works better. –  Schof Oct 13 '09 at 22:28
1  
Hmm.. This kills the ability to use $ !34, as the command numbers change every prompt. Is there a workaround @Davide @Schof @kch? –  Charles Merriam Mar 17 '10 at 19:33
3  
FYI None of the solutions mentioned here can solve the following problem. I have two shell windows A and B. In shell window A, I run sleep 9999, and (without waiting for the sleep to finish) in shell window B, I want to be able to see sleep 9999 in the bash history. –  pts Mar 25 '11 at 13:43
1  
@pts I too was aiming for live behavior, but then I realized it is more convenient to have terminal specific histories which make working on different things in different terminals easier. I found this to be very useful: stackoverflow.com/questions/338285/#answer-7449399 Based on that, I made myself an alias called href that refreshes the history of my current terminal instantly and cleans up the history file in the process. Whenever I open a new terminal, that cleanup/sync is executed in my bashrc file so the new terminal has the latest history. I'm using that along with history -a –  trusktr Oct 16 '12 at 15:56

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.

share|improve this answer
2  
kch's solution works better than mine does. I'm now using his solution in my .bashrc. –  Schof Oct 13 '09 at 22:28

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).

  1. Append the just entered line to the $HISTFILE (default is .bash_history). This will cause $HISTFILE to 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 lines 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. 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.

share|improve this answer
    
Why not "history -n" (reload lines not already loaded) instead of "history -c; history -r" ? –  Graham Sep 14 '11 at 20:40
    
@Graham: I did not want to use history -n because it messes up the history counter. Also, I found history -n to be too unreliable. –  lesmana Sep 16 '11 at 12:02
    
One disadvantage: Commands with multi-line strings are normally still preserved in the current session. With this trick, they are split into individual lines instantly. Using -n for -c -r does not help, neither does cmdhist or lithist. I don't think there is a workaround at this point. –  Jo Liss Feb 1 '12 at 12:30
8  
After trying this for a bit, I've actually found that running only history -a, without -c and -r, is better usability-wise (though it's not what the question asked). It means commands you run are available instantly in new shells even before exiting the current shell, but not in concurrently running shells. This way Arrow-Up still always selects the last-run commands of the current session, which I find much less confusing. –  Jo Liss Feb 4 '12 at 15:15
    
outstandingly good answer, this works reliably unlike the more common "history -a; history -n" –  RichVel Jan 12 '13 at 22:07

I'm not aware of any way using bash. But it's one of the most popular features of zsh.
Personally I prefer zsh over bash so I recommend trying it.

Here's the part of my .zshrc that deals with history:

SAVEHIST=10000 # Number of entries
HISTSIZE=10000
HISTFILE=~/.zsh/history # File
setopt APPEND_HISTORY # Don't erase history
setopt EXTENDED_HISTORY # Add additional data to history like timestamp
setopt INC_APPEND_HISTORY # Add immediately
setopt HIST_FIND_NO_DUPS # Don't show duplicates in search
setopt HIST_IGNORE_SPACE # Don't preserve spaces. You may want to turn it off
setopt NO_HIST_BEEP # Don't beep
setopt SHARE_HISTORY # Share history between session/terminals
share|improve this answer

I can offer a fix for that last one: make sure the env variable HISTCONTROL does not specify "ignorespace" (or "ignoreboth").

But I feel your pain with multiple concurrent sessions. It simply isn't handled well in bash.

share|improve this answer

If you need a bash or zsh history synchronizing solution which also solves the problem below, then see it at http://ptspts.blogspot.com/2011/03/how-to-automatically-synchronize-shell.html

The problem is the following: I have two shell windows A and B. In shell window A, I run sleep 9999, and (without waiting for the sleep to finish) in shell window B, I want to be able to see sleep 9999 in the bash history.

The reason why most other solutions here won't solve this problem is that they are writing their history changes to the the history file using PROMPT_COMMAND or PS1, both of which are executing too late, only after the sleep 9999 command has finished.

share|improve this answer
    
This is a nice solution, but I got a few questions though. 1. Can I use the original .bash_history file, I do not want another bash history file existed in my $HOME 2. May be you should consider set a github repo for this. –  Techlive Zheng Aug 15 '12 at 10:23
    
It seems the debug hook is conflicted with bashdb, the follow output every time I start a bash session. ``` bash debugger, bashdb, release 4.2-0.8 Copyright 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Rocky Bernstein This is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. ** Internal debug error _Dbg_is_file(): file argument null bash: _Dbg_filenames[$fullname]: bad array subscript ``` –  Techlive Zheng Aug 15 '12 at 10:28
    
@Techlive Zheng: 1. The original .bash_history is deliberately not supported, because .merged_bash_history uses a different file format, so in case .merged_bash_history fails to load properly, bash won't accidentally clobber the accumulated history. Robustness by design, will be kept as is. 2. A github repo is a good idea in general, but I don't have time to maintain that for this project, so I'm not doing it. -- Yes, it conflicts with bashdb, and there is no easy solution (they use the same hooks). I'm not planning to work on a fix, but I'm accepting patches. –  pts Aug 15 '12 at 10:45
    
Okay, thank you. I have come up with a much simple and better sulotion. –  Techlive Zheng Aug 17 '12 at 9:33
    
@TechliveZheng: Would you share your simple and better solution with us, so we all can learn from it? (If so, please add an answer to the question.) –  pts Aug 17 '12 at 12:21

To do this, you'll need to add two lines to your ~/.bashrc:

shopt -s histappend
PROMPT_COMMAND="history -a;history -c;history -r;$PROMPT_COMMAND"

From man bash:

If the histappend shell option is enabled (see the description of shopt under SHELL BUILTIN COMMANDS below), the lines are appended to the history file, otherwise the history file is over-written.

share|improve this answer
    
was just going to say the exact same thing, just not as well. :) –  Tim Kennedy Nov 25 '11 at 15:48

I have written a script for setting a history file per session or task its based off the following.

        # write existing history to the old file
        history -a

        # set new historyfile
        export HISTFILE="$1"
        export HISET=$1

        # touch the new file to make sure it exists
        touch $HISTFILE
        # load new history file
        history -r $HISTFILE

It doesn't necessary save every history command but it saves the ones that i care about and its easier to retrieve them then going through every command. My version also lists all history files and provides the ability to search through them all.

Full source: https://github.com/simotek/scripts-config/blob/master/hiset.sh

share|improve this answer

Here's an alternative that I use. It's cumbersome but it addresses the issue that @axel_c mentioned where sometimes you may want to have a separate history instance in each terminal (one for make, one for monitoring, one for vim, etc).

I keep a separate appended history file that I constantly update. I have the following mapped to a hotkey:

history | grep -v history >> ~/master_history.txt

This appends all history from the current terminal to a file called master_history.txt in your home dir.

I also have a separate hotkey to search through the master history file:

cat /home/toby.walker/master_history.txt | grep -i

I use cat | grep because it leaves the cursor at the end to enter my regex. A less ugly way to do this would be to add a couple of scripts to your path to accomplish these tasks, but hotkeys work for my purposes. I also periodically will pull history down from other hosts I've worked on and append that history to my master_history.txt file.

It's always nice to be able to quickly search and find that tricky regex you used or that weird perl one-liner you came up with 7 months ago.

share|improve this answer
    
Welcome to U&L.SE. Keep providing concise answers like this, and your reputation will rise quickly. –  eyoung100 Nov 20 at 15:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.