Persistent shell history


< #DEV >
< RFC+ >

For bash, zsh.

Revisions:
    20200815 initial
    20210308
    20210323 adding common logging part
    20210811 (notes on common logging)
    20221215 pingo (new ideas)
    20240413 works with bash and now zsh too
    20240419 fix zsh label bug ... oversee, mimaize frags ... →RFC+





SETUP !

Want common loging across all accounts?:
    sudo touch /common_persistent_history
    sudo chmod 777 /common_persistent_history

Put the script somewhere:
    sudo mkdir /scripts
    sudo joe /scripts/configure_loghistory.sh ###copy-in the script below
    sudo chmod +x /scripts/configure_loghistory.sh

Now add in ALL USERS' .bashrc / .zsh, INCLUDING /root, at the end:
    source /scripts/configure_loghistory.sh 'label'
    # the extra 'label' string is optional, will show up in history too
    # ... useful when you have several containers, for example





THE SCRIPT !


#!/bin/bash
#__________________________________________
#__________________________________________
# via https://e2h.totalism.org/e2h.php?_=dev-phistory
# Revisions:
#—— 20200914 1900
#—— 20210811 1518
#—— 20231021 0310
#—— 20240413 1704
#—— 20240419 0433
#__________________________________________

echo
echo "| "
echo "| All commands are logged, see /common_persistent_history."
echo "| "
echo

# see https://www.baeldung.com/linux/bash-histsize-vs-histfilesize
HISTSIZE=1000000 #available within session
HISTFILESIZE=100000 #saved, available to next sessions


log_bash_persistent_history()
{
  [[ 
    $(history 1) =~ ^\ *[0-9]+\ +([^\ ]+\ [^\ ]+)\ +(.*)$
  ]]
  local date_part="${BASH_REMATCH[1]}"
  local command_part="${BASH_REMATCH[2]}"
  if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ]
  then
  #
  #mods:
  # $USER → user ID
  # $1 → optional label string (add when you 'source') ... can be useful for identifying containers etc
  # $$ → process ID of current shell, so you can pluck through even parallel instances
  #
    ### for this user
    echo $USER $1 $$ $date_part "|" "$command_part" >> ~/.persistent_history
    ### for all users
    echo $USER $1 $$ $date_part "|" "$command_part" >> /common_persistent_history

    export PERSISTENT_HISTORY_LAST="$command_part"
  fi
}

#--- --- --- --- --- --- --- --- --- --- --- --- --- 

log_zsh_persistent_history()
{
  if [[ $(history -i | tail -n1) =~ ^\ *[0-9]+\ +([^\ ]+\ [^\ ]+)\ +(.*)$ ]]; then
    local date_part="${match[1]}"
    local command_part="${match[2]}"
    if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ]; then
      echo $USER $1 $$ $date_part "|" "$command_part" >> ~/.persistent_history
      echo $USER $1 $$ $date_part "|" "$command_part" >> /common_persistent_history
      export PERSISTENT_HISTORY_LAST="$command_part"
    fi
  fi
}

#--- --- --- --- --- --- --- --- --- --- --- --- --- 


#
# Check if the shell is Bash or zsh
#
if [ -n "$BASH_VERSION" ]; then
    # If Bash, set PROMPT_COMMAND
    PROMPT_COMMAND="log_bash_persistent_history $1"
elif [ -n "$ZSH_VERSION" ]; then
    # zsh history is weird
    # help via https://unix.stackexchange.com/questions/647001/histtimeformat-change-is-not-working-at-all
    unalias history

    lbl="$1" #zsh can't see $1 within precmd() below
    precmd() {
       log_zsh_persistent_history "${lbl}"
    }
fi

export HISTTIMEFORMAT="%F %T  "

echo "__________ session" $USER $$ $date_part >> ~/.persistent_history
echo "__________ session" $USER $$ $date_part >> /common_persistent_history


#__________________________________________
#__________________________________________





INFO

Based on:
    https://eli.thegreenplace.net/2013/06/11/keeping-persistent-history-in-bash/.

