0
#!/bin/bash

DefaultColor="\033[0m"
GREEN="\033[32m"
RED="\033[31m"
PURPLE="\033[35m"
YELLOW="\033[33m"
CYAN="\033[36m"

CmdOk="\033[32m✔\033[0m"

CmdError="\033[31m🞮\033[0m"

function QUIET(){
    "$@" > /dev/null 2>&1
}


function CheckCmd(){
if [[ $? -eq 0 ]]
then
    echo -e ["$CmdOk"]
    echo
    sleep 0,7
else
    echo -e ["$CmdError"]
    echo
    sleep 0,7
fi
}


function SPINNER(){
    local pid=$!
    local delay=0.14
    local spinstr='⠖⠲⠴⠦'
    while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
        local temp=${spinstr#?}
        printf "["$DefaultColor""$YELLOW"%s"$DefaultColor"]" "${spinstr:0:1}"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        printf "\b\b\b"
    done
    #printf "    \b\b\b\b"
}

function SystemUpdate(){
sudo apt update
sudo apt upgrade
sudo apt-get clean
}

QUIET SystemUpdate SPINNER CheckCmd

In the script below I want to combine the QUIET function with the SystemUpdate function so that let the SystemUpdate function run silently without hiding the spinner and CheckCmd function (check the SystemUpdate function).

I tried with a pipe | but it returns me a grep error.

Do you have any idea?

4
  • 1
    QUIET gets three arguments and runs the first as the command and the rest as its arguments. So, it runs SystemUpdate with two arguments, SPINNER and CheckCmd. That's not what you want. Commented Nov 2 at 14:58
  • Why not using tput commands instead of hard coded ANSI colors? Check: for i in {0..7}; do tput setaf $i; echo "tput setaf $i"; tput sgr0; done Commented Nov 3 at 6:55
  • setting aside the issue with QUIET() (see choroba's comment), and keeping in mind that you need to separate commands with a newline or semicolon, would QUIET SystemUpdate; SPINNER; CheckCmd suffice? if you need to pipe all output to a single follow-on command then { QUIET SystemUpdate; SPINNER; CheckCmd; } | some_command would 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 accomplish Commented Nov 3 at 14:22
  • There are a number of problems I can see. QUIET won't start anything other than SystemUpdate. 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,7 isn't valid syntax (unless there are some regional variations that I am ignorant of). Commented Nov 3 at 14:43

1 Answer 1

0
  • 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
1
  • Thank Arnaud Valmary your script work perfectly. Thanks for the explanations. Merci à toi ♥ Commented Nov 13 at 21:41

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.