This code is given as a workaround for array indirection at bash-hackers.org wiki page as an example to get array length and indices indirectly.
It's also mentioned in this Stackoverflow question.
I would like to understand specifically why local -a 'xkeys=("${!'"$1"'[@]}")'
works. What is happening here. I understand that there are three different strings to the right of the equal sign:
Why does that work?
This is the code:
isSubset() {
local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'
set -- "${@/%/[key]}"
(( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1
local key
for key in "${xkeys[@]}"; do
[[ ${!2+_} && ${!1} == ${!2} ]] || return 1
done
}
Thanks
@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
.