Etan Reisner
Etan Reisner

Reputation: 80921

bash non-array ${!name[@]} parameter expansion confusion

Given that the Bash Reference Manual in section 3.5.3 Shell Parameter Expansion says:

${!name[@]}
${!name[*]}

If name is an array variable, expands to the list of array indices (keys) assigned in name. If name is not an array, expands to 0 if name is set and null otherwise. When ‘@’ is used and the expansion appears within double quotes, each key expands to a separate word.

and

${parameter:-word}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

and

${parameter:+word}

If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

Can someone explain the output from the following expansions to me:

$ bash --version
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
$ cat hm.sh
p() {
    printf "%-11s : f=%1s : " "$*" "$f"
    eval printf \'%1s :\\n\' "$@"
}

p '$f'
p '${!f[@]}'
p '${!f[@]:-b}'
echo "echo: ${!f[@]:-b} :"
p '${!f[@]:+b}'
echo "echo: ${!f[@]:+b} :"
f=f
p '$f'
p '${!f[@]}'
p '${!f[@]:-b}'
echo "echo: ${!f[@]:-b} :"
p '${!f[@]:+b}'
echo "echo: ${!f[@]:+b} :"
f=t
p '$f'
p '${!f[@]}'
p '${!f[@]:-b}'
echo "echo: ${!f[@]:-b} :"
p '${!f[@]:+b}'
echo "echo: ${!f[@]:+b} :"

$ bash hm.sh
$f          : f=  :   :
${!f[@]}    : f=  :   :
${!f[@]:-b} : f=  : b :
echo: b :
${!f[@]:+b} : f=  :   :
echo:  :
$f          : f=f : f :
${!f[@]}    : f=f : 0 :
${!f[@]:-b} : f=f : f :
echo: f :
${!f[@]:+b} : f=f : b :
echo: b :
$f          : f=t : t :
${!f[@]}    : f=t : 0 :
${!f[@]:-b} : f=t : b :
echo: b :
${!f[@]:+b} : f=t :   :
echo:  :

That is why do the contents of the variable in question change how the ${!name[@]:-default} and ${!name[@]:+alt} expansions expand when the contents do not change how the ${!name[@]} expansion itself expands?

Upvotes: 3

Views: 172

Answers (1)

rici
rici

Reputation: 241681

In the syntax ${!f[@]}, the parameter expansion is being parsed as a "list the keys" expression, which is one of the specific exceptions to the rule that {! introduces a level of indirection. The syntax ${!f[@]:-b} does not match that pattern (because it ends with :-b), so the ! is interpreted as an indirect reference, and consequently it is the variable whose name is the value of f which is being tested by the default value modifier.

The interesting question is, "what does the [@] modify in that expression?" It seems to be modifying f, which does nothing when f is a scalar but produces an invalid name when f is an array of more than one element; in that case, the default value substitution seems to occur.

My best guess is that this is an undocumented and possibly unintended corner case of parameter expansion.

Upvotes: 3

Related Questions