Brief statement of the question:
Is there built-in bash method to count number of elements in bash array, where the name of the array is dynamic (i.e. stored in a variable), without resorting to making a fully copy of the array or using eval
?
More information:
Using bash parameter substitution, one can do the following:
- Determine the length of an array:
myArr=(A B C); echo ${#myArr[@]}
. - Indirectly reference a variable by name:
NAME=myVar; echo ${!NAME}
(this also applies to array elements):
NAME=myArr[1]; echo ${!NAME}
But if the name of an array is stored in another variable, how can one determine the number of elements in the array? (One might consider this a combination of the above two parameter substitutions.) For example:
myArr=(A B C D)
NAME=myArr
# Get the number of elements in the array indirectly referenced by NAME.
count=${#$NAME[@]} # This syntax is invalid. What is the right way?
Below are multiple attempts that all FAIL:
# Setup for following attempts:
myArr=(A B C D)
NAME=myArr
EXPR1=$NAME[@] # i.e. EXPR1='myArr[@]'
EXPR2=#$NAME[@] # i.e. EXPR2='#myArr[@]'
# Failed attempts to get the lengh of the array indirectly:
1. count=${#$NAME[@]} # ERROR: bash: ...: bad substitution
2. count=${#!EXPR1} # ERROR: bash: !EXPR}: event not found
3. count=${#\!EXPR1} # ERROR: bash: ...: bad substitution
4. count=${!#EXPR1} # ERROR: bash: ...: bad substitution
5. count=${!EXPR2} # Returns NULL
I've also tried some other variants of the above, but have not yet found anything that works without either: (A) making a copy of the array or (B) by using eval
.
Working Methods:
There are a couple ways of solving this that are probably not optimal (but correct me if I'm wrong):
Method 1: Copy the Array
Assign the array to another (statically-named) variable and get the number of elements in it.
EXPR=$NAME[@]
arrCopy=( "${!EXPR}" )
count=${#arrCopy}
Method 2: Use eval
EXPR="count=\${#$NAME[@]}" # i.e. 'count=${myArr[@]}'
eval $EXPR
# Now count is set to the length of the array
Summary:
Is there any built-in method (i.e. parameter substitution syntax) in bash to determine the length of an array indirectly? If not, what is the most efficient way to do this? I assume it is the eval
method above, but are there security or performance issues with eval
?
bash
namerefs?.declare -n ref=abc; abc=(A B C D); printf '%s\n' "${ref[@]}"
time bash -c 'a=(1 a +); c=a; for ((i=0;i<100000;i++)); do eval "echo \${#$c[@]}"; done' > /dev/null
, and similarly withe=$c[@]; d=("${!e}); echo ${#d[@]}
in the loop. The eval took about 90% of the time taken by copying. And I suppose that gap will only increase the larger the array and its elements are.