I want my script to perform the product of all its integer arguments. Instead of performing a loop I tried to replace blanks with *
and then compute the operation. But I got the following result which I don't understand:
#!/bin/bash
# product.sh
echo $(( ${*// /*} )) # syntax error with ./product.sh 2 3 4
args=$*
echo $(( ${args// /*} )) # ./product.sh 2 3 4 => outputs 24
How is it that the first one produces an error while using an intermediate variable works fine?
CodePudding user response:
How is it that the first one produces an error:
From the Bash Reference Manual:
If parameter is ‘@’ or ‘*’, the substitution operation is applied to each positional parameter in turn
(emphasis mine)
That is, the expression ${*// /*}
replaces spaces inside positional parameters, not the spaces separating positional parameters. That expression expands to 2 3 4
(which gives a syntax error when used in an arithmetic context), since the parameters itself don't contain a space. Try with
./product '2 ' '3 ' 4
and you will see the difference.
CodePudding user response:
You may utilize IFS
:
#!/bin/bash
# product.sh
# set IFS to *
local IFS='*'
# use IFS in output
echo "$*"
# perform arithmetic
echo "$(( $* ))";
Output:
2*3*4
24
Reason why echo $(( ${*// /*} ))
doesn't work because bash doesn't allow nested expressions.
CodePudding user response:
In your example, the value $*
does not actually contain any literal spaces, so ${*// /*}
does not do anything.
If it did, those asterisks would be subject to wildcard expansion, so the idea of performing a substitution would seem to be rather brittle even if it worked.
I would simply create a function to process the arguments, instead of rely on trickery with substitutions -- these tend to have icky corner cases when one of the arguments is a variable or etc.
mul () {
case $# in
[01]) echo "$@";;
*) local n=$1; shift; echo $((n * $(mul "$@")));;
esac
}
CodePudding user response:
Or use printf
, like this:
echo $(( $(printf '%s*' $*)1 ))