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

时间:2021-09-01 22:57:26

I'm trying to generate dynamic var names in a shell script to process a set of files with distinct names in a loop as follows:

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

I would expect the output:

我期望输出:

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

but i got:

但我有:

SAMPLE{1}
SAMPLE{2}

Is it possible generate var names in the fly?

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

5 个解决方案

#1


62  

You need to utilize Variable Indirection:

你需要利用变量间接:

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

From the Bash man page, under 'Parameter Expansion':

在Bash man页面,在“参数扩展”下:

"If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion."

如果参数的第一个字符是感叹号(!),则引入一个可变的间接层。Bash使用由其余参数组成的变量的值作为变量的名称;然后展开该变量,该值将在替换的其余部分中使用,而不是参数本身的值。这就是所谓的间接扩张。

#2


16  

The Problem

You're using the value of i as if it were an array index. It isn't, because SAMPLE1 and SAMPLE2 are separate variables, not an array.

你把i的值当作数组索引来使用。它不是,因为SAMPLE1和SAMPLE2是单独的变量,而不是数组。

In addition, when calling echo SAMPLE{$i} you are only appending the value of i to the word "SAMPLE." The only variable you are dereferencing in this statement is $i, which is why you got the results you did.

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

Ways to Address the Problem

There are two main ways to address this:

有两种主要的方法来解决这个问题:

  1. Multi-stage dereferencing of an interpolated variable, via the eval builtin or indirect variable expansion.
  2. 通过计算内建或间接变量展开,对插补变量进行多级去引用。
  3. Iterating over an array, or using i as an index into an array.
  4. 遍历一个数组,或将i用作数组的索引。

Dereferencing with eval

The easiest thing to do in this situation is to use 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

This will append the value of i to the end of the variable, and then reprocess the resulting line, expanding the interpolated variable name (e.g. SAMPLE1 or SAMPLE2).

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

Dereferencing with Indirect Variables

The accepted answer for this question is:

这个问题的公认答案是:

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

This is technically a three-step process. First, it assigns an interpolated variable name to var, then dereferences the variable name stored in var, and finally expands the result. It looks a little cleaner, and some people are more comfortable with this syntax than with eval, but the result is largely the same.

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

Iterating Over an Array

You can simplify both the loop and the expansion by iterating over an array instead of using variable interpolation. For example:

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

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

This has added benefits over the other methods. Specifically:

这增加了其他方法的好处。具体地说:

  1. You don't need to specify a complex loop test.
  2. 您不需要指定复杂的循环测试。
  3. You access individual array elements via the $SAMPLE[$i] syntax.
  4. 您可以通过$SAMPLE[$i]语法访问单个数组元素。
  5. You can get the total number of elements with the ${#SAMPLE} variable expansion.
  6. 可以使用${#SAMPLE}变量展开获得元素的总数。

Practical Equivalency for Original Example

All three methods will work for the example given in the original question, but the array solution provides the most overall flexibility. Choose whichever one works best for the data you have on hand.

这三种方法都适用于原始问题中给出的示例,但是数组解决方案提供了最全面的灵活性。选择最适合你手头数据的。

#3


3  

You can use eval as shown below:

可以使用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


3  

Not as far as I know, They way @johnshen64 said. Also, you could solve your problem using an array like so:

据我所知,他们是这么说的@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

Note that you don't need to use numbers as indexes SAMPLE[hello] will work just as well

注意,您不需要使用数字作为索引示例[hello]也可以工作

#5


2  

Not a standalone answer, just an addition to Miquel's answer which I couldn't fit well in a comment.

不是一个独立的答案,只是米奎尔的答案的补充,我无法很好地融入评论。

You can populate the array using a loop, the += operator, and a here document as well:

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

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

In bash 4.0, it's as simple as

在bash 4.0中,它非常简单

readarray SAMPLE <<EOF
1-first.with.custom.name
2-second.with.custom.name
EOF

#1


62  

You need to utilize Variable Indirection:

你需要利用变量间接:

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

From the Bash man page, under 'Parameter Expansion':

在Bash man页面,在“参数扩展”下:

"If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion."

如果参数的第一个字符是感叹号(!),则引入一个可变的间接层。Bash使用由其余参数组成的变量的值作为变量的名称;然后展开该变量,该值将在替换的其余部分中使用,而不是参数本身的值。这就是所谓的间接扩张。

#2


16  

The Problem

You're using the value of i as if it were an array index. It isn't, because SAMPLE1 and SAMPLE2 are separate variables, not an array.

你把i的值当作数组索引来使用。它不是,因为SAMPLE1和SAMPLE2是单独的变量,而不是数组。

In addition, when calling echo SAMPLE{$i} you are only appending the value of i to the word "SAMPLE." The only variable you are dereferencing in this statement is $i, which is why you got the results you did.

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

Ways to Address the Problem

There are two main ways to address this:

有两种主要的方法来解决这个问题:

  1. Multi-stage dereferencing of an interpolated variable, via the eval builtin or indirect variable expansion.
  2. 通过计算内建或间接变量展开,对插补变量进行多级去引用。
  3. Iterating over an array, or using i as an index into an array.
  4. 遍历一个数组,或将i用作数组的索引。

Dereferencing with eval

The easiest thing to do in this situation is to use 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

This will append the value of i to the end of the variable, and then reprocess the resulting line, expanding the interpolated variable name (e.g. SAMPLE1 or SAMPLE2).

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

Dereferencing with Indirect Variables

The accepted answer for this question is:

这个问题的公认答案是:

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

This is technically a three-step process. First, it assigns an interpolated variable name to var, then dereferences the variable name stored in var, and finally expands the result. It looks a little cleaner, and some people are more comfortable with this syntax than with eval, but the result is largely the same.

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

Iterating Over an Array

You can simplify both the loop and the expansion by iterating over an array instead of using variable interpolation. For example:

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

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

This has added benefits over the other methods. Specifically:

这增加了其他方法的好处。具体地说:

  1. You don't need to specify a complex loop test.
  2. 您不需要指定复杂的循环测试。
  3. You access individual array elements via the $SAMPLE[$i] syntax.
  4. 您可以通过$SAMPLE[$i]语法访问单个数组元素。
  5. You can get the total number of elements with the ${#SAMPLE} variable expansion.
  6. 可以使用${#SAMPLE}变量展开获得元素的总数。

Practical Equivalency for Original Example

All three methods will work for the example given in the original question, but the array solution provides the most overall flexibility. Choose whichever one works best for the data you have on hand.

这三种方法都适用于原始问题中给出的示例,但是数组解决方案提供了最全面的灵活性。选择最适合你手头数据的。

#3


3  

You can use eval as shown below:

可以使用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


3  

Not as far as I know, They way @johnshen64 said. Also, you could solve your problem using an array like so:

据我所知,他们是这么说的@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

Note that you don't need to use numbers as indexes SAMPLE[hello] will work just as well

注意,您不需要使用数字作为索引示例[hello]也可以工作

#5


2  

Not a standalone answer, just an addition to Miquel's answer which I couldn't fit well in a comment.

不是一个独立的答案,只是米奎尔的答案的补充,我无法很好地融入评论。

You can populate the array using a loop, the += operator, and a here document as well:

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

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

In bash 4.0, it's as simple as

在bash 4.0中,它非常简单

readarray SAMPLE <<EOF
1-first.with.custom.name
2-second.with.custom.name
EOF