【问题标题】:Lookup shell variables by name, indirectly [duplicate]按名称查找shell变量,间接[重复]
【发布时间】:2025-12-07 13:55:01
【问题描述】:

假设我有一个变量的名称存储在另一个变量中:

myvar=123
varname=myvar

现在,我想通过使用 $varname 变量来获得 123。 有没有直接的方法?我发现没有这样的 bash 内置按名称查找,所以想出了这个:

function var { v="\$$1"; eval "echo "$v; }

所以

var $varname  # gives 123

最后看起来并不算太糟糕,但我想知道我是否错过了更明显的东西。

【问题讨论】:

  • 这被标记为与 6 年后提出的问题的重复?恕我直言,这个问题+答案比链接的更清楚。
  • 顺便说一句,以前的答案在哪里? @tripleee
  • @mariotti 我不明白这个问题。本页顶部的黄色框链接到包含 7 个答案的重复问题。我主要考虑了赞成票的数量,但您会注意到重复项反过来也是重复项。也许这个应该被标记为链末端那个的副本。在确定某项内容是否重复时,问题的年龄通常不是决定性因素。

标签: bash eval


【解决方案1】:

来自man page of bash

${!varname}

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

【讨论】:

  • 当然,就是这样。尝试使用 RTFM,但不知何故此描述: ----------------- ${!prefix*} ${!prefix@} 名称匹配前缀。扩展为名称以前缀开头的变量的名称,由 IFS 特殊变量的第一个字符分隔。当使用 @ 并且扩展出现在双引号内时,每个变量名都会扩展为一个单独的单词。 ----------------- 没有给我留下明显的答案。为此干杯!
  • @DigitalRoss:问题被标记为“bash”,那么为什么要将答案限制为 posix 标准?
  • 这是一个很好的答案,是的,它确实适用于 bash。我想完美的答案会宣布${!varname},然后注意可移植性问题,以防止 OP 出现问题。
  • 同意,你的答案是完美的答案:)
  • @inger: ${!prefix*}${!prefix@}${!prefix} 不同
【解决方案2】:

没有直接符合 Posix 的语法,只有 bashism。我通常这样做:

eval t="\$$varname"

这将适用于任何 Posix shell,包括那些bash 是登录 shell 而/bin/sh 是像ash 这样更小更快的系统。我喜欢 bash 并将其用于我的登录 shell,但我避免在命令文件中使用 bashism。


注意:编写特定于 bash 的脚本的一个问题是,即使您可以指望安装 bash,它也可能位于路径上的任何位置。在这种情况下,使用完全通用的/usr/bin/env shebang 样式可能是个好主意,但请注意,这仍然不是 100% 可移植的,并且存在安全问题.

【讨论】:

  • 根据您需要该值的上下文,您可以使用$(eval \$$varname) 而不是赋值。
  • 几乎尝试过,但错过了。比我的短——为此欢呼!
  • 不是$(....) 非posix 吗?不应该是`eval \$$varname`吗?
  • 变量应该被引用以避免分词eval t="\$$varname".
  • @JustinHoward 是的,我曾经在示例中省略引号,但我认为它们确实应该包含在内。固定。
【解决方案3】:

${!varname} 应该可以解决问题

$ var="content"
$ myvar=var
$ echo ${!myvar}
content

【讨论】:

  • 谢谢,您的答案似乎比 tangens 晚了一点,所以我选择了那个:)
【解决方案4】:

当我需要更新我的 Bash 技能时,我通常会查看 Advance Bash-Scripting Guide

关于你的问题看Indirect References

符号是:

Version < 2
\$$var

Version >= 2
${!varname}

【讨论】:

  • 嗯,快速浏览了该指南 - 显然太快了:) 看来我必须正确阅读它.. 感谢您的提示!
  • 这是我选择的快速参考,我倾向于比手册页更频繁地使用它。但正如您所说,快速浏览有时是不够的。
  • 一般来说,ABS 应该与大量的盐一起服用。不过,这个答案本身并没有错。
【解决方案5】:
# bmuSetIndirectVar()
# TO DOUBLE CHECK THIS COMMENT AND DEMO
# This function is an helper to read indirect variables.
# i.e. get the content of a variable whose name is saved
# within an other variable. Like:
# MYDIR="/tmp"
# WHICHDIR="MYDIR"
#       bmuSetIndirectVar "WHICHDIR" "$MYDIR"
#
bmuSetIndirectVar(){
    tmpVarName=$1
    locVarName=$1
    extVarName=$2
    #echo "debug Ind Input >$1< >$2<"
    eval tmpVarName=\$$extVarName
    #echo "debug Ind Output >$tmpVarName< >$extVarName<"
    export $locVarName="${tmpVarName}"
}

我目前正在使用这个小功能。我对此并不完全满意,并且我在网上看到了不同的解决方案(如果我记得我会在这里写它们),但它似乎有效。在这几行中已经有一些冗余和额外的数据,但对调试很有帮助。

如果您想看到它就位,即我在哪里使用它,请检查: https://github.com/mariotti/bmu/blob/master/bin/backmeup.shellfunctions.sh

当然这不是最好的解决方案,但让我继续工作,在 希望我能尽快用更通用的东西代替它。

【讨论】:

  • 提问前 ;) 我不喜欢这个函数的地方是我使用了一个“eval”,它通常会触发糟糕的设计。当然也有需要的地方。我喜欢的是使用一个函数,我可以将代码从问题中分离出来,然后再进行优化。甚至可能引入 shell 版本检查等。