Version 3 of text mode (terminal) progress bar / progressbar

Updated 2006-11-11 15:44:14 by lwv

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
 }

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
 }

clear screen


Category Text