This post is part of a series: Bash Tips and Tricks.
I’ve mentioned before that I like to show users of my scripts useful information. This can be a complex progress bar, a simple progress meter, or it can be an animation to let the user know the script hasn’t hung. These animations are typically called Throbbers are exist purely to tell the user to continue patiently waiting. Sometimes folks like me call Throbbers Spinners for two reasons. First, a very common throbber animation type is the spinning wheel. Second, the word throbber sounds oddly sexual, and sort of creeps me out if I say it too many times in a sentence (Just kidding. Well, no, not really.)
This tip will show you to create a spinner for your Bash scripts. If you have a long running process and don’t want to try to tell the user approximately how much of that process is left to run, showing them a spinner is a great alternative.
Implementing the Spinner
The throbber, er I mean spinner, is implemented as a loop that shifts a string during each iteration.
spinner()
{
local pid=$1
local delay=0.75
local spinstr='|/-\'
while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
local temp=${spinstr#?}
printf " [%c] " "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
Let’s dissect the spinner function so to illustrate how it works.
local pid=$1
local delay=0.75
local spinstr='|/-\'
This is pretty self-explanatory. The function has one input argument and two internal variables that control how it works.
The input argument is assigned to the local variable
pid. This is the process ID of the background task for which we are showing the spinner.The next local variable,
delay, is how long each frame of the spinner animation stays visible for. We’ve set it to 75% of 1 second or 750ms. A smaller number will make the spinner rotate faster while a larger number will make it slower.The final local variable is called
spinstr. This is a string for which each character is a frame in our spinner animation. As you can see, the string is 4 characters long, therefore we have four frames in our animation.
Moving along we get to the meat of the function, the primary loop.
while [ "$(ps a | awk '{print $1}' | grep -w $pid)" ]; do
The loop condition does three things:
ps awill show all processes.awk '{print $1}'will extract the pid column of the process list.grep -w $pidwill look for the process ID of our background task in the list of PIDs printed byawk.
The loop is conditioned on the return value of grep, the last command in our
pipe chain. If grep finds a match in our PID list it will return 0, otherwise
it will return 1.
The next several lines do the hard work of displaying our animation. I’ve created two images to help illustrate what’s going on.
First, I remove the first character from the string and save the remaining
characters into a temp.
local temp=${spinstr#?}
Then I use printf to output the first character of spinstr, which contains
our animation. Only the first character is output because I use the %c
format string.
printf " [%c] " "$spinstr"
These two steps are illustrated below.

Finally, I shift spinstr by constructing a new string that contains the
value of temp and all characters from spinstr that aren’t in temp.
local spinstr=$temp${spinstr%"$temp"}
The first part of this, the character rotation, appears as step 3 and the last
part, the assignment to spinstr appears as step 4.

Using the Bash Spinner
When you have a task to run that will take a large (or unknown) amount of time invoke it in a background subshell like this:
(a_long_running_task) &
Then, immediately following that invocation, call the spinner and pass it the PID of the subshell you invoked.
spinner $!
The $! is a bash internal variable for the PID of the
last job run in the background. In this case, it will give us the PID of the
bash shell executing our long running task.
When it’s all said and done you’ll have a nice and simple Bash spinner like the one below.

Other posts you might like:



