Skip to content

Instantly share code, notes, and snippets.

@StrangeRanger
Last active December 19, 2024 23:04
Show Gist options
  • Save StrangeRanger/12e9b8292dc0d499a4fea42fdda93c03 to your computer and use it in GitHub Desktop.
Save StrangeRanger/12e9b8292dc0d499a4fea42fdda93c03 to your computer and use it in GitHub Desktop.
spinning-waiting-stick.sh
#!/usr/bin/env bash
#
# Sometimes, it's helpful to show users that a background process is running and the
# script is awaiting its completion. A common approach is to display a spinner in the
# terminal until the process finishes. This script offers two methods to implement such
# a spinner, catering to different needs and scenarios.
#
########################################################################################
####[ Global Variables ]################################################################
## Used to colorize output.
C_GREEN="$(printf '\033[0;32m')"
C_BLUE="$(printf '\033[0;34m')"
C_NC="$(printf '\033[0m')"
C_CLRLN="$(printf '\r\033[K')"
readonly C_GREEN C_BLUE C_NC C_CLRLN
## Short-hand colorized messages.
readonly C_SUCCESS="${C_GREEN}==>${C_NC} "
readonly C_INFO="${C_BLUE}==>${C_NC} "
# The number of iterations to run in each loop.
readonly C_STOP_AT=1000000
# Enable debugging output.
readonly DEBUG=true
####[ Functions ]#######################################################################
####
# This version of the spinning stick will wait for a specific process to finish running
# before it stops spinning.
#
# WHEN TO USE:
# This version of the spinning stick is ideal when you want to wait for a specific
# process to finish running before continuing with the rest of the script.
#
# PARAMETERS:
# - $1: pid (Required)
# - The process ID of the process that the spinning stick will be waiting for.
spinner_one() {
local pid="$1"
local spin='-\|/'
local i=0
## While the specified process is running, output a new character from the $spin
## variable to create the illusion of a spinning stick.
while kill -0 "$pid" 2>/dev/null; do
i=$(( (i+1) % 4 ))
# ${parameter:offset:length} -> ${spin:$i:1}
printf "%sPerforming action: %s" "$C_CLRLN" "${spin:$i:1}"
sleep 0.1
done
}
####
# This version of the spinning stick will continue to spin until the function is stopped
# or killed.
#
# WHEN TO USE:
# This version of the spinning stick is ideal when you want to display a spinning
# stick as a background process. This is great when you need to perform a task where
# modifications to a variable must persist.
spinner_two() {
local spin='-\|/'
local i=0
## Continuously output a new character from the $spin variable to create the
## illusion of a spinning stick.
while true; do
i=$(( (i+1) % 4 ))
# ${parameter:offset:length} -> ${spin:$i:1}
printf "%sPerforming action: %s" "$C_CLRLN" "${spin:$i:1}"
sleep 0.1
done
}
####[ Main ]############################################################################
###
### [ Example of Using 'spinner_one' ]
###
### In the following example, I run a loop that will increment a variable from 0 to
### 1,000,000. This loop will run as a subprocess in the background. The 'spinner_one'
### function will display a spinning stick that will continue to spin until the
### subprocess is finished running.
###
iteration=0
echo -e "${C_INFO}Executing 'spinner_one'"
[[ "$DEBUG" == true ]] && start_time=$(date +%s%N)
## Operations that will run in the background, while the spinning stick is displayed.
{
for ((i = 0; i <= C_STOP_AT; i++)); do
iteration="$i"
done
printf "%sOutput of 'iteration': %s\n" "$C_CLRLN" "${C_GREEN}${iteration}${C_NC}"
} &
spinner_one "$!"
if [[ "$DEBUG" == true ]]; then
end_time=$(date +%s%N)
elapsed=$(( (end_time - start_time)/1000000 ))
echo -e "${C_SUCCESS}Done with 'spinner_one' in ${elapsed} ms\n"
else
echo -e "${C_SUCCESS}Done with 'spinner_one' example\n"
fi
###
### [ Example of Using 'spinner_two' ]
###
### In the following example, I run a loop that will increment a variable from 0 to
### 1,000,000. Compared to the previous example, the 'spinner_two' function will run in
### the background instead of the operations. This is because I want the changes to the
### 'iteration' variable to persist.
###
iteration=0
echo -e "${C_INFO}Executing 'spinner_two'"
[[ "$DEBUG" == true ]] && start_time=$(date +%s%N)
# Start the spinner.
spinner_two &
spinner_pid=$!
# Main workload runs in the main shell.
for ((i = 0; i <= C_STOP_AT; i++)); do
iteration="$i"
done
# Work is done; stop the spinner.
kill "$spinner_pid" 2>/dev/null
wait "$spinner_pid" 2>/dev/null
# Clean up and output the result.
printf "%sOutput of 'iteration': %s\n" "$C_CLRLN" "${C_GREEN}${iteration}${C_NC}"
if [[ "$DEBUG" == true ]]; then
end_time=$(date +%s%N)
elapsed=$(( (end_time - start_time)/1000000 ))
echo "${C_SUCCESS}Done with 'spinner_two' in ${elapsed} ms"
else
echo -e "${C_SUCCESS}Done with 'spinner_two' example\n"
fi
@StrangeRanger
Copy link
Author

Project Tracker

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment