Question

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:

  1. 'xkeys=("${!'
  2. "$1"
  3. '[@]}")'

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

Was it helpful?

Solution

@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.

OTHER TIPS

${!"$1"} fetches the variable named in $1. ${variable[@]} expands to all the elements in the array variable. So this basically copies the array named by $1 into xkeys.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top