Skip to content

Instantly share code, notes, and snippets.

@gatopeich
Last active February 2, 2023 09:25
Show Gist options
  • Save gatopeich/35d872335dc96e363ed104f3092fb48d to your computer and use it in GitHub Desktop.
Save gatopeich/35d872335dc96e363ed104f3092fb48d to your computer and use it in GitHub Desktop.
Merge and keep bash history from different folders and processes
#!/bin/bash
# BASH history merger by Agustín F. Pozuelo (c) 2023
# Merge history files from multiple sessions, removing duplicate commands
# TODO: Maintain order of commands in the most recent appearances
# BASH history merger.
# I use this to maintain history of my many BASH sessions
# Each session writes history to a separate file
# Running this from inside .bashrc, the 10 most recent sessions are merged together
# duplicate lines are removed, then they are all sorted by timestamp and place in a new history file
# The new file is used throughout the session then saved
# My history settings:
export HISTCONTROL=ignoredups:erasedups # Avoid (only) consecutive dups
export HISTSIZE=20000 # Got 17846 as of January 2023
export HISTFILESIZE=20000
export HISTTIMEFORMAT="%F %T "
export HISTFILE="$HOME/.history-$(date +%Y%m%d)-$(basename $(pwd))-$$"
echo "Biggest story files:"
ls -Shs ~/.*history* | head
function merge_histories {
>&2 echo "Merging history files:"
>&2 ls -lh "$@"
for h in "$@"; do
unset timestamp
# N=0
TS_PATTERN='^\#[0-9]{10}'
cat $h | while read -r line; do
# ((N+=1))
if [[ $line =~ ^\#[0-9]{10} ]]; then
if [[ -v timestamp ]]; then
# >&2 echo "NUL between '$timestamp' and '$line'..."
echo -en '\0'
fi
timestamp=$line
# >&2 echo "Got TIMESTAMP=$timestamp ($h:$N)"
echo -n "$timestamp "
elif [[ -v timestamp ]]; then
# >&2 echo "Got COMMAND LINE=$line ($h:$N)"
echo "$line"
else
>&2 echo "No timestamps in file: $h"
# TODO: use the file's timestamp? "#$(stat -c%Y $h)"
break
fi
done | while read -rd $'\0' timestamp commands; do
# >&2 echo -e "$h:TIMESTAMP=$timestamp\n$h:COMMANDS=$commands"
echo -en "$commands $timestamp\0"
done
done |sort -zr| # r-sort to keep last timestamp for same command
while read -rd $'\0' l; do
timestamp=${l##*#}
# >&2 echo "GOT LINE: $l, timestamp=$timestamp"
echo -en "$timestamp ${l%#*}\0" # Replace timestamp in front and uniq by the rest
done |uniq -zf1|sort -z| # Unique and sort by timestamp
while read -rd $'\0' timestamp rest; do
echo -e "#$timestamp\n$rest"
# >&2 echo "WROTE TIMESTAMP=#$timestamp REST=$rest"
done
}
histories=$(ls -t1 ~/.*history*|head) # 10 most recent
merge_histories $histories > $HISTFILE
ls -lh $HISTFILE
history -r $HISTFILE
echo "History lines: $(history|wc -l)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment