40

我正在尝试在 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 名称?

4

6 回答 6

74

您需要利用变量间接:

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 手册页的“参数扩展”下:

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

于 2012-05-30T16:35:23.173 回答
20

问题

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

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

解决问题的方法

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

  1. 内插变量的多阶段解引用,通过eval内置或间接变量扩展
  2. 遍历数组,或使用i作为数组的索引。

使用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的值附加到变量的末尾,然后重新处理结果行,扩展内插变量名称(例如SAMPLE1SAMPLE2)。

使用间接变量取消引用

这个问题的公认答案是:

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}变量扩展获得元素的总数。

原始示例的实际等效性

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

于 2012-05-30T16:35:36.933 回答
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]也可以使用

于 2012-05-30T16:34:07.737 回答
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
于 2012-05-30T16:35:39.633 回答
2

不是一个独立的答案,只是对 Miquel 的答案的补充,我不太适合发表评论。

您可以使用循环、+= 运算符和此处的文档来填充数组:

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
于 2012-05-30T21:28:45.733 回答
0

评估“回声$SAMPLE${i}”

于 2021-07-20T20:55:53.833 回答