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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10820343/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:35:09  来源:igfitidea点击:

How can I generate new variable names on the fly in a shell script?

linuxbashshell

提问by pQB

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 名称?

采纳答案by johnshen64

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

"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 使用由参数的其余部分形成的变量的值作为变量的名称;然后扩展这个变量,然后值用于替换的其余部分,而不是参数本身的值。这称为间接扩展。”

回答by Miquel

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

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

回答by Todd A. Jacobs

The Problem

问题

You're using the value of ias 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 ito 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 evalbuiltin or indirect variable expansion.
  2. Iterating over an array, or using ias an index into an array.
  1. 通过eval内置变量间接变量扩展对内插变量进行多阶段解引用。
  2. 迭代数组,或使用i作为数组的索引。

Dereferencing with eval

使用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 ito the end of the variable, and then reprocess the resulting line, expanding the interpolated variable name (e.g. SAMPLE1or SAMPLE2).

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

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. You access individual array elements via the $SAMPLE[$i]syntax.
  3. You can get the total number of elements with the ${#SAMPLE}variable expansion.
  1. 您不需要指定复杂的循环测试。
  2. 您可以通过$SAMPLE[$i]语法访问单个数组元素。
  3. 您可以使用${#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.

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

回答by dogbane

You can use evalas 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

回答by chepner

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

不是一个独立的答案,只是 Miquel 的答案的补充,我无法很好地发表评论。

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