How do I create a stopwatch Bash script to constantly display elapsed time?
I use the following code snippet in long-running scripts.The timer runs in a function (separate process) which will be killed (trap) if the main process is terminated by a keybord interrupt. The output shows the time elapsed from the start of the timer:
... working [hh:mm:ss] 00:07:58
The snippet:
#=== FUNCTION ================================================================# NAME: progressIndicatorTime# DESCRIPTION: Display a progress indicator with seconds passed.# PARAMETERS: [increment] between 1 and 60 [sec], default is 2 [sec]#===============================================================================function progressIndicatorTime (){ declare default=2 # default increment [sec] declare increment="${1:-$default}" # 1. parameter or default declare format='\b\b\b\b\b\b\b\b%02d:%02d:%02d' # time format hh:mm:ss declare timepassed=0 declare seconds minutes hours [[ ! "$increment" =~ ^([1-9]|[1-5][0-9]|60)$ ]] && increment=$default printf " ... working [hh:mm:ss] 00:00:00" while : ; do # infinite loop ((seconds=timepassed%60)) ((minutes=timepassed/60)) ((hours=minutes/60)) ((minutes=minutes%60)) printf "$format" $hours $minutes $seconds sleep $increment || break # sleep ... ((timepassed+=increment)) done} # ---------- end of function progressIndicatorTime ----------progressIndicatorTime & # run progress indicatordeclare progressIndicatorPid=${!} # save process IDtrap "kill $progressIndicatorPid" INT TERM # trap keyboard interrupt## run long command#kill -s SIGTERM $progressIndicatorPid # terminate progress indicator
The art could use some work, but give this a try:
#!/bin/bashref_date='Thu Apr 19 17:07:39 CDT 2012'ref_sec=$(date -j -f '%a %b %d %T %Z %Y' "${ref_date}" +%s)update_inc=1tput clearcat <<'EOF' [|] [|] _-'''''''''''''-_ / \ | | | | | | \ / '-_____________-'EOFwhile :do ((sec=$(date +%s) - ${ref_sec})) ((day=sec/86400)) ((sec-=day*86400)) ((hour=sec/3600)) ((sec-=hour*3600)) ((min=sec/60)) ((sec-=min*60)) tput cup 6 14 printf "%.2id:%.2ih:%.2im:%.2is\r" ${day} ${hour} ${min} ${sec} sleep ${update_inc}doneexit 0
Note that the syntax of the first date command is for OSX.
For GNU date, use date --date="${ref_date}" +%s
If you have Bash4 or ksh93, you can use the printf %()T
syntax to print strftime
formats. The following example is Bash 4 printing in the format you want. Bash precision is limited to 1 second. ksh93 supports floats and would require some modification.
#!/usr/bin/env bash# To supply a default date/time. To use now as the default, leave empty, or run with a null first arg.deftime='Fri Apr 14 14:00:00 EDT 2011'if (( ${BASH_VERSINFO[0]}${BASH_VERSINFO[1]} >= 42 )); then unixTime() { printf ${2+-v "$2"} '%(%s)T' -1 }else unixTime() { printf ${2+-v "$2"} "$(date '+%s')" }fistopWatch() { local timestamp="$(date ${1:+'-d' "$1"} '+%s')" curtime day=$((60*60*24)) hour=$((60**2)) # unixTime -1 timestamp while unixTime -1 curtime (( curtime -= timestamp )) printf '%02dd:%02dh:%02dm:%02ds\r' $(( curtime / day )) $(( (curtime / hour) % 24 )) $(( (curtime / 60) % 60 )) $(( curtime % 60 )) do sleep 1 done}stopWatch "${1-deftime}"
You may also be interested in the $SECONDS
variable. Unfortunately there is no convienient way to accept a human-readable date like you want. It would require considerable parsing effort. The date command (particularly GNU date) can do that to a limited extent.