Wrote this one because I didn't find this one: Progress. In some ways mine is simpler and prettier (although code is less readable). In case someone finds it useful - here it is, up for the taking (or improving)...
proc progres {cur tot} { # if you don't want to redraw all the time, uncomment and change ferquency #if {$cur % ($tot/300)} { return } # set to total width of progress bar set total 76 set half [expr {$total/2}] set percent [expr {100.*$cur/$tot}] set val (\ [format "%6.2f%%" $percent]\ ) set str "\r|[string repeat = [ expr {round($percent*$total/100)}]][ string repeat { } [expr {$total-round($percent*$total/100)}]]|" set str "[string range $str 0 $half]$val[string range $str [expr {$half+[string length $val]-1}] end]" puts -nonewline stderr $str }
VI 2007-02-09: Another one modeled after the one from wget. Auto updates every 5% or 5s.
32%|================..................................| 1600|ETA: 4s
Updated to print Total time taken at end - rather than reducing count.
proc progress_init {tot} { set ::progress_start [clock seconds] set ::progress_last 0 set ::progress_last_time 0 set ::progress_tot $tot } # We update if there's a 5% difference or a 5 second difference proc progress_tick {cur} { set now [clock seconds] set tot $::progress_tot if {$cur > $tot} { set cur $tot } if {($cur >= $tot && $::progress_last < $cur) || ($cur - $::progress_last) >= (0.05 * $tot) || ($now - $::progress_last_time) >= 5} { set ::progress_last $cur set ::progress_last_time $now set percentage [expr round($cur*100/$tot)] set ticks [expr $percentage/2] if {$cur == 0} { set eta ETA:[format %7s Unknown] } elseif {$cur >= $tot} { set eta TOT:[format %7d [expr int($now - $::progress_start)]]s } else { set eta ETA:[format %7d [expr int(($tot - $cur) * ($now - $::progress_start)/$cur)]]s } set lticks [expr 50 - $ticks] set str "[format %3d $percentage]%|[string repeat = $ticks]" append str "[string repeat . $lticks]|[format %8d $cur]|$eta\r" puts -nonewline stdout $str if {$cur >= $tot} { puts "" } flush stdout } } # Sample Test: progress_init 5000 for {set i 0} {$i < 6200} {incr i 200} { progress_tick $i after 200 }
CAU: I wrote this some time ago as an exercise. It displays a fancy sliding bar for the user while a long running command is in progress.
#Use on bourne/korn shell. # -- waitbar # # Shows a flashy progress bar to the user on their terminal while a # background process executes. # # Uses a Tcl interpreter to manage the screen updating! # # Arguments: # See usage message below. Function accepts EITHER a command line or a PID. # # Result: # Outputs a wait bar on the terminal. waitbar() { unset pid cmdline OPTIND=0 barLength=15 barTime=2000 barChars="-#" sliderLength=3 reverse=0 usage="Usage: waitbar [ -l<bar-length> ] [ -s<slider-length> ] [ -t<bar-time> ] [ -d<display-chars> ] [ -r ] <pid> | <command-line>" if [ $# -eq 0 ] ; then echo "No options supplied!" 1>&2 echo "$usage" 1>&2 return 1 else while getopts "l:t:d:s:r" opt "$@" do case $opt in d) barCharStr=$OPTARG if [ `printf $barCharStr | wc -m` -eq 2 ] ; then barChars=$barCharStr fi ;; l) barLength=$OPTARG ;; s) sliderLength=$OPTARG ;; t) barTime=$OPTARG ;; r) reverse=1 ;; \?) echo "$usage" 1>&2 return 1 ;; esac done fi # Resolve whether were using a command line or a PID shift `expr $OPTIND - 1` if [ $# -ne 1 ] ; then echo "$usage" 1>&2 return 1 fi userOpt=`echo "$1" | nawk '{ if (match($0,"^[0-9]+$") > 0) { printf "P" } else { printf "C" } }'` if [ "$userOpt" = "C" ] ; then cmdline="$1" else pid="$1" fi ttyStr=`tty` # Lock the terminal from any distorting iuser input. stty -echo stty -echonl { if [ "$userOpt" = "C" ] ; then eval "$cmdline &" pid=$! fi # Only show the waitbar if script is being run from a terminal if [ "$ttyStr" != "" ] ; then echo "set pid $pid set barLength $barLength set barChars \"$barChars\" set barduration $barTime set lagLength $sliderLength set reverse $reverse "' fconfigure stdout -buffering none #set barChars "-#" #set duration 10000 set duration 0 #set barduration 2000 #set barLength 15 #set lagLength 3 set barduration [expr $barduration * 1.0] set interval [expr {round($barduration/$barLength)}] set fillchar [string index $barChars 0] set poschar [string index $barChars 1] set finished 0 set timesofar 0 set bartime 0 set position 1 set forward 1 while {! $finished} { set forebar "" set aftbar "" set posString "" while {1} { set posString "$poschar$posString" set posStrLen [string length $posString] if {(($posStrLen >= $lagLength) || ($posStrLen >= $position)) || \ ( [expr {$barLength - $posStrLen}] <= [expr {$position-$lagLength}] ) } { break } } set posStrLen [string length $posString] if {$position <= $barLength} { set aftbar [string repeat $fillchar [expr $barLength - $position]] } set aftbarLen [string length $aftbar] set forebarLen [expr {$barLength-($posStrLen+$aftbarLen)}] set forebar [string repeat $fillchar $forebarLen] set aftbar [string repeat $fillchar [expr {$barLength - ($forebarLen + $posStrLen)}]] # There is a difference in how various unixes pass through data # to tclsh, so in some cases, the <CR> will need to be protected # with another slash. This is true on SunOS 5.8. set outStr [format "\\r\[$forebar$posString$aftbar\]" ] if {$reverse && (! $forward)} { set outStr [format "\\r\[$aftbar$posString$forebar\]" ] } puts -nonewline $outStr flush stdout after $interval incr bartime $interval incr timesofar $interval incr position if {$position >= [expr {$barLength + $lagLength}]} { set bartime 0 set position 1 set forward [expr {abs($forward -1)}] } if {$timesofar >= $duration} { if {$duration == 0} { set timesofar 0 if {[exec ps -opid= -p $pid] == ""} { set finished 1 } } else { set finished 1 } } } ' | tclsh & tclpid=$! wait $pid wait $tclpid printf "\\r \\r" else wait $pid fi } 2> /dev/null stty echo stty echonl return 0 }