progress_bar with percentage progress

linux fan linuxscratch at gmail.com
Sat Jul 11 08:02:04 PDT 2009


This is a proposed version of progress_bar.sh which is backward
compatible with the standard version and adds the capability to show
percentage complete and advance according to the percentage. For the
added behavior 2 additional arguments are the number of SBUs as seen
in the book and the number of seconds in 1 SBU. A third optional
additional argument dubbed slowpoke is whole number of seconds to
sleep if a large percentage of time sleeping is desired. Without the
additional arguments, it behaves like the standard version.


# $Id: progress_bar.sh 3336 2007-03-19 18:27:00Z manuel $

set -e

# Be sure that we know the taget name
[[ -z $1 ]] && exit
TARGET=$1  # Remember the target build we are looking for
MAKE_PPID=$2

# For percentage progress, supply $3,$4 = #SBUs,1SBU
# and optionally supply $5 = slowpoke (see below).
# e.g., sh progress_bar.sh foo $PPID .3 540
#
# If either $3 or $4 is not supplied, this will perform the usual way.
#
# Examples of valid #SBUs: 1, 1.5, 0.1, .5
#
# 1SBU must be a whole number of seconds
#
# External code (or typist) must supply the #SBUs and 1SBU - a difficult job.
#
# Anything supplied in $5 makes it slowpoke -- at least 1 second sleep
# If a number is supplied in $5 -- that many seconds sleep
# e.g., sh progress_bar.sh foo $PPID .1 540 slow (1 second sleep)
# e.g., sh progress_bar.sh foo $PPID .1 540 5 (5 second sleep)
#
# Note: slowpoke
#       There are 5 graphic characters per cycle and it sleeps on each one.
#       e.g., if 1 second sleep, then 1 second between tests for exit,
#       and 5 seconds between position jumps and time/percentage updates.
#       Monitored proceess (e.g., Makefile segment) preferably might ensure
#       that it (e.g., houskeeping) sleeps at least slowpoke seconds
#       to avoid possible sloppy multiple progress_bars behaviors,
#       although when one ends the other will continue (so no calamity).

declare -r  CSI=$'\e['  # DEC terminology, Control Sequence Introducer
declare -r  CURSOR_OFF=${CSI}$'?25l'
declare -r  CURSOR_ON=${CSI}$'?25h'
declare -r  ERASE_LINE=${CSI}$'2K'
declare -r  FRAME_OPEN=${CSI}$'2G['
declare -r  FRAME_CLOSE=${CSI}$'63G]'
declare -r  TS_POSITION=${CSI}$'65G'
declare -r  LINE_WRAP_OFF=${CSI}$'?7l'
declare -r  LINE_WRAP_ON=${CSI}$'?7h'
declare -a  RESET_LINE=${CURSOR_OFF}${ERASE_LINE}${FRAME_OPEN}${FRAME_CLOSE}

declare -a  GRAPHIC_STR="| / - \\ + "
declare -a  GRAPHIC_STR10="/ | - \\ - "         #backwards STR
declare -i  SEC=0  # Seconds accumulator
declare -i  PREV_SEC=0

# Prevent segfault on stripping phases
if [[ "$BASHBIN" = "/tools/bin/bash" ]] ; then
  SLEEP=/tools/bin/sleep
else
  SLEEP=/bin/sleep
fi

function get_eta() {
  if [ -z "$1" ] || [ -z "$2" ]; then
    return
  fi
  SBUS=$1
  SBU=$2
  if [ "${SBUS%.*}.${SBUS#*.}" == $SBUS ]; then
    SBUS10=${SBUS%.*}${SBUS#*.} # multiply by 10: bash math has no decimal
  else
    SBUS10="${SBUS}0" # multiply by 10: bash math has no decimal
  fi
  SBUS10=$(( 10#$SBUS10 + 0 )) # strip leading zeros
  ETA10=$(( $SBUS10 * $SBU))
}

function catch_up_slowpoke() {
  if [ -z "$1" ] || [ -z "$2" ]; then
    return
  fi
  # It jumps a bunch and thats what you get for going slow.
  # The tradeoff is a high percentage of time spent sleeping.
  GET_TO=$1
  WE_AT=$2
  while [ $GET_TO -gt $WE_AT ]; do
    if [ $TOFRO -eq -1 ] && [ $SCREENPOS -gt 3 ]; then
      SCREENPOS=$(( $SCREENPOS + $TOFRO ))
      write_or_exit "${CSI}${SCREENPOS}G-"
    elif [ $TOFRO -eq 1 ] && [ $SCREENPOS -lt 62 ]; then
      SCREENPOS=$(( $SCREENPOS + $TOFRO ))
      write_or_exit "${CSI}${SCREENPOS}G+"
    fi
    WE_AT=$(( $WE_AT + 1 ))
  done
}

ETA10=0
PCNT=0
PPCNT=0
MOD_PCNT=0
PMOD_PCNT=0
SCREENPOS=3
TOFRO=1
SLOWPOKE=$5
if [ -n "$SLOWPOKE" ]; then
  # maybe got a number
  SLOWPOKE=${SLOWPOKE##*[^0-9]}
  SLOWPOKE=${SLOWPOKE%%[^0-9]*}
  SLOWPOKE="0${SLOWPOKE}"
  SLOWPOKE=$(( 10#$SLOWPOKE + 0 ))
  if [ $SLOWPOKE -eq 0 ]; then
    SLOWPOKE=1 # slowpokes have to go a least 1 second
  fi
fi
get_eta $3 $4

write_or_exit() {
    # make has been killed or failed or run to completion, leave
  [[ ! -e /proc/${MAKE_PPID} ]] && echo -n "${CURSOR_ON}" && exit

    # Target build complete, leave.
  [[ -f ${TARGET} ]] && echo -n "${CURSOR_ON}" && exit

    # It is safe to write to the screen
  echo -n "$1"
}

  # initialize screen
write_or_exit "${RESET_LINE}${TS_POSITION}0 min. 0 sec"

  # loop forever..
while true ; do

    if [ $ETA10 -gt 0 ]; then
      if [ $TOFRO -eq 1 ]; then
        GSTRING=${GRAPHIC_STR}
      else
        GSTRING=${GRAPHIC_STR10}
      fi
    else
      GSTRING=${GRAPHIC_STR}
    fi

      # Loop through the animation string
    for GRAPHIC_CHAR in ${GSTRING}; do
      SLEEP_TIME=.1
      if [ $ETA10 -gt 0 ]; then
        write_or_exit "${CSI}${SCREENPOS}G${GRAPHIC_CHAR}"
        if [ $ETA10 -lt 400 ]; then
          SLEEP_TIME=.05 # unlikely
        fi
        if [ -n "$SLOWPOKE" ]; then
          SLEEP_TIME=$SLOWPOKE # for some like it slow
        fi
      else
        write_or_exit "${CSI}$((SEC + 3))G${GRAPHIC_CHAR}"
      fi
      #$SLEEP .12 # This value MUST be less than .2 seconds.
      #$SLEEP .1 # Sleep time is also part of the fudgework
      $SLEEP $SLEEP_TIME # Sleep time is also part of the fudgework
    done

      # A BASH internal variable, the number of seconds the script
      # has been running. modulo convert to 0-59
    SEC=$(($SECONDS % 60))

      # Detect rollover of the seconds.
    if [ $ETA10 -eq 0 ]; then
      (( PREV_SEC > SEC )) && write_or_exit "${RESET_LINE}"
      (( PREV_SEC = SEC ))
    fi

      # Display the accumulated time. div minutes.. modulo seconds.
    write_or_exit "${TS_POSITION}$(($SECONDS / 60)) min. $SEC sec  "

    if [ $ETA10 -gt 0 ]; then

      PCNT=$(( $SECONDS * 1000 / $ETA10 ))
      if [ $PCNT -gt $PPCNT ]; then
        if [ $SCREENPOS -gt 7 ]; then
          # print percentage within left bracket
          write_or_exit "${CSI}3G${PCNT}% "
        fi
      fi
      PPCNT=$PCNT

      # Allow the thing to go back and forth since elapsed time
      # might exceed SBU estimates and/or fudgework.
      # It would rarely be exactly 100% because SBU times are approximate.
      MOD_PCNT=$(( $PCNT % 100 ))
      if [ $PMOD_PCNT -gt $MOD_PCNT ]; then
        TOFRO=$(( $TOFRO * -1 ))
      fi
      PMOD_PCNT=$MOD_PCNT

      SCRN_PCNT=$(( $SCREENPOS - 3 ))
      if [ $TOFRO -eq -1 ]; then
        SCRN_PCNT=$(( 60 - $SCREENPOS ))
      fi
      SCRN_PCNT=$(( $SCRN_PCNT * 100 / 60 ))

      if [ $(( $PCNT % 100 )) -gt $SCRN_PCNT ]; then
        # advance if stays in bounds
        if [ -n "$SLOWPOKE" ]; then
          catch_up_slowpoke $(( $PCNT % 100 )) $SCRN_PCNT
        fi
        if [ $TOFRO -eq -1 ] && [ $SCREENPOS -gt 3 ]; then
          SCREENPOS=$(( $SCREENPOS + $TOFRO ))
        elif [ $TOFRO -eq 1 ] && [ $SCREENPOS -lt 62 ]; then
          SCREENPOS=$(( $SCREENPOS + $TOFRO ))
        fi
      fi

    fi

done

exit



More information about the alfs-discuss mailing list