License:
    super-lite version of 🔗comrade-license
    * you're probably seeing this as dcht00 gave you the link
    * with link, you're free to use it yourself, or on non-commerical, or commons-based commercial projects (as a cooperative)
    * do not post the code nor the link publicly
    * you are invited to share it directly to aligned people





*** PAD


[!!!*] ALIKE— Try 'atuin'
<20251109

"""
Atuin replaces your existing Linux or Unix shell (bash,  zsh, fish and others) history with a SQLite database and records additional context for your commands. Additionally, it provides optional and fully encrypted synchronization of your history between machines via an Atuin server. It is free and open-source software written in Rust. Would you try it?
"""



[!] ALIKE— Why not use 'script'

<l1

yeah script could be used as well but this seems more full-proof?
###


[m!] ALIKE— Why not just use zshhistory, how is this different

* full times
* all users on same spot
* is guaranteed persistent (unlimited)
* ... but doesn't suffer from "all history is kept in memory" problem
* ###



[!*] DEV/FEATURE— Persist out
aka external reporting

for...:
    * backups
    * anti-tamper



[!!] UX— "it can feel a bit awkward"

don't think that's neccessary bad tho



[!!*] BUG— Seems that opening a new shell adds some previous command?

###


[!!**t] DEV— Logging inside screen/tmux

###



—————



[!**] DEV/UX— Change dates to YYYYMMDD


[!!**o] DEV— include current path

*) at least in session start
*) maybe "every now and then"



[!!*] DEV/UX— better searching

* -C3 (context)?
* display title/whole session
* ###




[k!!**] DEV— make zsh compatible

don't support $PROMPT_COMMAND

see
https://superuser.com/questions/735660/whats-the-zsh-equivalent-of-bashs-prompt-command

###

____________ more

https://unix.stackexchange.com/questions/273861/unlimited-history-in-zsh
"""
SAVEHIST=10000000
setopt BANG_HIST                 # Treat the '!' character specially during expansion.
setopt EXTENDED_HISTORY          # Write the history file in the ":start:elapsed;command" format.
setopt INC_APPEND_HISTORY        # Write to the history file immediately, not when the shell exits.
setopt SHARE_HISTORY             # Share history between all sessions.
setopt HIST_EXPIRE_DUPS_FIRST    # Expire duplicate entries first when trimming history.
#setopt HIST_IGNORE_DUPS          # Don't record an entry that was just recorded again.
#setopt HIST_IGNORE_ALL_DUPS      # Delete old recorded entry if new entry is a duplicate.
setopt HIST_FIND_NO_DUPS         # Do not display a line previously found.
#setopt HIST_IGNORE_SPACE         # Don't record an entry starting with a space.
#setopt HIST_SAVE_NO_DUPS         # Don't write duplicate entries in the history file.
setopt HIST_REDUCE_BLANKS        # Remove superfluous blanks before recording entry.
setopt HIST_VERIFY               # Don't execute immediately upon history expansion.
setopt HIST_BEEP                 # Beep when accessing nonexistent history.
"""


_____________ yes

https://unix.stackexchange.com/questions/647001/histtimeformat-change-is-not-working-at-all
unalias history


_______________ another one

https://stackoverflow.com/questions/30249853/save-zsh-history-to-persistent-history
"""
setopt append_history # append rather then overwrite
setopt extended_history # save timestamp
setopt inc_append_history # add history immediately after typing a command
"""




[!*] DEV/SERVER— concord histories accross containers

(case of hackerspaces.org srv0 : main, web0, mx0)

how?

A) python, looks at "session started" blocks and sorts them, puts together
B) ###



[!!**] DEV/COADMIN— auto-deploy across all users
(check in cron)

push into...
/home/*/.bashrc
/home/*/.zshhrc
/root/.bashrc
/root/.zshhrc



[!!*o] DEV/COADMIN— annotation, common review, etc

*) just freely annotate the common file:
    use "#" in front, i guess

*) copy to pad, work with it there:
    be mindful of × stuff ... inline passwords, etc

*) ###




[!*oft] DEV/COADMIN— tamper-proof

uuuh... blockchain? jk jk



[!*] DEV/COADMIN— import past histories

###



[!!**oe] DEV/COADMIN— Integrate "say"

would be a perfect match to common log!



<--------------------------- ((new))