@tripleee is essentially right, though one detail was missed (and I made the same mistake initially a few minutes ago when writing my explanation of the rest of isSubset
in my answer here).
The crucial detail here is that local
(and declare
and typeset
) do their own parsing of their arguments.
Let's walk through the various expansions of this line
local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'
Splitting the quoted pieces for now will aid in understanding. That gets us
local -a 'xkeys=("${!' "$1" '[@]}")' 'ykeys=("${!' "$2" '[@]}")'
Which expands to
local -a 'xkeys=("${!' "a" '[@]}")' 'ykeys=("${!' "b" '[@]}")'
Which reassembles into
local -a 'xkeys=("${!'"a"'[@]}")' 'ykeys=("${!'"a"'[@]}")'
Combining adjacent quoted words gets us
local -a 'xkeys=("${!a[@]}")' 'ykeys=("${!a[@]}")'
If this was anything but local
we would be done at this point (ok eval
also).
For example echo
: e() { echo 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'; }; e a b
outputs xkeys=("${!a[@]}") ykeys=("${!b[@]}")
But since this is local
which, as I said before, does its own parsing (and in fact you know it does already because otherwise the {0..5}
expansions in the assignment to a
wouldn't have worked. Try echo {0..5} vs. "{0..5}"
to see what I mean) the single-quoted strings get re-evaluated by local
and the array index expansions occur. So ${!a[@]}
expands to the indices of a
and ${!b[@]}
expands to the indices of b
.