🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.unix.shell
Article #4030

Re: code: history auto management (for bash, but should be close to ksh)

#4030
From: S P Arif Sahari Wibowo
Date: Mon, 29 Sep 2008 00:55
129 lines
6800 bytes
Updated version, now include sanity check: in case processing 
fails, not to remove older history files.


########### History management ###########
# SPAS 2008IO, 2008IR - http://www.arifsaha.com/
# need license? How about LGPL, ok?
#
# to be incorporated into or sourced from bash startup script, such as .bashrc

case $- in
   *i* )   INTERACTIVE=1 ;;
esac
ThisPID="$$"
HISTLogFile="/tmp/histmon.$(whoami).log"
HISTFileBase="$HOME/.bash_history"
HISTTmpBase="$HOME/.bash_histtmp"

# In some system 'which' never fails,
# requiring secondary check as below, duh!
if [[ ! -x $(which tac) ]] ; then
   echo define tag function >&2
   function tac() {
     tail -r "$@"
   }
fi

# define function to get the last time
# any activity in the tty of a process
if [[ -d /proc ]] ; then
   function timelastactiv() {
     date -r "/proc/$1/fd/255" +"%s"
   }
elif ( stat --help | grep "%y.*modification" ) >/dev/null 2>&1 ; then
   function timelastactiv() {
     stat -f "%y" /dev/$(ps -otty -p "$1" | tail -1)
   }
else
   function timelastactiv() {
     stat -f "%m" /dev/$(ps -otty -p "$1" | tail -1)
   }
fi

function routines() {
   history -a
   #echo -ne \\e[s\\e[1A$(date) history saved!\\e[u >&2
   echo $ThisPID, $(date) history saved! >> $HISTLogFile
}

if [[ ! "$HISTFILESIZE" ]] ; then HISTFILESIZE=4096 ;fi

if [[ "$INTERACTIVE" ]] ; then
   trap routines SIGUSR1
   WaitIni=$((2 * 3600))
   WaitSmall=600
   LastSave=0
   # Routine below run a background loop that
   # will signal the shell to save history after some time.
   # Instead of fix periods, it try to do it in idle time,
   # to reduce duplication caused by vigorous work
   # using the same commands again and again.
   # However, the calculation of idle time required is shorter over time,
   # so it assure that the history saving signal will be sent after some time.
   ( trap "" SIGINT ; while WaitCur=$WaitIni ; while TOUCHED=$(timelastactiv "$ThisPID"); if [[ ! "$TOUCHED" ]] || [[ "$TOUCHED" -le "$LastSave" ]] ; then IDLE=1 ; else IDLE=$(( $(date +"%s") - $TOUCHED )); fi ; echo $ThisPID, $(date): $IDLE .. $WaitCur >> $HISTLogFile ; [[ $IDLE -lt $WaitCur ]] ; do WaitCur=$(( $WaitCur * 2 / 3 )) ; sleep $WaitSmall ; done ; kill -s SIGUSR1 $ThisPID ; do LastSave=$(date +'%s'); sleep $WaitSmall ; done & echo -n $! >&2 )
   echo " "signalling shell $ThisPID >&2

   HISTTmpCollect=$(mktemp "$HISTTmpBase.XXXXXXXX")
   {
     # Routine below collects files being worked on
     # by other instance of this script:
     ls -rt "$HISTTmpBase".* 2>/dev/null | while read -r afile ; do echo got work file "$afile" >&2 ; cat "$afile" ; done

     # Routine below collects history files by previous sessions:
     ls -rt "$HISTFileBase" "$HISTFileBase".* 2>/dev/null | while read -r afile ; do
       # To avoid other session open & append the history file
       # just after being read and before deleted,
       # each file is renamed before read:
       HISTTmp=$(mktemp "$HISTTmpCollect.XXXXXXXX");
       if mv -f "$afile" "$HISTTmp" ; then echo got hist file "$afile" now "$HISTTmp" >&2 ; cat "$HISTTmp"
       else echo cannot mv "$afile" "$HISTTmp" >&2 ; cat "$afile" ; fi
     done
   } | tac | awk '!seen[$0]++' | head -n $HISTFILESIZE | tac > "$HISTTmpCollect"
   #  nl -ba -s: -nrz | tac | sort -t: -u -k2.1 | sort -t: -n -k1,1 | cut -d: -f2- | tail -n $HISTFILESIZE > "$HISTTmpCollect"
   # The phrase above will filter out duplicate,
   # keeping the order, keeping last occurrence of any duplicate.
   # The result saved to a new file, not original history file
   # to avoid other session open & append the original history file
   # just before it being overwritten.
   # Note that race condition can still occur in reading the history file:
   # it is possible a session started at the same time grab
   # a history file and rename it before it can be read
   # but just after collecting work file is done.
   # So some history may not get loaded.
   # But this is less big deal since the information is not
   # lost and next session will be able to read it.

   # Saity check whether the pipe was actually successful:
   # (alternatively bash' PIPESTATUS can be used,
   # just it will make it harder to port to other shell)
   if [[ -s "$HISTTmpCollect" ]] ; then
     history -r "$HISTTmpCollect"
     HISTCollect=$(mktemp "$HISTFileBase.XXXXXXXX")
     if mv "$HISTTmpCollect" "$HISTCollect" ; then rm "$HISTTmpCollect".* ; fi

     # Actual history saving file is another new history file
     # instead of the new combined old history
     # to keep better ordering of history (i.e. newer history file
     # not to be put behind the old one):
     HISTNew=$(mktemp "$HISTFileBase.XXXXXXXX")
     if [[ -w "$HISTNew" ]] ; then HISTFILE="$HISTNew" ; fi
   else
     rm "$HISTTmpCollect"
   fi

   # Move back any work file left over because of some failures:
   ls -rt "$HISTTmpCollect".* 2>/dev/null | while read -r afile ; do
     HISTTmp=$(mktemp "$HISTFileBase.XXXXXXXX");
     mv "$afile" "$HISTTmp"
     echo mv back "$afile" "$HISTTmp" >&2
   done
   echo load history from $(wc "$HISTCollect") resulting $(history | wc -l) lines, save to "$HISTFILE" >&2
