9

In a bash script sometimes you need the user to wait some seconds for a background process to finish.

I usually use for example:

sleep 10

How can I add a kind of progressbar to the script, so the user knows how long to wait?

I installed the command bar but I don't understand the manual.

2
  • Bash FAQ 44 Commented Sep 30, 2013 at 5:18
  • I saw that, but it is only usable for copying files. In that case, I added another answer below Commented Sep 30, 2013 at 5:28

6 Answers 6

16
while true;do echo -n .;sleep 1;done &
sleep 10 # or do something else here
kill $!; trap 'kill $!' SIGTERM
echo done

this will start an infinite while loop that echos a spinner every second, executed in the background.

Instead of the sleep 10 command run any command you want.

When that command finishes executing this will kill the last job running in the background (which is the infinite while loop)

source: https://stackoverflow.com/a/16348366/1069083

You can use various while loops instead, e.g. a spinner like this:

while :;do for s in / - \\ \|; do printf "\r$s";sleep 1;done;done
2
  • I wonder why you define the trap on SIGTERM only after waiting 10 seconds? To me that trap ... SIGTERM line should be just after the while....& Commented Jul 15 at 14:45
  • the sleep 10 is just an example command that does nothing for 10s. you should add the command youu want to see the spinner while it is executed there Commented Aug 6 at 7:52
7

This should be enough to get you started:

#!/bin/bash

for i in {001..100}; do
    sleep 1
    printf "\r $i"

done

Using the \r escape sequence returns the line to the start without a newline. This allows you to update the output without having hundreds of lines of output. By using this base, you could find a way to slowly print out an arrow such as =>25% ==>50% ===>75% instead of simply printing a number out. You could do this in a very basic way by using if-then logic to print out a specific number of ='s depending on the size of the number.

4

If you're looking for a way to add a progress bar to a timer with fixed sleeping time, you can also use a combination of echos and pv:

while true; do echo -n .; sleep 1; done | pv -s 10 -S -F '%t %p' > /dev/null

This produces a timer with progress bar:

0:00:02 [=====================>                                                      ] 30%

Explanation

  • while true; do echo -n .; sleep 1; done infinitely prints a . once a second.
  • | pv -s 10 -S -F '%t %p' pipes the output to pv that generates the progress bar
    • -s SIZE, --size SIZE defines the number of characters to expect
    • -S, --stop-at-size tells pv to stop after SIZE characters
    • -F FORMAT, --format FORMAT select what to print: elapsed time %t and progress bar %p
  • > /dev/null prevents printing the outputs of our echo to the terminal (they are piped through by pv)
  • select the timer duration with the -s flag (-s 10 → 10 characters → 10 seconds)
2
  • Any way to run this in background while waiting for the background jobs? Commented Jun 11, 2024 at 4:42
  • I don't see how waiting can be done easily; it introduces interprocess communication. Commented Jul 14 at 19:29
2

Here's one using cursor movements that will rewrite the line in order to show a countdown:

c=5 # seconds to wait
REWRITE="\e[25D\e[1A\e[K"
echo "Starting..."
while [ $c -gt 0 ]; do 
    c=$((c-1))
    sleep 1
    echo -e "${REWRITE}$c"
done
echo -e "${REWRITE}Done."
1

In case you want to see the progress of a file-copy process, you can simply use

pv source_file > destination_file

or

rsync --progress source_file destination_file

instead of the cp command

0

Just a little tweak on the answer by @orzechow. All credit should go to @orzechow. I just don't know how to put a whole program in a comment.

This is a shell script which can be run like: scriptName 13 to sleep for 13 seconds.

#!/bin/bash
#
# $Id: ohmSleepBar,v 1.2 2025/07/13 11:15:14 bennett Exp bennett $
#
# ohmSleepBar is like sleep with a text bar countdown
#
# First try.  It needs a lot of work.

if [[ -z "$1" ]] ; then
    echo "Usage: $0 <integer>" >&2
    echo "Sleeps for <integer> seconds showing a status bar." >&2
fi

# Clean up $1.
sleepTime=$(printf "%i" "$1" 2> /dev/null)

(for i in $(seq 1 $sleepTime) ; do echo -n . ; sleep 1 ; done) \
    | pv -X -s $sleepTime -F "%t %p"

Caveats: It might need bash. It does need a working seq. It does need a working printf. It requires a newer pv for the -X option. It's not terribly efficient, but it does capitalize on pv's use of the terminal.

Again, this was not my idea, but a sussed out version of @orzechow's. (thanks!)

-E

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.