Bash:迭代数组名称

时间:2012-07-18 18:53:10

标签: arrays bash loops iteration

我正在尝试编写代码以将大型数组分解为许多不同的小数组。最终我传递的数组是一个未知的大小,这只是我的测试主题。我已经走到了这一步:

#!/bin/bash
num=(10 3 12 3 4 4)
inArray=${#num[@]}
numArrays=$(($inArray/2))
remain=$(($inArray%2))
echo $numArrays
echo $remain
nun=0
if test $remain -gt  $nun; then
        numArrays=$(($numArrays+1))
fi
array=(1 2)
j=0
for ((i=0;i<$numArrays;i++, j=j+2)); do
        array=("${num[@]:$j:2}")
        echo "The array says: ${array[@]}"
        echo "The size? ${#array[@]}"
done    

我真正遇到的问题是:我想让变量'array'每次都能稍微更改名称,因此每个数组都保留并且在循环后具有唯一的名称。我尝试将名称array_$i设为但是返回:

[Stephanie@~]$ ./tmp.sh 
3
0
./tmp.sh: line 16: syntax error near unexpected token `"${num[@]:$j:2}"'
./tmp.sh: line 16: `    array_$i=("${num[@]:$j:2}")'
[Stephanie@RDT00069 ~]$ ./tmp.sh 
3
0
./tmp.sh: line 16: syntax error near unexpected token `$i'
./tmp.sh: line 16: `    array($i)=("${num[@]:$j:2}")'

有没有人有任何建议? 感谢

3 个答案:

答案 0 :(得分:1)

使用简单变量,您可以使用declare关键字进行间接分配:

v=foo
declare $v=5
echo $foo    # Prints 5

这并没有扩展到显而易见的阵列(对我来说,无论如何):

i=2
# This produces a syntax error
declare -a array_$i=("${num[@]:$j:2}")

相反,您可以声明一个空数组

declare -a array_$i

或一次分配一个项目:

declare -a array_$i[0]=item1 array_$i[1]=item2

这是一个使用for循环复制的例子,比如第3个 和一个大阵列的第四个字母变成一个较小的一个。我们用 i作为较小数组名称的动态部分,和 j作为该数组的索引。

letters=(a b c d e f)
i=1
j=0
for letter in "${letters[@]:2:2}"; do
    # E.g., i=0 and j=1 would result in
    #   declare -a array_0[1]=c
    declare -a array_$i[$j]=$letter
    let j+=1
  done
done

echo ${array_1[@]};  # c d

${foo[@]:x:y}x, x+1, ..., x+y-1

为我们提供了foo元素

您可以将整个事物包装在另一个for循环中,以实现将letters拆分为3个较小数组的目标:

 # We'll create array_0, array_1, and array_2
for i in 0 1 2; do 
  # Just like our subset above, but start at position i*2 instead of
  # a constant.
  for letter in "${letters[@]:$((i*2)):2}"; do
      declare -a array_$i[$j]=$letter
  done
done

一旦设法填充三个阵列,如何在没有eval的情况下访问它们? Bash具有间接访问的语法:

v=foo
foo=5
echo ${!v}   # echoes 5!

感叹号表示使用后面的单词作为变量,其值应该用作要展开的参数的名称。知道这一点,你可能认为你可以做到以下几点,但你错了。

i=1
v=array_$i   # array_1
echo ${!v[0]}  # array_1[0] is c, so prints c, right? Wrong.

在上文中,bash尝试查找名为v[0]的变量并展开它以获取要扩展的参数的名称。实际上,我们必须将数组及其索引视为单个名称:

i=1
v=array_$i[0]
echo ${!v}    # This does print c

答案 1 :(得分:1)

我认为你不能在这里真正避免eval,但如果你小心的话,你可以安全地做到这一点。这是我的方法:

for name in "${!array_*}"; do # Get all names starting with array_
    i="${name#array_*}" # Get the part after array_
    if [[ $i != *[^0-9]* ]]; then # Check that it's a number.
        printf '%s is not a valid subarray name\n' "$name"
    else
        # Create a variable named "statement" that contains code you want to eval.
        printf -v statement 'cur_array=( "${%s[@]}" )' "$name"
        eval "$statement"
        # Do interesting things with $cur_array
    fi
done

在此之前,当您刚刚创建数组时,您知道$name应该是什么,所以只需使用printf -v部分。

为了使它更安全,您可以将所有允许的数组名称保存在另一个数组check that $name is a member中。

答案 2 :(得分:0)

这应该可行,但这不是一个好的解决方案,另一种语言可能更好bash不支持多维数组

eval array_$i='('"${num[@]:$j:2}"')'

然后,例如

eval 'echo "${array_'$i'[0]}"'