For some reason the options work fine the first call of lib_progress_bar -c "@" -u "_" 0 100
, but on the second call and beyond everything is default because it seems like getopts c:u:d:p:s:%:m: flag
isn't true the second time around, or atleast the case is never executed when I used set -x
#!/bin/bash
lib_progress_bar() {
local current=0
local max=100
local completed_char="#"
local uncompleted_char="."
local decimal=1
local prefix=" ["
local suffix="]"
local percent_sign="%"
local max_width=$(tput cols)
local complete remain subtraction width atleast percent chars
local padding=3
while getopts c:u:d:p:s:%:m: flag; do
case "$flag" in
c) completed_char="$OPTARG";;
u) uncompleted_char="$OPTARG";;
d) decimal="$OPTARG";;
p) prefix="$OPTARG";;
s) suffix="$OPTARG";;
%) percent_sign="$OPTARG";;
m) max_width="$OPTARG";;
esac
done
shift $((OPTIND-1))
current=${1:-$current}
max=${2:-$max}
if (( decimal > 0 )); then
(( padding = padding + decimal + 1 ))
fi
let subtraction=${#completed_char}+${#prefix}+${#suffix}+padding+${#percent_sign}
let width=max_width-subtraction
if (( width < 5 )); then
(( atleast = 5 + subtraction ))
echo >&2 "the max_width of ($max_width) is too small, must be atleast $atleast"
return 1
fi
if (( current > max ));then
echo >&2 "current value must be smaller than max. value"
return 1
fi
percent=$(awk -v "f=%${padding}.${decimal}f" -v "c=$current" -v "m=$max" 'BEGIN{printf('f', c / m * 100)}')
(( chars = current * width / max))
# sprintf n zeros into the var named as the arg to -v
printf -v complete '%0*.*d' '' "$chars" ''
printf -v remain '%0*.*d' '' "$((width - chars))" ''
# replace the zeros with the desired char
complete=${complete//0/"$completed_char"}
remain=${remain//0/"$uncompleted_char"}
printf '%s%s%s%s %s%s\r' "$prefix" "$complete" "$remain" "$suffix" "$percent" "$percent_sign"
if (( current >= max )); then
echo ""
fi
}
lib_progress_bar -c "@" -u "_" 0 100
echo
lib_progress_bar -c "@" -u "_" 25 100
echo
lib_progress_bar -c "@" -u "_" 50 100
echo
exit;
An option character in this string can be followed by a colon (' : ') to indicate that it takes a required argument. If an option character is followed by two colons (' :: '), its argument is optional; this is a GNU extension. getopt has three ways to deal with options that follow non-options argv elements.
According to man getopts , OPTIND is the index of the next argument to be processed (starting index is 1). Hence, In sh foo.sh -abc CCC Blank arg1 is -abc , so after a we are still parsing arg1 when next is b ( a 1 ).
Updated: 02/01/2021 by Computer Hope. On Unix-like operating systems, getopts is a builtin command of the Bash shell. It parses command options and arguments, such as those passed to a shell script.
getopt vs getopts The main differences between getopts and getopt are as follows: getopt does not handle empty flag arguments well; getopts does. getopts is included in the Bourne shell and Bash; getopt needs to be installed separately.
Just add:
local OPTIND
at the top of your function.
To explain why Dennis's answer works, see the bash
man page (search for getopts
):
OPTIND is initialized to 1 each time the shell or a shell script is invoked.
The shell does not reset OPTIND automatically; it must be manually reset between multiple calls to getopts within the same shell invocation if a new set of parameters is to be used.
This is how getopts
can process multiple options.
If getopts
didn't maintain global state in the OPTIND
variable, each call to getopts
in your while
loop would keep processing $1
, and never advance to the next argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With