bash中的动态变量名称
我对狂欢脚本感到困惑。
我有以下代码:
function grep_search() {
magic_way_to_define_magic_variable_$1=`ls | tail -1`
echo $magic_variable_$1
}
我希望能够创建一个包含命令的第一个参数的变量名称,并具有 ls
的最后一行的值。
因此,为了说明我想要的东西:
$ ls | tail -1
stack-overflow.txt
$ grep_search() open_box
stack-overflow.txt
那么,我应该如何定义/声明 $ magic_way_to_define_magic_variable_ $ 1
,我应该如何在脚本中称呼它?
我尝试过 eval
, $ {...}
, \ $$ {...}
,但我仍然很困惑。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
我最近一直在寻找更好的方法。对我来说,联想阵列听起来像是过度杀伤。看看我发现的内容:
...然后...
从 docs :
I've been looking for better way of doing it recently. Associative array sounded like overkill for me. Look what I found:
...and then...
From the docs:
使用具有命令名称的关联数组。
如果您不能使用关联阵列(例如,必须支持
bash
3),则可以使用声明
来创建动态变量名称:并使用间接参数扩展来访问价值。
请参阅Bashfaq:间接 - 评估间接/参考变量。
Use an associative array, with command names as keys.
If you can't use associative arrays (e.g., you must support
bash
3), you can usedeclare
to create dynamic variable names:and use indirect parameter expansion to access the value.
See BashFAQ: Indirection - Evaluating indirect/reference variables.
除了关联阵列外,还有几种方法可以实现bash中的动态变量。请注意,所有这些技术都呈现出风险,在本答案结束时进行了讨论。
在以下示例中,我将假设
i = 37
,并且您想别名名为var_37
的变量,其初始值为lolilol
。方法1。使用“指针”变量,
您可以简单地将变量的名称存储在间接变量中,与C指针不同。然后,bash具有读取的语法 aleased变量:
$ {!name}
扩展到该变量的值,其名称是变量name name name
name < /代码>。您可以将其视为两个阶段的扩展:
$ {!name}
将扩展到$ var_37
,它扩展到lolilol
。不幸的是,修改的同类变量没有对应语法。相反,您可以使用以下技巧之一来实现作业。
1a。用
eval
eval
分配是邪恶的,但也是实现我们目标的最简单,最便携的方法。您必须小心地逃脱分配的右侧,因为它将两次评估。这样做的一种简单而系统的方法是事先评估右侧(或使用
printf%q
)。和您应该手动检查左侧是有效的变量名称,或带有索引的名称(如果是
Ever> Evil_code#
?)。相比之下,下面的所有其他方法都会自动执行它。缺点:
评估
是邪恶的。eval
是邪恶的。eval
是邪恶的。1b。用
读取
读取
内置的允许您将值分配给您提供名称的变量,这一事实可以与此处串触相结合:ifs part和选项
-r
确保将值分配给AS-IS,而选项-d''
允许分配多行值。由于最后一个选项,命令使用非零退出代码返回。请注意,由于我们正在使用此处的弦,因此 newline字符附加到值。
despsides:
1C。用
printf
分配自Bash 3.1(发布2005年),
printf
内置也可以将其结果分配给给出名称的变量。与以前的解决方案相反,它只是有效的,不需要额外的努力来逃脱事物,以防止分裂等。弊端:
方法2。自Bash 4.3(发布2014年发布)以来,使用“参考”变量
,
neclare
indentin具有一个选项-n
用于创建一个变量,该变量是“名称参考”到另一个变量,就像C ++引用一样。就像方法1中一样,参考将其名称存储在别名变量的名称中,但是每次访问参考(用于读取或分配)时,BASH都会自动解决间接。此外,BASH具有一种特殊且非常令人困惑的语法,以获取参考本身的价值,请自己判断:
$ {!ref}
。这并不能避免下面解释的陷阱,但至少它使语法直接。
缺点:
风险
所有这些混叠技术都带来了几种风险。第一个是每次解决间接时执行任意代码(用于阅读或分配)。实际上,您也可以像
var_37
那样而不是标量变量名称,也可以像arr [42]
一样别名。但是Bash每次需要评估方括号的内容,因此Aliasingarr [$(do_evil)]
将产生意想不到的效果……因此,仅在控制时使用这些技术别名的出处。第二种风险是创建一个环状别名。由于狂欢变量是通过其名称而不是通过其范围来标识的,因此您可以无意间为自己创建一个别名(同时认为它会从封闭范围中吸用变量)。使用通用变量名称时,这可能会特别发生(例如
var
)。结果,才能在控制别名变量的名称的名称时使用这些技术。Source:
Beyond associative arrays, there are several ways of achieving dynamic variables in Bash. Note that all these techniques present risks, which are discussed at the end of this answer.
In the following examples I will assume that
i=37
and that you want to alias the variable namedvar_37
whose initial value islolilol
.Method 1. Using a “pointer” variable
You can simply store the name of the variable in an indirection variable, not unlike a C pointer. Bash then has a syntax for reading the aliased variable:
${!name}
expands to the value of the variable whose name is the value of the variablename
. You can think of it as a two-stage expansion:${!name}
expands to$var_37
, which expands tololilol
.Unfortunately, there is no counterpart syntax for modifying the aliased variable. Instead, you can achieve assignment with one of the following tricks.
1a. Assigning with
eval
eval
is evil, but is also the simplest and most portable way of achieving our goal. You have to carefully escape the right-hand side of the assignment, as it will be evaluated twice. An easy and systematic way of doing this is to evaluate the right-hand side beforehand (or to useprintf %q
).And you should check manually that the left-hand side is a valid variable name, or a name with index (what if it was
evil_code #
?). By contrast, all other methods below enforce it automatically.Downsides:
eval
is evil.eval
is evil.eval
is evil.1b. Assigning with
read
The
read
builtin lets you assign values to a variable of which you give the name, a fact which can be exploited in conjunction with here-strings:The
IFS
part and the option-r
make sure that the value is assigned as-is, while the option-d ''
allows to assign multi-line values. Because of this last option, the command returns with an non-zero exit code.Note that, since we are using a here-string, a newline character is appended to the value.
Downsides:
1c. Assigning with
printf
Since Bash 3.1 (released 2005), the
printf
builtin can also assign its result to a variable whose name is given. By contrast with the previous solutions, it just works, no extra effort is needed to escape things, to prevent splitting and so on.Downsides:
Method 2. Using a “reference” variable
Since Bash 4.3 (released 2014), the
declare
builtin has an option-n
for creating a variable which is a “name reference” to another variable, much like C++ references. Just as in Method 1, the reference stores the name of the aliased variable, but each time the reference is accessed (either for reading or assigning), Bash automatically resolves the indirection.In addition, Bash has a special and very confusing syntax for getting the value of the reference itself, judge by yourself:
${!ref}
.This does not avoid the pitfalls explained below, but at least it makes the syntax straightforward.
Downsides:
Risks
All these aliasing techniques present several risks. The first one is executing arbitrary code each time you resolve the indirection (either for reading or for assigning). Indeed, instead of a scalar variable name, like
var_37
, you may as well alias an array subscript, likearr[42]
. But Bash evaluates the contents of the square brackets each time it is needed, so aliasingarr[$(do_evil)]
will have unexpected effects… As a consequence, only use these techniques when you control the provenance of the alias.The second risk is creating a cyclic alias. As Bash variables are identified by their name and not by their scope, you may inadvertently create an alias to itself (while thinking it would alias a variable from an enclosing scope). This may happen in particular when using common variable names (like
var
). As a consequence, only use these techniques when you control the name of the aliased variable.Source:
下面的示例返回$ name_of_var的值
Example below returns value of $name_of_var
使用
声明
不需要在其他答案上使用前缀,也不需要。仅使用
声明
,双引号和参数扩展。我经常使用以下技巧解析参数列表conaning
n to n
格式为key = key = value eleseke equale = etherkeque et eett et eett et et et etceet = etc
,例如:一样扩展argv列表
但是:但是像额外 尖端
Use
declare
There is no need on using prefixes like on other answers, neither arrays. Use just
declare
, double quotes, and parameter expansion.I often use the following trick to parse argument lists contanining
one to n
arguments formatted askey=value otherkey=othervalue etc=etc
, Like:But expanding the argv list like
Extra tips
在这里将两个高度评价的答案结合到一个完整的示例中,希望有用且不言而喻:
输出:
您知道,我在家中有一只宠物猫
你知道吗,我在家里有一只宠物鸡
你知道吗,我在家有宠物母牛
你知道吗,我在家有宠物狗
你知道吗,我又在家有一只宠物猪
,但是阅读常规变量:
你知道吗,我在家里有一只宠物猫
你知道吗,我在家里有一只宠物鸡
你知道吗,我在家有宠物母牛
你知道吗,我在家有宠物狗
你知道吗,我在家有宠物猪
Combining two highly rated answers here into a complete example that is hopefully useful and self-explanatory:
Output:
You know what, I have a pet cat at home
You know what, I have a pet chicken at home
You know what, I have a pet cow at home
You know what, I have a pet dog at home
You know what, I have a pet pig at home
Again, but reading regular variables:
You know what, I have a pet cat at home
You know what, I have a pet chicken at home
You know what, I have a pet cow at home
You know what, I have a pet dog at home
You know what, I have a pet pig at home
这也会起作用
my_"$x"_code echo $z ## o/p: green在您的情况下,
This will work too
my_"$x"_code echo $z ## o/p: greenIn your case
对于ZSH(Newers Mac OS版本),您应该使用
代替“!”
For zsh (newers mac os versions), you should use
Instead of "!"
这应该有效:
This should work:
按 bashfaq/bashfaq/006 > with 在这里字符串语法用于分配间接变量:
用法:usage:usage:usage:usage: usage:usage:
As per BashFAQ/006, you can use
read
with here string syntax for assigning indirect variables:Usage:
不依赖于您拥有的shell/bash版本的额外方法是使用
envsubst
。例如:An extra method that doesn't rely on which shell/bash version you have is by using
envsubst
. For example:即使这是一个古老的问题,我仍然很难找到动态变量名称,同时避免了
eval
(evil)命令。用
nectrare -n
求解它,该创建了对动态值的引用,这在CI/CD进程中特别有用,在CI/CD过程中,CI/CD服务的所需秘密名称直到运行时才知道。以下是:Even though it's an old question, I still had some hard time with fetching dynamic variables names, while avoiding the
eval
(evil) command.Solved it with
declare -n
which creates a reference to a dynamic value, this is especially useful in CI/CD processes, where the required secret names of the CI/CD service are not known until runtime. Here's how:亲吻方法:
结果4
KISS approach:
results in 4
哇,大多数语法都是可怕的!这是一个具有一些简单语法的解决方案,如果您需要间接参考数组:
对于更简单的用例,我建议您建议语法在高级bash-scripting指南中描述的语法 。
Wow, most of the syntax is horrible! Here is one solution with some simpler syntax if you need to indirectly reference arrays:
For simpler use cases I recommend the syntax described in the Advanced Bash-Scripting Guide.
script.sh.sh
文件的第一个参数的变量名称:test:
per
help> help> help eval eval
:您也可以使用Bash
$ {!var}
间接扩展,但是它不支持检索数组索引。有关进一步的阅读或示例,请检查 bashfaq/006关于indirection 。
但是,您应该根据以下说明重新考虑使用间接方向。
script.sh
file:Test:
As per
help eval
:You may also use Bash
${!var}
indirect expansion, as already mentioned, however it doesn't support retrieving of array indices.For further read or examples, check BashFAQ/006 about Indirection.
However, you should re-consider using indirection as per the following notes.
虽然我认为
声明-n
仍然是最好的方法,但没有人提到的另一种方式,在CI/CD中非常有用,此功能将不支持空格,因此
Dynamic“ 2 3”
将返回错误。While I think
declare -n
is still the best way to do it there is another way nobody mentioned it, very useful in CI/CDThis function will not support spaces so
dynamic "2 3"
will return an error.对于索引数组,您可以像这样引用它们:
可以类似地引用关联数组,但需要
-A
switch Onneclare
而不是-A
。For indexed arrays, you can reference them like so:
Associative arrays can be referenced similarly but need the
-A
switch ondeclare
instead of-a
.此解决方案的POSIX合规答案
您需要将R/W权限具有
/tmp
文件夹。我们创建一个保存我们的变量的临时文件,并利用
-A
set> set
内置的标志:因此,如果我们创建一个保留动态变量的文件,我们可以使用设置将它们在脚本中栩栩如生。
实施
解释了上述步骤:
POSIX compliant answer
For this solution you'll need to have r/w permissions to the
/tmp
folder.We create a temporary file holding our variables and leverage the
-a
flag of theset
built-in:Therefore, if we create a file holding our dynamic variables, we can use set to bring them to life inside our script.
The implementation
Explaining the steps above:
小心:我刚刚发现这项工作非常不同 - 在
zsh
中不起作用。因此,请确保您在外壳中的意思是您要参加;或将出于脚本的最终执行上下文。Careful: I just found this works very differently - doesn't work - in
zsh
. So make sure you're in the shell you mean to be in; or will be in for your script's eventual execution context.对于
varname = $ prefix_suffix
格式,只需使用:for
varname=$prefix_suffix
format, just use: