0

I am trying to print a progress bar in shell as follows

#!/bin/bash sleep $1 & PID=$! printf "[" counter=1 while kill -0 $PID 2> /dev/null; do percent_done=$(echo "${counter}*100/${1}" | bc -l ) printf "▓%.0f" $percent_done sleep 1 counter=$counter+1 done printf "] done!" 

and i call it as

myScript.sh 10

so the script will invoke a sleep of 1 sec for 10 seconds. On each iteration of the loop, i want to print

▓<the percentage of the loop completed>

for instance i expect

▓10▓20▓30▓40▓50▓60 and so on. but i get

▓10▓11▓12▓13▓14▓15 instead

what am i doing wrong? am i not updating the counter properly? also, if i do

percent_done=$(echo "${counter}/${1}" | bc -l )

then i get

▓0.1000000000000▓1.100000000000000000▓2.10000000000000000000▓3.10000000000000000000▓ and so on.

Since I dont want the long floating point value, i added the *100 in the expression.

What am i doing wrong?

2
  • 1
    Why are you using floating-point math here? Work with 100 rather than 1.0 as your point of completion, and there's no need for bc; even if you want five decimal points of precision, you could work with a number out of 10000, divide by (the integer) 100 to get the value to the left of the decimal point, take the remainder of dividing by 100 to get the value to the right, and again, you have a value with purely integer math (and thus no need for bc or %f format strings) at all. Commented Apr 20, 2018 at 20:31
  • hmm, makes sense, but i really dont know how to translate that into the shell script. could you please help me out with the syntax a bit? i am not that familiar with shell scripting Commented Apr 20, 2018 at 20:38

2 Answers 2

2
$ function tt { sleep $1 & PID=$! printf "[" counter=10 while kill -0 $PID 2> /dev/null; do percent_done=$(echo "${counter} |bc -l) printf "▓%.0f" $percent_done sleep 1 counter=$counter+10 done printf "] done!" } $ tt 10 [▓10▓20▓30▓40▓50▓60▓70▓80▓90▓100 

You can further simplify your code by declaring an integer to bash:

$ function t { sleep $1 & PID=$!; printf "["; declare -i counter=10; while kill -0 $PID 2> /dev/null; do printf "▓%.0f" $counter; sleep 1; counter=$counter+10; done; printf "] done!"; } $ t 20 [1] 4227 [▓10▓20▓30▓40▓50▓60▓70▓80▓90▓100▓110▓120▓130▓140▓150▓160▓170▓180▓190▓200[1]+ Done 
Sign up to request clarification or add additional context in comments.

3 Comments

thanks, but for an input of 20, the above prints [▓5▓15▓25▓35▓45▓55▓65▓75▓85▓95▓105▓115▓125▓135▓145▓155▓165▓175▓185▓195] done!
@AbtPst Updated further
thanks for your help. i would really like to print the percentage of the loop completed thus far. so even for an input of 20, i should only see numbers between 0 and 100
1

I guess counter=$counter+1 does not do what you expect. It just concatenates $counter and "+1". So after n iterations you'll have some sting like "1+1+...+1" (n+1-times). You have that in the expression you pipe to bc and operator precedence (* before +) then leads to the unexpected results.

Change counter=$counter+1 to also use bc as you've done above, thus counter=$(echo "${counter}+1" | bc).

Or change percent_done=$(echo "${counter}*100/${1}" | bc -l ) to percent_done=$(echo "(${counter})*100/${1}" | bc -l ) (note the parenthesis) to overrule operator precedence, if for some reason you really want such a "1+1..+1" string.

1 Comment

counter=$(( counter + 1 )) is far more efficient. bc is needed for floating-point math, but for integer math it's utterly unnecessary and (like any other external command) takes several orders of magnitude longer to invoke than shell-builtin functionality.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.