How can I show spinner till command line finish it is job? In other words, If I am running a script and I want to show spinner while this script is running and the spinner disappears when the script finish it is job.

Bellow is a common spinner code:

echo -n ' '
while true
printf "\b${sp:i++%${#sp}:1}"

How can I link the previous spinner code to a command to let it show spinner while the command is running and the spinner disappears when the command finish it is job? If I include the command inside the loop it will loop with the spinner so what is the solution in this case?


Have your while loop watch for your real command to exit. I'll assume a Linux environment that has /proc entries for each PID, but you could slice it other ways:

# your real command here, instead of sleep
sleep 7 &
echo -n ' '
while [ -d /proc/$PID ]
  printf "\b${sp:i++%${#sp}:1}"
| improve this answer | |
  • 9
    This is a busy loop that will eat up cpu resources. I'd suggest having a delay of some kind in your while loop. – ACase Jul 20 '16 at 14:36
  • This makes sense and seems to be the simplest method, but it doesn't work for me (on MacOS), so I'm trying to make it work. What does -d /proc/$PID do? – matharden Jul 7 at 11:04
  • -d /proc/$PID, as part of the test command [ ... ], checks whether /proc/$PID exists as a directory (after expanding the $PID variable, of course). It's a quick way in Linux to see if a process exists with that PID. – Jeff Schaller Jul 7 at 11:49

This shell script should do what you're looking for:

#!/usr/bin/env bash

  local -r pid="${1}"
  local -r delay='0.75'
  local spinstr='\|/-'
  local temp
  while ps a | awk '{print $1}' | grep -q "${pid}"; do
    printf " [%c]  " "${spinstr}"
    sleep "${delay}"
    printf "\b\b\b\b\b\b"
  printf "    \b\b\b\b"

("$@") &
show_spinner "$!"

Assuming you store the shell script in a file named spinner, you can invoke it like this to display a spinner while the command sleep 10 is running:

$ spinner sleep 10
| improve this answer | |

Here's another fancy spinner which you can use like this:

spinner ping google.com
echo "ping exited with exit code $?"

spinner sleep 10
echo "sleep exited with exit code $?"

It has 12 themes and picks one randomly.

# Shows a spinner while another command is running. Randomly picks one of 12 spinner styles.
# @args command to run (with any parameters) while showing a spinner. 
#       E.g. ‹spinner sleep 10›

function shutdown() {
  tput cnorm # reset cursor
trap shutdown EXIT

function cursorBack() {
  echo -en "\033[$1D"

function spinner() {
  # make sure we use non-unicode character type locale 
  # (that way it works for any locale as long as the font supports the characters)
  local LC_CTYPE=C

  local pid=$1 # Process Id of the previous running command

  case $(($RANDOM % 12)) in
    local spin='⠁⠂⠄⡀⢀⠠⠐⠈'
    local charwidth=3
    local spin='-\|/'
    local charwidth=1
    local spin="▁▂▃▄▅▆▇█▇▆▅▄▃▂▁"
    local charwidth=3
    local spin="▉▊▋▌▍▎▏▎▍▌▋▊▉"
    local charwidth=3
    local spin='←↖↑↗→↘↓↙'
    local charwidth=3
    local spin='▖▘▝▗'
    local charwidth=3
    local spin='┤┘┴└├┌┬┐'
    local charwidth=3
    local spin='◢◣◤◥'
    local charwidth=3
    local spin='◰◳◲◱'
    local charwidth=3
    local spin='◴◷◶◵'
    local charwidth=3
    local spin='◐◓◑◒'
    local charwidth=3
    local spin='⣾⣽⣻⢿⡿⣟⣯⣷'
    local charwidth=3

  local i=0
  tput civis # cursor invisible
  while kill -0 $pid 2>/dev/null; do
    local i=$(((i + $charwidth) % ${#spin}))
    printf "%s" "${spin:$i:$charwidth}"

    cursorBack 1
    sleep .1
  tput cnorm
  wait $pid # capture exit code
  return $?

("$@") &

spinner $!
| improve this answer | |
  • Very interesting, but not working at all on macOS zsh. – kitsune Jul 11 at 8:41
  • it is a bash script. If you chmod +x it and run it via ./script it'll be run with /bin/bash (even if your shell runs zsh). I just tried it (with Linux though) and I don't see why it shouldn't work with MacOS. – Jonas Eberle Jul 12 at 9:59
  • 1
    Of course I passed the script under chmod! :D However, the case 1) works fine, but other cases, for example 11) seems that cursorBack() doesn't work, so I get a long line of characters printed in succession. – kitsune Jul 13 at 8:29
  • Seems not to be a zsh or bash problem I guess. I think your terminal does not know what to do with `echo -en "\033[$1D"`` - maybe we could find a sequence for "cursorBack" which is more portable even for unusual terminal settings? – Jonas Eberle Jul 14 at 11:56
  • Man.. this is a beauty.. thank you for the inspiration!! – TacB0sS Nov 21 at 0:36

If you want a lowest common denominator spinner that works with /bin/sh and doesn't rely on the extended bash parameter substitution this should work:


# The command you are waiting on goes between the ( ) here
# The example below returns a non zero return code

(sleep 20 ; /bin/false) &

pid=$! ; i=0
while ps -a | awk '{print $1}' | grep -q "${pid}"
    c=`expr ${i} % 4`
    case ${c} in
       0) echo "/\c" ;;
       1) echo "-\c" ;;
       2) echo "\\ \b\c" ;;
       3) echo "|\c" ;;
    i=`expr ${i} + 1`
    # change the speed of the spinner by altering the 1 below
    sleep 1
    echo "\b\c"

# Collect the return code from the background process

wait ${pid}

# You can report on any errors due to a non zero return code here

exit ${ret}
| improve this answer | |

So while all the above answer work.. I thought to add mine:

DEFAULT_SpinnerFrames=("—" "\\" "|" "/")

## @function: spinner(action, label, &spinnerFramesRef[])
## @description: Perform an action asynchronously and display
## spinner till action is completed
## @param action: The action the execute
## @param label: The label to display while waiting
## @param spinnerRef: In case you feel like a custom spinner, pass a ref to an array of strings
spinner() {
  local frameRef
  local action="${1}"
  local label="${2} "
  local spinnerRef="${3-DEFAULT_SpinnerFrames}"
  local spinnerFrames=$(eval "echo \${!${spinnerRef}[@]}")

  spinnerRun() {
    while true; do
      for frame in ${spinnerFrames[@]}; do
        echo "${label}${!frameRef}"
        tput cuu1 tput el
        sleep 0.2
    echo -e "\r"

  spinnerRun &
  local spinnerPid=$!
  kill "${spinnerPid}"

github tests

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy