Buy this tutorial as a PDF for only $5
28 Mar 2018
Spinner
Creating a simple Spinner to show a script is still running
In this tips section we already have a Progress Bar article, which will show updates to the user whilst some long-running process is happening in the background.
This is similar, but in some ways the opposite: This spinner runs in the background, whilst we wait for a long-running process in the foreground to complete. Also, because it does not know how long it will be running for, instead of processing from the left to the right of the screen, it simply spins indefinitely, a bit like those irritating mouse cursors when Windows is having one of its 'moments'.
The specific reason I wrote this today, was that a certain environment had a firewall which would time-out inactive sessions. My script was using cUrl to call into a web service which would take many minutes to complete, before sending output back to the script. In the meantime, the firewall would have decided that nothing was happening in the session, and kill the SSH session, logging the user out in the process. So having this regularly updating the screen is one way to ensure that the connection stays alive. (see "Keepalive" notes below)
For the purposes of demonstration, this script uses http://slowwly.robertomurray.co.uk/, a useful service which is deliberately slow to respond. When it does, we get it to pass us the contents of https://www.shellscript.sh/tips/spinner/test.txt, a simple test file containing a couple of lines of text:
Hello. This is the test file.
The command as it runs:

Download spinner.sh
#!/bin/bash spin() { spinner="/|\\-/|\\-" while : do for i in `seq 0 7` do echo -n "${spinner:$i:1}" echo -en "\010" sleep 1 done done } echo "About to make a slow web call..." # Start the Spinner: spin & # Make a note of its Process ID (PID): SPIN_PID=$! # Kill the spinner on any signal, including our own exit. trap "kill -9 $SPIN_PID" `seq 0 15` # http://slowwly.robertomurray.co.uk/ is a useful # webservice - it is deliberately slow! curl -L http://slowwly.robertomurray.co.uk/delay/15000/url/https://www.shellscript.sh/tips/spinner/test.txt echo "Finished." # If the script is going to exit here, there is nothing to do. # The trap above will kill the spinner when this script exits. # Otherwise, if the script is going to do more stuff, you can # kill the spinner now: kill -9 $SPIN_PID
The spin() function
The spin
function defines the $spinner
variable as a string containing
each of the characters of the spinner: / | \ - / | \ -
. Each of these is displayed in turn.
Notice that the backslashes (\) have to be escaped; otherwise, \-
would be interpreted
as a backslashed -
symbol, and the backslash itself would be ignored.
The loop then displays ${spinner:$i:1}
- that is, the i
th character of the $spinner
variable. The -n
ensures that the echo
is not
followed by a newline. Then the echo -en "\010"
emits Octal character 010, which is the
backspace code in ASCII. This puts the cursor back over the previous character, ready to overwrite it.
After a sleep 1
, it goes through the loop again.
Because the whole thing is wrapped in a while :
loop, it will continue displaying the
spinner forever (until it is kill
ed).
The calling process
The calling process runs spin
in the background; this will execute in a separate shell.
The caller makes a note of its Process ID (PID) via the special variable $!
. $!
always contains the PID of the most recently-run background process.
The trap
tells the shell to run kill -9 $SPIN_PID
on any signal 0-15. This
ensures that as soon as our script exits (Signal 0), is interrupted (Signal 1), etc, the spinner is kill
ed. If the spinner kept on running after the main script had ended for whatever reason, then the display would look messy.
Now the spinner has been set up, the main script calls the curl
command, which will
potentially take a long time. The spinner will be updated every second, while the curl
displays nothing as it waits for a response. The spinner is now keeping the terminal's SSH session alive.
Finally, the script can choose to kill -9 $SPIN_PID
or - if it is about to terminate
anyway, it can allow the trap
to run that automatically as the parent script exits.
A nice side-effect of the way in which the spin()
function displays a character
and then immediately backspaces over it, is that when it is kill
ed, it is almost guaranteed that
the spinner character itself will not be shown. There is no sign, once the script has finished,
that the spinner was ever there at all.
Footnote: Keepalive
It is possible for the SSH protocol to keep the session alive, even if nothing appears to be happening. With traditional SSH, you could put this in your $HOME/.ssh/config
, to send a "keepalive" message every 60 seconds, and only tear down the connection if 3 consecutive keepalives are dropped:
Host * ServerAliveInterval 60 ServerAliveCountMax 3
On the server side, you can also enforce this; add these entries to /etc/ssh/sshd_config
and restart sshd
:
ClientAliveInterval 60 ClientAliveCountMax 3
With the PuTTY SSH client, you can put a value (say, 60) by "Seconds between keepalives (0 to turn off)" in the "Connection" tab.
Appreciate this site? Please consider making a donation:
Books and eBooks
My Shell Scripting books, available in Paperback and eBook formats.
![]() Shell Scripting Tutorial is this tutorial, in 88-page Paperback and eBook formats. Convenient to read on the go, and to keep by your desk as an ever-present companion. | ![]() Shell Scripting: Expert Recipes for Linux, Bash and more is my 564-page book on Shell Scripting. The first half explains the features of the shell; the second half has real-world shell scripts, organised by topic, with detailed discussion of each script. |
Contact
You can mail me with this form. If you expect a reply, please ensure that the address you specify is valid. Don't forget to include the simple addition question at the end of the form, to prove that you are a real person!