计算 bash 数组中元素的数量,其中数组的名称是动态的(即存储在变量中)

计算 bash 数组中元素的数量,其中数组的名称是动态的(即存储在变量中)

问题的简要陈述:

是否有内置的 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.

工作方法:

有几种解决此问题的方法可能不是最佳的(但如果我错了,请纠正我):

方法一:复制数组

将数组分配给另一个(静态命名)变量并获取其中的元素数量。

EXPR=$NAME[@]
arrCopy=( "${!EXPR}" )
count=${#arrCopy}

方法二:使用eval

EXPR="count=\${#$NAME[@]}"  # i.e. 'count=${myArr[@]}'
eval $EXPR
# Now count is set to the length of the array

概括:

bash中有没有内置方法(即参数替换语法)来间接确定数组的长度?如果没有,最有效的方法是什么?我认为这是eval上面的方法,但是是否存在安全或性能问题eval

答案1

你应该在索引评估中处理这些东西。你可以间接通过如果将其设为数组,则为间接变量的索引。

a=(abc1 def2 ghi3 jkl4 mno5)
r=('a[c=${#a[@]}]' a\[i] a\[@])
for   i in   0 1 2 3 4 5
do    c=
      printf "<%s>\n" "${!r-${!r[i<c?1:2]}}"
      printf "\n\tindex is $i and count is $c\n\n"
done

<abc1>

    index is 0 and count is 5

<def2>

    index is 1 and count is 5

<ghi3>

    index is 2 and count is 5

<jkl4>

    index is 3 and count is 5

<mno5>

    index is 4 and count is 5

<abc1>
<def2>
<ghi3>
<jkl4>
<mno5>

    index is 5 and count is 5

因为bash的索引是从 0 开始的,所以数组对象的总数将始终比最高设置索引多 1,因此:

c=
echo "${a[c=${#a[@]}]-this index is unset}" "$c"

this index is unset 5

...如果提供了参数,该参数将扩展为默认单词。

如果未提供:

c=
${!r}
echo "$c"

5

……没有造成任何伤害。

在循环中,我跟踪一个$i索引变量并检查它是否至少与$count一样大。当它较小时,我将 ef 扩展$r为,a[i]因为它是有效索引,但当它等于或大于时,我将$ref 扩展为整个$a数组。

这是在一个函数中:

ref_arr(){
    local    index=-1 count=
    local    ref=(   "$1[ count= \${#$1[@]}  ]"
                     "$1[ index ]"    "$1[ @ ]"
    )  &&    printf  "input array '%s' has '%d' members.\n" \
                     "$1"  "${!ref-${count:?invalid array name: "'$1'"}}"
    while    [ "$((index+=1))" -lt "$count"  ]
    do       printf  "$1[$index]  ==  '%s'\n"  "${!ref[1]}"
    done
}
some_array=(some "dumb
            stuff" 12345\'67890 "" \
          '$(kill my computer)')
ref_arr some_array
ref_arr '$(echo won'\''t work)'

input array 'some_array' has '5' members.
some_array[0]  ==  'some'
some_array[1]  ==  'dumb
                stuff'
some_array[2]  ==  '12345'67890'
some_array[3]  ==  ''
some_array[4]  ==  '$(kill my computer)'
bash: count: invalid array name: '$(echo won't work)'

答案2

bash 4.3 namerefs 是天赐之物。但是,您可以这样做:

$ myArr=(A B C D)
$ NAME=myArr
$ tmp="${NAME}[@]"
$ copy=( "${!tmp}" )
$ echo "${#copy[@]}"
4

相关内容