fi


-- 
                              (stephan paul) Arif Sahari Wibowo
    _____  _____  _____  _____
   /____  /____/ /____/ /____
  _____/ /      /    / _____/      http://www.arifsaha.com/
Technical Details
Message-ID: <alpine.OSX.1.10.0809282053010.22505@imac2006.local>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!tiscali!newsfeed1.ip.tiscali.net!195.208.113.2.MISMATCH!goblin2!goblin.stu.neva.ru!feed.cnntp.org!news.cnntp.org!not-for-mail
References: <alpine.OSX.1.10.0809261024590.13605@imac2006.local> <48dd0915$0$33600$742ec2ed@news.sonic.net> <alpine.OSX.1.10.0809271102250.13605@imac2006.local>

Show Full Headers
Complete Article Headers
X-Google-Thread: 1012a6,1b90defcc7ae87a1
X-Google-Thread: 1054c0,1b90defcc7ae87a1
X-Google-Attributes: gid1012a6,gid1054c0,domainid0,public,usenet
X-Google-Language: ENGLISH,ASCII-7-bit
Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!tiscali!newsfeed1.ip.tiscali.net!195.208.113.2.MISMATCH!goblin2!goblin.stu.neva.ru!feed.cnntp.org!news.cnntp.org!not-for-mail
Newsgroups: comp.unix.shell,gnu.bash
Date: Sun, 28 Sep 2008 20:55:07 -0400
From: S P Arif Sahari Wibowo <arifsaha@yahoo.com>
Subject: Re: code: history auto management (for bash, but should be close to
 ksh)
In-Reply-To: <alpine.OSX.1.10.0809271102250.13605@imac2006.local>
Message-ID: <alpine.OSX.1.10.0809282053010.22505@imac2006.local>
References: <alpine.OSX.1.10.0809261024590.13605@imac2006.local> <48dd0915$0$33600$742ec2ed@news.sonic.net> <alpine.OSX.1.10.0809271102250.13605@imac2006.local>
User-Agent: Alpine 1.10 (OSX 962 2008-03-14)
X-Archive: encrypt
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; format=flowed; charset=US-ASCII
Lines: 129
Organization: CNNTP
NNTP-Posting-Host: 1ea19a15.read.cnntp.org
X-Trace: DXC=5[[IRC3?=1N?ZNgFG`EG\IWoT\PAgXa?AC4W9jWT_L:DBQ=;T6O1baEZ]dged6hbFLbKn>=1b3\MJc`a91_Q?Wn@
X-Complaints-To: abuse@cnntp.org