Article View: comp.unix.shell
Article #4028code: history auto management (for bash, but should be close to ksh)
From: S P Arif Sahari Wibowo
Date: Fri, 26 Sep 2008 15:09
Date: Fri, 26 Sep 2008 15:09
126 lines
6360 bytes
6360 bytes
Hi!
Just want to sharing some scripting code, and also RFC for
improving it, if any.
Background: I am lazy and indecisive. :-) I did many long
commands and one-liners that ended up reuse again and again, but
it is difficult for me to decide in the beginning which one need
to be saved and which one not, and I am too lazy too manage huge
collections of small scripts manually.
Bash history keeping have been very helpful, but it has several
limitation, especially since I tend to keep multiple shells open
for weeks:
- When the machine crashes (e.g. kid-induced power failure),
all my history is lost.
- History per shell can be huge, upon saving push out even not
too old history just saved from other shell session.
- Even though I setup generous HISTFILESIZE, one session that
does not start properly can wipe out most of stuff from the
history file.
- Even if the shell support "erasedups" HISTCONTROL,
duplication still occur in the file from multiple session. This
will be loaded back into the shell taking history space.
So I wrote a set of scripts below, generally to automatically
safe the history behind the scene and reduce duplication in
loading. Maybe it can be useful for you too. Let me know if you
have any suggestion. Specifically there are several complexity I
introduced because of some considerations. Let me know if this
considerations don't seem right:
- Instead of just automatically save the history in a fix
cycle, I try to do it in idle time, to reduce duplication of I
work vigorously use the same commands again and again.
- However, the calculation of idle time is such (idle time
required is shorter and shorter) so eventually the history will
be saved anyway.
- In loading history, I put a lot of effort to avoid race
condition where another session open and append to a history
file just before it get overwritten, so: (a) instead of saving
uniquely filtered history file to the original location, it
saves to new file, and (b) in collection all previous history
files, it is first renamed before read and deleted.
- I have several multiple systems with different tools and file
system, so the script start with try to guess which tools /
features available and which are not and define functions to
replace unavailable ones. Obviously not needed in a single or
homogeneous environment.
So here it is:
########### History management ###########
# SPAS 2008IO - http://www.arifsaha.com/
# need license? How about LGPL, ok?
#
# to be incorporated into or called from bash startup script, such as .bashrc
if ! which tac ; then
function tac() {
tail -r "$@"
}
fi
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
case $- in
*i* ) INTERACTIVE=1 ;;
esac
THISPID="$$"
HISTLOGFILE="/tmp/histmon.$(whoami).log"
HISTFILEBASE="$HOME/.bash_history"
HISTTMPBASE="$HOME/.bash_histtmp"
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
echo -n Shell $THISPID signalled by\
( 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 $!)
HISTTMPCOLLECT=$(mktemp "$HISTTMPBASE.XXXXXXXX")
{ ls -rt "$HISTTMPBASE".* | while read -r fn ; do echo got work file "$fn" >&2 ; cat "$fn" ; done
ls -rt "$HISTFILEBASE" "$HISTFILEBASE".* | while read -r fn ; do
HISTTMP=$(mktemp "$HISTTMPCOLLECT.XXXXXXXX");
if mv -f "$fn" "$HISTTMP" ; then echo got hist file "$fn" now "$HISTTMP" >&2 ; cat "$HISTTMP"
else echo cannot mv "$fn" "$HISTTMP" >&2 ; cat "$fn" ; fi
done
} | nl -ba -s: -nrz | tac | sort -t: -u -k2.1 | sort -t: -n -k1,1 | cut -d: -f2- | tail -n $HISTFILESIZE > "$HISTTMPCOLLECT"
history -r "$HISTTMPCOLLECT"
HISTCOLLECT=$(mktemp "$HISTFILEBASE.XXXXXXXX")
if mv -f "$HISTTMPCOLLECT" "$HISTCOLLECT" ; then rm "$HISTTMPCOLLECT".* ; fi
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/
Technical Details
Message-ID:
<alpine.OSX.1.10.0809261024590.13605@imac2006.local>Path:
arc1-ca.pugleaf.net!g2news2.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!news.k-dsl.de!feeder1.news.weretis.net!newsfeeder.dynfx.net!weretis.net!newsfeed.datemas.de!feed.cnntp.org!news.cnntp.org!not-for-mailShow Full Headers
Complete Article Headers
X-Google-Thread: 1012a6,1b90defcc7ae87a1,start X-Google-Thread: 1054c0,1b90defcc7ae87a1,start X-Google-Attributes: gid1012a6,gid1054c0,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: arc1-ca.pugleaf.net!g2news2.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!news.k-dsl.de!feeder1.news.weretis.net!newsfeeder.dynfx.net!weretis.net!newsfeed.datemas.de!feed.cnntp.org!news.cnntp.org!not-for-mail Newsgroups: comp.unix.shell,gnu.bash Date: Fri, 26 Sep 2008 11:09:57 -0400 From: S P Arif Sahari Wibowo <arifsaha@yahoo.com> Subject: code: history auto management (for bash, but should be close to ksh) Message-ID: <alpine.OSX.1.10.0809261024590.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: 126 Organization: CNNTP NNTP-Posting-Host: 1f7d9350.read.cnntp.org X-Trace: DXC=AGHmC]ZXCA=iBm:2gM@Ib>WoT\PAgXa?1C4W9jWT_L:4BQ=;T6O1ba5Z]dged6hbF<eWTm_djkI`1GmQccMHM>L6 X-Complaints-To: abuse@cnntp.org