🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.unix.shell
Article #4029

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

#4029
From: S P Arif Sahari Wibowo
Date: Sat, 27 Sep 2008 15:58
161 lines
8075 bytes
On Fri, 26 Sep 2008, Icarus Sparry wrote:
> If you are going to number the lines and then do sorts on them 
> then you don't need to reverse the lines.

The numbering and sorting on it is to keep the original 
ordering. Reverse the lines it to make sure the last occurence 
of each duplication are kept.

> I suggest a
> 	tac | print_line_first_time_seen | head -n | tac 
> pipeline would be better. print_line_first_time_seen is 
> usually done as
>      awk '!seen[$0]++'

Thanks! I choose the sort method before because I believe it is 
more "general" in term of it does not depend on ability to put 
the whole file in memory (works for large files). Obviously it 
will be hardly the issue with history files. I tested your 
method and it seems to be a bit faster. I keep the original line 
commented nearby in-case someone one to use 1 GB history files. 
:-)

> I find the long variables names bad, It took several reads to 
> see that HISTTMPBASE and HISTFILEBASE were different.

Well, it was from mixed influences. I used to long file name in 
Pascal, but off course there it is camel-cased. The all capital 
influenced by common environment variable naming. I principally 
generally favor more explanatory variable name (since data 
structure is more important than algorithm in programming). In 
any case, I am changing the variable name to more camel case, 
hopefully it more understandable.

I don't see any code repetition can be saved by creating 
function, but I also trying comment more also to get better 
separation between code sections.

> if there are race conditions with multiple shells running at 
> the same time, but it certainly looks as if there are issues.

There is definitely one in reading the history file: it is 
possible to missed some files. But information is not lost and 
next session should got them. Also, there can be an issue with 
any process that open a file for extended time, but hard to get 
around it without mandatory locking and shell process should not 
do that, right?

So here is the improved code:


########### History management ###########
# SPAS 2008IO - 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"

if ! which tac ; then
   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
   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 $! )
   echo -n Shell $ThisPID signalled by" "

   HISTTmpCollect=$(mktemp "$HISTTmpBase.XXXXXXXX")
   {
     # Routine below collects files being worked on
     # by other instance of this script:
     ls -rt "$HISTTmpBase".* | 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".* | 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.

   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
   echo load history from $(wc "$HISTCollect") resulting $(history | wc -l) lines, save to "$HISTFILE"
fi


-- 
                              (stephan paul) Arif Sahari Wibowo
    _____  _____  _____  _____
   /____  /____/ /____/ /____
  _____/ /      /    / _____/      http://www.arifsaha.com/

Disclaimer: IANAL, IANALP, IANAMD, IANAMP, IANAAP
my statements - if any - should be treated as such.
Technical Details
Message-ID: <alpine.OSX.1.10.0809271102250.13605@imac2006.local>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.germany.com!feeder1.news.weretis.net!newsfeeder.dynfx.net!weretis.net!news.szaf.org!feeder06.uucp-net.de!news.uucp.at!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>

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!news3.google.com!news.germany.com!feeder1.news.weretis.net!newsfeeder.dynfx.net!weretis.net!news.szaf.org!feeder06.uucp-net.de!news.uucp.at!feed.cnntp.org!news.cnntp.org!not-for-mail
Newsgroups: comp.unix.shell,gnu.bash
Date: Sat, 27 Sep 2008 11:58:26 -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: <48dd0915$0$33600$742ec2ed@news.sonic.net>
Message-ID: <alpine.OSX.1.10.0809271102250.13605@imac2006.local>
References: <alpine.OSX.1.10.0809261024590.13605@imac2006.local> <48dd0915$0$33600$742ec2ed@news.sonic.net>
User-Agent: Alpine 1.10 (OSX 962 2008-03-14)
X-Archive: encrypt
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed
Lines: 161
Organization: CNNTP
NNTP-Posting-Host: 0d5fa549.read.cnntp.org
X-Trace: DXC=^dRDUX1h]7YS03?DM:TjdRWoT\PAgXa?QU_T_E47UJoXBQ=;T6O1baUZ]dged6hbF\WCL;1A^C63\l1dXKZC9V2Z
X-Complaints-To: abuse@cnntp.org