Skip to content

Instantly share code, notes, and snippets.

@marcinantkiewicz
Created March 19, 2026 04:21
Show Gist options
  • Select an option

  • Save marcinantkiewicz/290294e2b7fa5263fdbede6e47f6480b to your computer and use it in GitHub Desktop.

Select an option

Save marcinantkiewicz/290294e2b7fa5263fdbede6e47f6480b to your computer and use it in GitHub Desktop.
Looking up better ways to record history in bash, I found https://github.com/barabo/advanced-shell-history and stopped working on this
# Work on this ended before the thing got much use. Misting trap and PROMPT_COMMAND was too much for my ability to troubleshoot bash
# pain comes from:
# - $? has to be recorded as first command in PROMPT_COMMAND, else it's overwritten by whaever runs first
# - trap debug runs every time, including every command listed in PROMPT_COMMAND
# - history 1 is a good way to get last command, but a pita in zsh
# - PROMPT_COMMAND probably does not capture subshells or pipe components right
# - sqlite does not like concurrent writes, this very much may cause concurrent writes
# just use https://github.com/barabo/advanced-shell-history
#
COMMAND_START_TIME=0;
export SESSION_ID=$(date +%s%N | base64 | tr -d '=');
HIST_DB_FILENAME="$HOME/.terminal_context.db";
# date is in ISO for sqlite
trap 'COMMAND_START_TIME=$(date "+%Y-%m-%d %H:%M:%S")' DEBUG;
make_shell_hist_db_schema() {
sqlite3 "$HIST_DB_FILENAME" <<EOF
CREATE TABLE IF NOT EXISTS history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hostname TEXT,
uid INTEGER,
directory TEXT,
command TEXT,
exit_code INTEGER,
shell TEXT,
session_id TEXT,
start_at DATETIME,
terminate_at DATETIME,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
EOF
}
log_hist_to_db() {
local EXIT_STATUS=$1; shift
# recording $? must be the first command that runs in this functioni
local COMMAND_END_TIME=$(date "+%Y-%m-%d %H:%M:%S");
local CLEAN_CMD=$(printf "%s\n" "$LAST_CMD_TO_LOG" | sed "s/'/''/g");
local CLEAN_SHELL=$(printf "%s\n" "$SHELL" | sed "s/'/''/g");
local CLEAN_PWD=$(printf "%s\n" "$PWD" | sed "s/'/''/g");
local FULL_COMMAND=$(HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g");
if [[ -n "$CLEAN_CMD" && "$CLEAN_CMD" != log_hist_to_db* ]]; then
echo "write to db" >> .debug
sqlite3 "$HIST_DB_FILENAME" "INSERT INTO history (hostname, uid, directory, command, exit_code, shell, session_id, start_at, terminate_at) VALUES ('$(hostname)', '$EUID', '$CLEAN_PWD', '$CLEAN_CMD', '$EXIT_STATUS', '$CLEAN_SHELL', '$SESSION_ID', '$COMMAND_START_TIME', '$COMMAND_END_TIME');";
fi
}
#make_shell_hist_db_schema
# log_hist_to_db must be the first command in PROMPT_COMMAND, because it must capture exit code before $? is overwritten
export PROMPT_COMMAND="_EXIT_STATUS=$?; log_hist_to_db \$_EXIT_STATUS; history -a; history -c; history -r; $PROMPT_COMMAND"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment