Краткое изложение вопроса:
Существует ли встроенный метод bash для подсчета количества элементов в массиве bash, где имя массива является динамическим (т.е. хранится в переменной), не прибегая к созданию полной копии массива или его использованию eval
?
Дополнительная информация:
Используя подстановку параметров bash, можно сделать следующее:
- Определить длину массива:
myArr=(A B C); echo ${#myArr[@]}
. - Косвенная ссылка на переменную по имени:
NAME=myVar; echo ${!NAME}
(это также относится к элементам массива):
NAME=myArr[1]; echo ${!NAME}
Но если имя массива хранится в другой переменной, как можно определить количество элементов в массиве? (Можно рассматривать это как комбинацию двух приведенных выше подстановок параметров.) Например:
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?
Ниже приведены несколько попыток, которые все НЕУДАЧИ:
# 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
Я также попробовал некоторые другие варианты вышеупомянутого, но еще не нашел ничего, что работает без: (A) создания копии массива или (B) с помощью eval
.
Методы работы:
Есть несколько способов решения этой проблемы, которые, вероятно, не оптимальны (но исправьте меня, если я ошибаюсь):
Способ 1: скопировать массив
Присвойте массив другой переменной (со статическим именем) и получите количество элементов в нем.
EXPR=$NAME[@]
arrCopy=( "${!EXPR}" )
count=${#arrCopy}
Способ 2: использование eval
EXPR="count=\${#$NAME[@]}" # i.e. 'count=${myArr[@]}'
eval $EXPR
# Now count is set to the length of the array
Резюме:
Есть ли в bash встроенный метод (т. Е. Синтаксис подстановки параметров) для косвенного определения длины массива? Если нет, то какой самый эффективный способ сделать это? Я предполагаю, что это eval
метод выше, но есть проблемы с безопасностью или производительностью eval
?
time bash -c 'a=(1 a +); c=a; for ((i=0;i<100000;i++)); do eval "echo \${#$c[@]}"; done' > /dev/null
и аналогично с e=$c[@]; d=("${!e}); echo ${#d[@]}
циклом. Эвал занимал около 90% времени, затрачиваемого на копирование. И я полагаю, что разрыв будет только увеличиваться, чем больше массив и его элементы.