Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Synchronize history across bash sessions
# Synchronize history between bash sessions
#
# Make history from other terminals available to the current one. However,
# don't mix all histories together - make sure that *all* commands from the
# current session are on top of its history, so that pressing up arrow will
# give you most recent command from this session, not from any session.
#
# Since history is saved on each prompt, this additionally protects it from
# terminal crashes.
# keep unlimited shell history because it's very useful
export HISTFILESIZE=-1
export HISTSIZE=-1
shopt -s histappend # don't overwrite history file after each session
# on every prompt, save new history to dedicated file and recreate full history
# by reading all files, always keeping history from current session on top.
update_history () {
history -a ${HISTFILE}.$$
history -c
history -r # load common history file
# load histories of other sessions
for f in `ls ${HISTFILE}.[0-9]* 2>/dev/null | grep -v "${HISTFILE}.$$\$"`; do
history -r $f
done
history -r "${HISTFILE}.$$" # load current session history
}
if [[ "$PROMPT_COMMAND" != *update_history* ]]; then
export PROMPT_COMMAND="update_history; $PROMPT_COMMAND"
fi
# merge session history into main history file on bash exit
merge_session_history () {
if [ -e ${HISTFILE}.$$ ]; then
cat ${HISTFILE}.$$ >> $HISTFILE
\rm ${HISTFILE}.$$
fi
}
trap merge_session_history EXIT
# detect leftover files from crashed sessions and merge them back
active_shells=$(pgrep `ps -p $$ -o comm=`)
grep_pattern=`for pid in $active_shells; do echo -n "-e \.${pid}\$ "; done`
orphaned_files=`ls $HISTFILE.[0-9]* 2>/dev/null | grep -v $grep_pattern`
if [ -n "$orphaned_files" ]; then
echo Merging orphaned history files:
for f in $orphaned_files; do
echo " `basename $f`"
cat $f >> $HISTFILE
\rm $f
done
echo "done."
fi
@jan-warchol
Copy link
Author

jan-warchol commented Aug 12, 2019

@dkadioglu save and source it in your shell configuration file (usually ~/.bashrc on Linux, ~/.bash_profile on Mac) - like this:

source sync-history.sh

Let me know if you need more detailed instructions.

@dkadioglu
Copy link

dkadioglu commented Aug 16, 2019

Thank you very much, works so far. One thing though: It seems, that the deletion of entries (history -d offset) is not possible anymore. At least for me, whichever item I delete, it still appears in the history. I tried to find a solution for that, without success so far. Do you have any idea?

@hoefkensj
Copy link

hoefkensj commented Jul 15, 2022

@dkadioglu save and source it in your shell configuration file (usually ~/.bashrc on Linux, ~/.bash_profile on Mac) - like this:

source sync-history.sh

Let me know if you need more detailed instructions.

hi there i stumbled on this, and just wanted to mention that instead of using source sync-history.sh there is a safer way to do this :

[[ -r /path/to/file.sh ]] && . /path/to/file.sh

explanation:

  • [[ -r checks fi the file that follows is readable and returns True if so

  • && executes only if the command before returns True (or exit status 0 = success)

  • . is somewhat shorthad for source (note there is a spacebehind the '.' so '. '

result : tests is if the file exists and is readable before sourcing it.

you can combine it with:

 FILE="nohup.out"  ; [[ -r $FILE ]] && .  $FILE ; [[ ! -e   $FILE ]] && echo "! Waring $FILE not found "

wich will give you a warning if you somhow in the future accidently rename or move the file.

if you have your bash rc split up in multiple files (like i have to create some order in the chaos) you can use:

for conf in $(ls /opt/local/scripts/rc/bash/[3-9]??_*) ; do 
    printf "Loading: $(basename $conf)"
    [[ -r "${conf}" ]] && source "${conf}" 
    printf '\x1b[40G\x1b[32mDONE\x1b[0m\n'
done

wich will in this case find all the files that start with a digit from 3-9 (my dir contents look like :bash 000_bashrc.conf 100_includes.conf 201_opts.conf 221_binds.conf ... 701_exports.conf hoefkens.bash_history ... an the loop is called from within includes (wich so the 000 and 1* , 2* dont need to get loaded
and source them one by one printing a success message to stdout
starting a new tty session thus looks like:
Screenshot_20220715_225318

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment