Progress bar for shell apps

shot1.png

Downloading a file, backing up your database, or installing a package for your system are all instances of long lived processes that we encounter daily. When such a process takes a long time to finish, it is always nice to give some kind of visual cue to the user.

A nice and space efficient way to achieve the previous is to use a progress bar or a display counter. This tutorial aims to help you create such an output for a console application.

The hardest part in creating such an output is to figure out how to clear the current line in the output and replace it with another. Well, there is a clever trick involved. Instead of using an \n at the end of the string that is printed to stdout, you can use the \r escape value. These two are familiar values but the first jumps to the start of the line and moves one line down, while the second only moves to the beginning of the line and thus rewrites that line when a new output arrives.

Here is a nice little script that will display a progress bar – written in the ruby programming language, but easily translatable to any other.

def print_progress_bar(finished_percent)
  finished = "#" * finished_percent
  empty    = "-" * (100 - finished_percent)

  print "\r[ #{finished}#{empty} ] #{finished_percent}% "
end

(0..100).each do |count|
  print_progress_bar(count)
  sleep 1
end

Full line progress bars #

fullline.png

The above code snippet works great, if your terminal is wider than ±100 columns. Otherwise it just breaks up your lines and looks ugly. On the other hand if your terminal is much wider than 110 columns the output
is again strange because it leaves much of the space unfilled.

Here is a solution. If we execute the tput cols command it will return us the width of the terminal window in column numbers and we can use it to cleverly calculate the length of our progress bar. The improved — tputs using — code snippet follows

def print_progress_bar(finished_percent)
  fixed_space = 9 # for braces and number

  width = `tput cols`.to_f - fixed_space

  finished_count = ((finished_percent*width)/100).ceil
  empty_count    = width - finished_count

  finished = "#" * finished_count
  empty    = "-" * empty_count

  print "\r[ #{finished}#{empty} ] #{finished_percent}% "
end

(0..100).each do |count|
  print_progress_bar(count)
  sleep 1
end
 
18
Kudos
 
18
Kudos

Now read this

Sinatra app with RSpec

There are times when the only thing I want to create is a simple API. In the Ruby ecosystem the standard for creating something simple is Sinatra. But, there are a lot of things you miss in Sinatra that you have predefined in Rails.... Continue →