4

I have made a pretty basic progress bar in BASH like this :-

doned=$1 #amount completed total=$2 #total amount doned=`echo $doned $total | awk '{print ($1/$2)}'` total=`tput cols | awk '{print $1-10}'` doned=`echo $doned $total | awk '{print int(($1*$2))}'` echo -n $doned"% [ " for i in $(seq 1 $doned); do echo -n "=" done for i in $(seq $((doned+1)) $total); do echo -n "-" done echo " ]" 

This works exactly like I want it to.

This script runs within a loop in another script. I want it to always display at the bottom of the terminal, or any other fixed place.

The loop is somewhat like this:-

for i in 10 20 30 40; do echo -n "Do you want to continue on to the next step? (y/n): "; read $yn if [[ "$yn" == "n" ]] || [[ "$yn" == "N" ]]; then exit 1; # stop the script d_3=0; fi doned=`some command` ### Get number of completed files totalss=`some other command` ### Get total number of files bash ./libraries/prog.sh $doned $totalss done 

So what I want is that the progress bar remain at the bottom even when inputting values, or displaying something. Is there a way to do this, preferably without having to install anything extra? Most of the computers I want to use this script on are either Debian version 8+ or Ubuntu 16+ systems

10
  • 1
    Have you considered using things like pv, or dialog/whiptail --gauge? Commented Sep 12, 2018 at 16:03
  • No, I hadn't. I wanted opinions on what would be the best way to achieve this, if possible, without having to install anything new, so that it works on any system I want to use from the get go Commented Sep 12, 2018 at 16:44
  • You may want to specify those requirements more clearly in the question. For instance, many systems covered by U&L.SE don't have bash or seq installed (those are GNU software found by default on GNU systems but few others). tput is a standard command, but tput cols is not standard... Commented Sep 12, 2018 at 16:51
  • Oh okay. I did not know this. But let's just say, all the computers I want to use it on have at least everything I have used already available to them. I just want this script to be usable on about the 30 Debian based systems in my lab. Commented Sep 12, 2018 at 16:59
  • And as I said, I might install some packages to do this if I need to. But here I was hoping for somethng not dependent on packages, if possible obviously. Commented Sep 12, 2018 at 17:03

3 Answers 3

5

I wrote a testing script to try and do what @MatrixManAtYrService suggested. I realized that this solution does not apply to all systems covered by U&L SE, but this works within the specifications I asked for.

#!/bin/bash # go to last line and print the empty progress bar tput sc #save the current cursor position tput cup $((`tput lines`-1)) 3 # go to last line echo -n "[" # the next 5 lines just print the required stuff to make the bar for i in $(seq 1 $((`tput cols`-10))); do echo -n "-" done echo -n "]" tput rc # bring the cursor back to the last saved position # the actual loop which does the script's main job for i in $(seq 0 10 100); do # print the filled progress bar tput sc #save the current cursor position doned=${i} #example value for completed amount total=100 #example value for total amount doned=`echo $doned $total | awk '{print ($1/$2)}'` # the next three lines calculate how many characters to print for the completed amount total=`tput cols | awk '{print $1-10}'` doned=`echo $doned $total | awk '{print int(($1*$2))}'` tput cup $((`tput lines`-1)) 4 #go to the last line for l in $(seq 1 $doned); do #this loop prints the required no. of "="s to fill the bar echo -n "=" done tput rc #bring the cursor back to the last saved position # the next 7 lines are to find the row on which the cursor is currently on to check if it # is at the last line # (based on the accepted answer of this question: https://stackoverflow.com/questions/2575037/) exec < /dev/tty oldstty=$(stty -g) stty raw -echo min 0 tput u7 > /dev/tty IFS=';' read -r -d R -a pos stty $oldstty row=$((${pos[0]:2} - 1)) # check if the cursor is on the line before the last line, if yes, clear the terminal, # and make the empty bar again and fill it with the required amount of "="s if [ $row -gt $((`tput lines`-2)) ]; then clear tput sc tput cup $((`tput lines`-1)) 3 echo -n "[" for j in $(seq 1 $((`tput cols`-10))); do echo -n "-" done echo -n "]" tput cup $((`tput lines`-1)) 4 for k in $(seq 1 $doned); do echo -n "=" done tput rc fi # this is just to show that the cursor is behaving correctly read -p "Do you want to continue? (y/n)" yn; done # the next few lines remove the progress bar after the program is over tput sc # save the current cursor position tput cup $((`tput lines`-1)) 3 # go to the line with the progress bar tput el # clear the current line tput rc # go back to the saved cursor position 

There must be better ways to handle overflow into the last line, instead of clearing the terminal, among other things. This serves more as a proof of concept of what @MatrixManAtYrService suggested. Any improvements or comments on its limitations are welcome

3

Maybe, but I think it's going to be harder than you expect.

You can make the cursor move around the terminal by outputting ANSI control codes. For more on them, see here.

In principle, you could move the cursor to your progress bar, add an =, and then move it back to wherever you plan to print output. Then on the next = you'd have to do it again... but this would probably be ugly.

If you're willing to depart from bash, you can probably find libraries that make this kind of thing easier (like this).

5
  • I don't really want to depart from bash if possible. I will try what you suggested, see if it works for me. Commented Sep 12, 2018 at 16:46
  • I did what you suggested, it worked. A lot of putting the cursor somewhere and then putting it back. Maybe I should just use something else as everyone is suggesting. But since this is exactly what I asked, accepted. Commented Sep 12, 2018 at 17:36
  • 1
    I also wrote an example script to check this. Should I post it here? Commented Sep 12, 2018 at 17:37
  • I usually put example code in a github gist so that the link fits nicely in a comment. Alternatively, you can answer your own question and post the bash directly. Either way, somebody else might have you same question and benefit from seeing your code, so if you bothered to write it you might as well post it somewhere. Commented Sep 12, 2018 at 18:27
  • Did post an answer, for what its worth Commented Sep 12, 2018 at 20:11
2

An easy way to get progress bars in a shell script is to use whiptail or dialog, as mentioned in comments. One or both should be available on most linux distros.

OP mentioned the aptitude installer in comments. Aptitude is a binary which uses the ncurses library, as does dialog.

2
  • I finally did use curses for doing the actual work, which is why I accepted Matrixman's answer. I had just asked for a solution which uses no extra tools. And I did not want a separate buffer for the loader, which is why I did not want to use dialog or whiptail. Commented Jan 23, 2019 at 23:18
  • Upvoted because of the information about aptitude Commented Jan 23, 2019 at 23:19

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.