问题的简要说明:
是否有内置的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
?
2
啊。嵌套变量。我会重新考虑使用嵌套变量以外的方法。这里的实际问题是什么?
—
muru
这是一个有趣的问题。我唯一要提醒您的是,假设某件事物有或没有性能问题。我在进行非常严格的测试以优化大型bash脚本时发现,一些bash内置函数在性能方面很糟糕,实际上,只需删除大型脚本中的一个启动测试,该测试使用了您可能期望的高效方法,即变量扩展,实际上,单行使整个执行速度降低了大约10%到20%。使用计时器在大循环中测试方法,结果可能会让您感到惊讶。
—
Lizardx
@muru-这只是语义,但是术语“嵌套变量”在版本2之前与bash有关。Bash v2添加了“间接变量引用”的语法。我只是问是否有特定的语法来获取间接引用数组的长度。我认为bash的作者如果没有被要求的,有用的技术,就不会去为标量和数组实现变量间接寻址,而不仅仅是保证立即使用“ Ugh”的hack,尽管我相信这值得商de 。
—
drwatsoncode 2015年
我做了一些基准测试:
—
muru
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%。而且我猜想间隙只会增加数组及其元素的大小。