I’m a heavy console user and I like my shell history to be relevant to the project that I’m working on. For example if I cd into ~/src/mixbook_editors, I want the shell history for that specific project—this is what I mean by per-directory Bash history.

Here is how I have this set up: I am exploiting the PROMPT_COMMAND environment variable. This variable is useful because Bash executes its contents after executing any command that I type.

For example: if I type the date command, and hit Return, Bash will execute the date command, and after that will execute the commands in the PROMPT_COMMAND environment variable. Here is a quick example:

$ export PROMPT_COMMAND='echo YEP'
YEP
$ date
Sat Jul 18 22:16:55 EEST 2020
YEP

So, I’ve set the PROMPT_COMMAND to the string 'echo YEP'. Then I execute the command date, and besides the output of date itself, I also see the output from echo YEP.

Now, besides simple commands like 'echo YEP', I can also put a shell function name and it will be executed. So I created a function that, whenever I change the current directory, it will check if there is a .bash_history file in it, and if there is, it will tell Bash to use that file as its history, instead of the default ~/.bash_history1.

The function itself is quite straightforward:

function check_for_local_history {
  function main {
    if found_local_history_file; then
      if [ ! "$PWD" == "$HOME" ]; then
        echo "Using local Bash history"
      fi

      use_history_file "$PWD/.bash_history"
    else
      use_history_file ~/.bash_history
    fi
  }

  function found_local_history_file {
    [ -e .bash_history ]
  }

  function use_history_file {
    history -w
    history -c
    export HISTFILE="$1"
    history -r
  }

  main
}

If I’d put the name of this function into the PROMPT_COMMAND variable, it will be executed on every command, and I would have achieved my goal, because this function will execute after every command, including when I switch to a directory with, say, cd ~/src/mixbook_editors.

The fact that it executes on every command seem abusive, so I look at which command was executed, and only execute the function after a cd command2.

Now, I only need to create the .bash_history in a directory, and when I cd into it, Bash picks it up, and yes, I also have a function that creates the .bash_history file: inith. 8-)

So now, whenever I start working on a new project, I do something like this:

mkdir ~/src/igwines
cd $_
inith
initv
cd
cd -

Which is this:

  • create a new directory;
  • cd into it;
  • call inith to create the local .bash_history file;
  • call initv—it does a similar thing for Vim; 8-)
  • cd home and back, so that Bash will pick up the local .bash_history.

Ta-da! Happy Bashing! :-)