如何在shell脚本中动态生成新的变量名?

时间:2012-05-30 16:25:54

标签: linux bash shell

我正在尝试在shell脚本中生成动态var名称,以便在循环中处理一组具有不同名称的文件,如下所示:

#!/bin/bash

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
  echo SAMPLE{$i}
done

我希望输出:

1-first.with.custom.name
2-second.with.custom.name

但我得到了:

SAMPLE{1}
SAMPLE{2}

是否有可能在飞行中生成var名称?

6 个答案:

答案 0 :(得分:65)

您需要使用变量间接:

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
   var="SAMPLE$i"
   echo ${!var}
done

来自Bash man page,在'参数扩展'下:

  

“如果参数的第一个字符是感叹号(!),则a   引入了变量间接的级别。 Bash使用的值   由参数的其余部分形成的变量作为名称   变量;然后展开此变量,并在该变量中使用该值   其余的替换,而不是参数本身的值。   这被称为间接扩张。“

答案 1 :(得分:17)

问题

您正在使用 i 的值,就好像它是一个数组索引一样。它不是,因为SAMPLE1和SAMPLE2是单独的变量,而不是数组。

此外,在调用echo SAMPLE{$i}时,您只需将 i 的值附加到单词“SAMPLE”。您在此声明中解除引用的唯一变量是 $ i ,这就是您获得结果的原因。

解决问题的方法

有两种主要方法可以解决这个问题:

  1. 通过 eval 内置或indirect variable expansion对插值变量进行多阶段解除引用。
  2. 迭代数组,或使用 i 作为数组的索引。
  3. 使用 eval

    取消引用

    在这种情况下最简单的方法是使用 eval

    SAMPLE1='1-first.with.custom.name'
    SAMPLE2='2-second.with.custom.name'
    
    for (( i = 1; i <= 2; i++ )); do
        eval echo \$SAMPLE${i}
    done
    

    这会将 i 的值附加到变量的末尾,然后重新处理结果行,展开插值变量名称(例如 SAMPLE1 SAMPLE2 )。

    使用间接变量取消引用

    这个问题的答案是:

    SAMPLE1='1-first.with.custom.name'
    SAMPLE2='2-second.with.custom.name'
    
    for (( i = 1; i <= 2; i++ ))
    do
       var="SAMPLE$i"
       echo ${!var}
    done
    

    这在技术上是一个三步过程。首先,它将插值变量名称分配给 var ,然后取消引用存储在 var 中的变量名称,最后展开结果。它看起来有点干净,有些人比使用 eval 更熟悉这种语法,但结果基本相同。

    迭代数组

    您可以通过迭代数组而不是使用变量插值来简化循环和扩展。例如:

    SAMPLE=('1-first.with.custom.name' '2-second.with.custom.name')
    for i in "${SAMPLE[@]}"; do
        echo "$i"
    done
    

    这比其他方法增加了好处。具体做法是:

    1. 您无需指定复杂的循环测试。
    2. 您可以通过 $ SAMPLE [$ i] 语法访问各个数组元素。
    3. 您可以使用 $ {#SAMPLE} 变量扩展来获取元素总数。
    4. 原始示例的实际等效性

      这三种方法都适用于原始问题中给出的示例,但阵列解决方案提供了最大的整体灵活性。选择最适合您手头数据的那个。

答案 2 :(得分:3)

据我所知,他们的方式@ johnshen64说。此外,您可以使用如下数组来解决您的问题:

SAMPLE[1]='1-first.with.custom.name'
SAMPLE[2]='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ )) do
    echo ${SAMPLE[$i]}
done

请注意,您不需要使用数字,因为索引SAMPLE[hello]也可以正常工作

答案 3 :(得分:3)

您可以使用eval,如下所示:

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
  eval echo \$SAMPLE$i
done

答案 4 :(得分:2)

不是一个独立的答案,只是对Miquel的答案的补充,我不能很好地评论。

您可以使用循环,+ =运算符和here文档填充数组:

SAMPLE=()
while read; do SAMPLE+=("$REPLY"); done <<EOF
1-first.with.custom.name
2-second.with.custom.name
EOF

在bash 4.0中,它就像

一样简单
readarray SAMPLE <<EOF
1-first.with.custom.name
2-second.with.custom.name
EOF

答案 5 :(得分:0)

eval "echo $SAMPLE${i}"