- Use
#!/usr/bin/env bash instead of #!/bin/bash;
- Use only lowercase for your variable names;
- Do not mix
echo and printf commands;
- Do not mix
$... and %... in printf arguments;
function fct_name () { ... } is older, use simply syntax fct_name () { ... }
- Do not declare variables in loops;
- To prevent errors in tests, redirect all outputs in trash with
>/dev/null 2>&1 like this:
while my_cmd >/dev/null 2>&1; do
...do something...
done
- It's not necessary to put your command in
[ ... ] or [[ ... ]] blocks to test condition or loop. The return code of command is used. Example:
while ps -p $the_pid >/dev/null 2>&1; do
...
done
- Like many commands (
ls, find...), put a grep after ps is ugly and dangerous. Try to use command options often as possible
- With
ps command, use -p, --ppid options to filter on process and -o to reduce output to the minimum necessary;
- Use
shellcheck tool on command line to improve your programs. If you use Visual Studio Code or VS Codium, install the ShellCheck extension;
- When you often move the cursor with ANSI sequence, hide the cursor. Of course, do not forget to restore the cursor visible;
- Don't forget to restore ANSI modes and cursor if keystoke [CTRL]+[C] signal is used on your program (see
trap command).
So, my spinner program result based on your work:
#!/usr/bin/env bash
# ASCII characters
declare -r char_esc=$'\e'
# ANSI sequences
declare -r ansi_csi="${char_esc}["
declare -r normal_mode="${ansi_csi}0m"
declare -r fg_green="${ansi_csi}32m"
declare -r fg_red="${ansi_csi}31m"
declare -r fg_purple="${ansi_csi}35m"
declare -r fg_yellow="${ansi_csi}33m"
declare -r fg_cyan="${ansi_csi}36m"
declare -r cursor_visible="${ansi_csi}?25h"
declare -r cursor_unvisible="${ansi_csi}?25l"
declare -r cursor_save_position="${ansi_csi}s"
declare -r cursor_restore_position="${ansi_csi}u"
declare -r erase_line="${ansi_csi}2K"
# On exit, restore mode and cursor visibility
# Useful when you use [CTRL]+[C]
on_sigint () {
printf "%s" "$normal_mode$cursor_visible"
printf "\nBye!\n"
exit 1
}
trap on_sigint SIGINT
# Run quietly
run_quietly () {
"$@" > /dev/null 2>&1
}
# Check command status and print something
declare -r text_ok="${fg_green}✔${normal_mode}"
declare -r text_error="${fg_red}🞮${normal_mode}"
check_cmd () {
local -i pid=$1
if wait $pid; then
printf "[%s]\n" "$text_ok"
else
printf "[%s]\n" "$text_error"
fi
}
# Run a spinner on a process
run_spinner () {
local -i pid=$1
local delay=0.1
local spinstr='⠖⠲⠴⠦'
local text_size=50
printf "%s" "$cursor_unvisible$cursor_save_position"
# shellcheck disable=SC2155
local text=$(ps --ppid $pid -o args= 2>/dev/null)
# shellcheck disable=SC2181
while [[ $? -eq 0 ]]; do
printf "%s[%s] %-*.*s" "$cursor_restore_position$normal_mode" "$fg_yellow${spinstr:0:1}$normal_mode" "$text_size" "$text_size" "$text"
spinstr="${spinstr#?}${spinstr:0:1}"
sleep $delay
text=$(ps --ppid $pid -o args= 2>/dev/null)
done
printf "%s" "$erase_line$cursor_restore_position$cursor_visible"
}
# Run a command quietly with a spinner
run_cmd_with_spinner () {
local -i pid=0
# Do not forget the "&"
run_quietly "$@" &
pid=$!
run_spinner $pid
check_cmd $pid
}
# My program
my_long_command () {
sleep 5
sleep 2
sleep 1
# return 0 # Just for test
return 1 # Just for test
}
run_cmd_with_spinner my_long_command
SystemUpdatewith two arguments,SPINNERandCheckCmd. That's not what you want.tputcommands instead of hard coded ANSI colors? Check:for i in {0..7}; do tput setaf $i; echo "tput setaf $i"; tput sgr0; doneQUIET()(see choroba's comment), and keeping in mind that you need to separate commands with a newline or semicolon, wouldQUIET SystemUpdate; SPINNER; CheckCmdsuffice? if you need to pipe all output to a single follow-on command then{ QUIET SystemUpdate; SPINNER; CheckCmd; } | some_commandwould be one idea; if these suggestions won't work in your scenario consider updating the question with more details of what you're trying to accomplishQUIETwon't start anything other thanSystemUpdate. That looks like it needs to be running as a background process for SPINNER to make sense. SPINNER's PID check is not robust. Running CheckCmd in the sequence suggested would only check the status of SPINNER (and it won't because there's nothing trying to start it.)sleep 0,7isn't valid syntax (unless there are some regional variations that I am ignorant of).