linux, OYB software, shell

A progress bar for the shell

I would like to share this little shell function that I wrote to spice up my shell scripts. It is a simple progress bar that you can launch for a number of seconds, to get an idea of how long there is still to wait.

It uses partial Unicode blocks to achieve subcharacter precision.

Usage

Include the function in your code, or source the file

$ source progress_bar.sh
$ progress_bar 60             # 60 seconds
│█████████████████████████████████████████████████████████▌                                  │ 62%

You can use time on a long task to get the duration, and then

$ time ./compile.sh                                                            # gives 300 seconds
$ ./compile.sh &>/dev/null & progress_bar 300
│███████████████████████████████                                                             │ 31%

Installation

Clone the code

git clone https://github.com/nachoparker/progress_bar.sh

Include the function in your code, or source the file.

You can also add it to your .zshrc or .bashrc to use it as any other command.

cat progress_bar.sh >> ~/.bashrc
cat progress_bar.sh >> ~/.zshrc

It requires a terminal session with unicode locales, for instance you might have in your .zshrc

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8

Configuration

You can play around with the INT parameter for more smoothness.

Code

#!/bin/bash

# Show a progress bar for $1 seconds
#
# Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
# GPL licensed (see end of file) * Use at your own risk!
#
# Example: progress_bar 60
#

progress_bar()
{
  local DURATION=$1
  local INT=0.25      # refresh interval

  local TIME=0
  local CURLEN=0
  local SECS=0
  local FRACTION=0

  local FB=2588       # full block

  trap "echo -e $(tput cnorm); trap - SIGINT; return" SIGINT

  echo -ne "$(tput civis)\r$(tput el)│"                # clean line

  local START=$( date +%s%N )

  while [ $SECS -lt $DURATION ]; do
    local COLS=$( tput cols )

    # main bar
    local L=$( bc -l <<< "( ( $COLS - 5 ) * $TIME  ) / ($DURATION-$INT)" | awk '{ printf "%f", $0 }' )
    local N=$( bc -l <<< $L                                              | awk '{ printf "%d", $0 }' )

    [ $FRACTION -ne 0 ] && echo -ne "$( tput cub 1 )"  # erase partial block

    if [ $N -gt $CURLEN ]; then
      for i in $( seq 1 $(( N - CURLEN )) ); do
        echo -ne \\u$FB
      done
      CURLEN=$N
    fi

    # partial block adjustment
    FRACTION=$( bc -l <<< "( $L - $N ) * 8" | awk '{ printf "%.0f", $0 }' )

    if [ $FRACTION -ne 0 ]; then 
      local PB=$( printf %x $(( 0x258F - FRACTION + 1 )) )
      echo -ne \\u$PB
    fi

    # percentage progress
    local PROGRESS=$( bc -l <<< "( 100 * $TIME ) / ($DURATION-$INT)" | awk '{ printf "%.0f", $0 }' )
    echo -ne "$( tput sc )"                            # save pos
    echo -ne "\r$( tput cuf $(( COLS - 6 )) )"         # move cur
    echo -ne "│ $PROGRESS%"
    echo -ne "$( tput rc )"                            # restore pos

    TIME=$( bc -l <<< "$TIME + $INT" | awk '{ printf "%f", $0 }' )
    SECS=$( bc -l <<<  $TIME         | awk '{ printf "%d", $0 }' )

    # take into account loop execution time
    local END=$( date +%s%N )
    local DELTA=$( bc -l <<< "$INT - ( $END - $START )/1000000000" \
                   | awk '{ if ( $0 > 0 ) printf "%f", $0; else print "0" }' )
    sleep $DELTA
    START=$( date +%s%N )
  done

  echo $(tput cnorm)
  trap - SIGINT
}

# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

Author: nachoparker

Humbly sharing things that I find useful [ github dockerhub ]

6 Comments on “A progress bar for the shell

  1. This is great. I’m a little new to shell scripting so I have a newbie question: how would I incorporate this into a function? Any chance you could point me to a basic example so I can experiment with this?

      1. You don’t actually need to say “function” in BASH. The following will do:
        progress_bar () {
        echo “YOUR CODE GOES HERE INSTEAD OF THE ECHO”
        }

Leave a Reply to Diveyez Cancel reply

Your email address will not be published. Required fields are marked *