#!/bin/sh # A command line Stopwatch # License: LGPLv2 # Author: # http://www.pixelbeat.org/ # Notes: # This script starts a few processes per lap, in addition to # the shell loop processing, so the assumption is made that # this takes an insignificant amount of time compared to # the response time of humans (~.1s) (or the keyboard # interrupt rate (~.05s)). # '?' for splits must be entered twice if characters # (erroneously) entered before it (on the same line). # '?' since not generating a signal may be slightly delayed # on heavily loaded systems. # Lap timings on ubuntu may be slightly delayed due to: # https://bugs.launchpad.net/bugs/62511 # Changes: # V1.0, 23 Aug 2005, Initial release # V1.1, 26 Jul 2007, Allow both splits and laps from single invocation. # Only start timer after a key is pressed. # Indicate lap number # Cache programs at startup so there is less error # due to startup delays. # V1.2, 01 Aug 2007, Work around `date` commands that don't have nanoseconds. # Use stty to change interrupt keys to space for laps etc. # Ignore other input as it causes problems. # V1.3, 01 Aug 2007, Testing release. # V1.4, 02 Aug 2007, Various tweaks to get working under ubuntu and Mac OS X. # V1.5, 27 Jun 2008, set LANG=C as got vague bug report about it. # V1.6, 19 Mar 2015 http://github.com/pixelb/scripts/commits/master/scripts/sw export LANG=C ulimit -c 0 #no cores from SIGQUIT trap '' TSTP #ignore Ctrl-Z just in case setglobal save_tty = $[stty -g] && trap "stty $save_tty" EXIT #restore tty on exit stty quit ' ' #space for laps rather than Ctrl-\ stty eof '?' #? for splits rather than Ctrl-D stty -echo #don't echo input proc cache_progs { stty > /dev/null date > /dev/null grep . < /dev/null shell {echo "import time" | python} 2> /dev/null bc < /dev/null sed '' < /dev/null printf '1' > /dev/null /usr/bin/time false !2 > /dev/null cat < /dev/null } cache_progs #to minimise startup delay date +%s.%N | grep -qF 'N' && setglobal use_python = '1' #if `date` doesn't have nanoseconds proc now { if test $use_python { echo "import time; print time.time()" !2 >/dev/null | python } else { printf "%.2f" $[date +%s.%N] } } proc fmt_seconds { setglobal seconds = $1 setglobal mins = $[echo $seconds/60 | bc] if test $mins != "0" { setglobal seconds = $[echo "$seconds - ($mins*60)" | bc] echo "$mins:$seconds" } else { echo $seconds } } proc total { setglobal end = $[now] setglobal total = $[echo "$end - $start" | bc] fmt_seconds $total } proc stop { test $lapped && lap $laptime "display" total exit } proc lap { setglobal laptime = $[echo $1 | sed -n 's/.*real[^0-9.]*\(.*\)/\1/p] test ! $laptime -o $laptime = "0.00" && return #signals too frequent setglobal laptotal = $[echo $laptime+0$laptotal | bc] if test $2 = "display" { setglobal lapcount = $[echo 0$lapcount+1 | bc] setglobal laptime = $[fmt_seconds $laptotal] echo $laptime "($lapcount)" setglobal lapped = '"true'" setglobal laptotal = '"0'" } } printf "Space for lap | ? for split | Ctrl-C to stop | Space to start..."> !2 while true { trap true INT QUIT #set signal handlers setglobal laptime = $[/usr/bin/time -p 2>&1 cat!2 > !1 cat >/dev/null] setglobal ret = $Status trap '' INT QUIT #ignore signals within this script if test $ret -eq 1 -o $ret -eq 2 -o $ret -eq 130 { #SIGINT = stop test ! $start && do { echo > !2; exit; } stop } elif test $ret -eq 3 -o $ret -eq 131 { #SIGQUIT = lap if test ! $start { setglobal start = $[now] || exit 1 echo > !2 continue } lap $laptime "display" } else { #eof = split test ! $start && continue total lap $laptime #update laptotal } }