Home > Enterprise >  Why does'nt ${*// /*} work to replace blanks with * in command line arguments?
Why does'nt ${*// /*} work to replace blanks with * in command line arguments?

Time:10-28

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 ))
  • Related