How to make the argument as optional in bash?

In below function with 9 arguments:

SUM() { 
    echo "The sum is $(($1+$2+$3+$4+$5+$6+$7+$8+$9))"
}

I want to make the second arguments to the next(3..9) become a optional arguments.

When I call the function with 2 arguments I get error:

SUM 3 8
bash: 3+8+++++++: syntax error: operand expected (error token is "+")

Note BOLD: first argument and second argument are force arguments and not optional for function. I only want second arguments to the next is optional and when I call the function less than 2 args the function must return no result.


Solution 1:

If you won't pass arguments with spaces:

sum() {  
[[ -n $2 ]] && echo $(( $(tr ' ' '+' <<<"$@") ))
}

Effect:

$ sum 1 2 3
6

Explanation:

  1. <<<"some string" feeds in only "some string" as the input. Think of it as shorthand for echo "some string" |. It is called a Here String.
  2. "$@" expands into all the positional parameters, separated by spaces. It is equivalent to "$1 $2 ...".
  3. Hence, tr ' ' '+' <<<"$@" outputs "$1+$2+$3...", which is evaluated by the outer $(( )).
  4. [[ -n $2 ]] tests if the second parameter is non-empty. You could replace [[ -n $2 ]] && with [[ -z $2 ]] ||.

Another way:

sum() {
[[ -n $2 ]] && (IFS=+; echo $(( $* )))
}

Explanation:

  1. $* is just like $@, except that the parameters are not separated by spaces, but by the first character of the Internal Field Separator (IFS). With IFS=+, it expands to "$1+$2+...". See What is the difference between $* and $@?
  2. We set IFS in a subshell (note the surrounding parentheses) so that the main shell isn't affected. IFS is, by default: \t\n (space, tab, newline). This is an alternative to using local variables.

Now to answer your question:

You can use a default value for any variable or parameter. Either:

SUM() { 
 echo "The sum is $(($1+$2+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))" || false
}

Or:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}

Solution 2:

Have a look at the shift operator. It will shift arguments 2 and onward to positions 1 and onward, discarding argument 1.

sum () {
    local total=0;
    while [ $# -gt 0 ]; do
        total=$(($total + $1))
        shift
    done
    echo $total
}