This is how array variable subscripts should expand. The demo code should run in bash or zsh. Array variables work similarly in mksh and most versions of ksh93 until recently. They have some bugs and missing features. {{{#!highlight bash #!/usr/bin/env bash if [[ -v ZSH_VERSION ]]; then emulate ksh zmodload zsh/system elif [[ -v BASH_VERSION ]]; then shopt -s lastpipe extglob expand_aliases shopt -u {assoc,array}_expand_once 2>/dev/null enable -f fdflags{,} enable -f tee{,} fi function p { printf ' %s ' "$1" >&3 IFS= read -rd "" t <&4 printf '%d\0' "$((t+1))" | tee /proc/self/fd/3 >&5 } function f { typeset -a a typeset fd f='a[$(p x)0]' { if [[ -v BASH_VERSION ]]; then fdflags -s +nonblock 4 5 elif [[ -v ZSH_VERSION ]]; then sysopen -ro nonblock -u 4 /proc/self/fd/4; sysopen -wao nonblock -u 5 /proc/self/fd/5 fi printf '%s\0' -1 >&5 (( $(p a)a[\$(p b)f$(p c)]$(p d) )) } 3>&1 4<<<'' 4<$(flock 4)<(flock -w 1 4) 5>/proc/self/fd/4 echo } function main { set +m typeset BASH_COMPAT=51 f unset -v BASH_COMPAT f echo } main typeset -u SHELL=$SHELL typeset -p "${SHELL##*/}_VERSION" }}} {{{ a 0 c 1 d 2 b 3 x 4 a 0 c 1 d 2 b 3 x 4 typeset ZSH_VERSION=5.9 a 0 c 1 d 2 b 3 x 4 a 0 c 1 d 2 b 3 x 4 declare -- BASH_VERSION="5.1.16(1)-release" a 0 c 1 d 2 b 3 x 4 a 0 c 1 d 2/proc/self/fd/9: line 25: $(p b)f: arithmetic syntax error: operand expected (error token is "$(p b)f") declare -- BASH_VERSION="5.3.0(1)-devel" }}} `a`, `c`, and `d` are never seen by the arithmetic expression. They are pre-expanded because they are unescaped and thus evaluated first from left-to-right before the arithmetic evaluation stage. Because `p` produces nothing on stdout the overall expression is effectively reduced to `a[\$(p b)f]`. Next the arithmetic expression encounters the array variable `a` which must call the variable resolver with the variable name including its subscript. The variable resolver now performs expansions left-to-right on the subscript, evaluating `b` which again produces no stdout and effectively reduces the overall expression to `a[f]`. Now the variable resolver calls the arithmetic evaluator again to evaluate the index which has been reduced to just `f`. Because f is a string that is not a valid integer literal, bash evaluates its value as an arithmetic expression so we now evaluate `a[$(p x)0]`. Thus x is the final command substitution to be evaluated and the overall expression is effectively reduced to `a[a[0]]`. The array variable `a` is a dummy variable with no elements so the result of the expression is 0 and all of the output was produced as a side-effect of evaluating the command substitutions. == test -v and unset == Example: Filter and uniqify an array. {{{#!highlight bash function purify_cows { typeset -n _ref=$1 typeset IFS x=$2 typeset -A set set -- "${_ref[@]@Q}" eval -- "set+=(${*/*/[&]=})" if BASH_COMPAT=51 test -v 'set[$x]'; then BASH_COMPAT=51 IFS= command -- unset -v -- 'set[${x}${IFS[BASH_COMPAT=${BASH_VERSINFO[*]::2},0]}]' _ref=("${!set[@]}") else return 1 fi } function _main { local - set -x typeset -a cows=(moo moo moooo! moo oom moo @ @ moo) purify_cows cows @ printf '%s ' "${cows[@]}" { BASH_XTRACEFD=3 echo; } 3>/dev/null } _main }}} {{{ + cows=('moo' 'moo' 'moooo!' 'moo' 'oom' 'moo' '@' '@' 'moo') + typeset -a cows + purify_cows cows @ + typeset -n _ref=cows + typeset IFS x=@ + typeset -A set + set -- ''\''moo'\''' ''\''moo'\''' ''\''moooo!'\''' ''\''moo'\''' ''\''oom'\''' ''\''moo'\''' ''\''@'\''' ''\''@'\''' ''\''moo'\''' + eval -- 'set+=(['\''moo'\'']= ['\''moo'\'']= ['\''moooo!'\'']= ['\''moo'\'']= ['\''oom'\'']= ['\''moo'\'']= ['\''@'\'']= ['\''@'\'']= ['\''moo'\'']=)' ++ set+=(['moo']= ['moo']= ['moooo!']= ['moo']= ['oom']= ['moo']= ['@']= ['@']= ['moo']=) + BASH_COMPAT=51 + test -v 'set[$x]' + BASH_COMPAT=51 + IFS= + command -- unset -v -- 'set[${x}${IFS[BASH_COMPAT=${BASH_VERSINFO[*]::2},0]}]' + _ref=("${!set[@]}") + printf '%s ' oom 'moooo!' moo oom moooo! moo }}} ---- CategoryExampleCode