【问题标题】:How to get a variable value if variable name is stored as string?如果变量名存储为字符串,如何获取变量值?
【发布时间】:2010-12-27 15:15:31
【问题描述】:

如果我将变量名作为字符串,如何检索 bash 变量值?

var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.

上下文:

我有一些 AMI (Amazon Machine Image),我想启动每个 AMI 的几个实例。他们完成启动后,我想根据其 AMI 类型设置每个实例。我不想在任何 AMI 中烘焙大量脚本或密钥,因此我准备了一个通用启动脚本,并将其放在 S3 上,并提供可公开访问的链接。在 rc.local 中,我放了一小段代码来获取启动脚本并执行它。这就是我在 AMI 中的全部内容。然后每个 AMI 访问适用于所有 AMI 的通用配置脚本和每个 AMI 的特殊设置脚本。这些脚本是私有的,需要一个签名的 URL 才能访问它们。

所以现在,当我启动一个 AMI 实例 (my_private_ami_1) 时,我为 S3 上显示的另一个文件传递了一个签名 URL,该文件包含所有私有脚本的键/值对的签名 URL。

config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
当启动脚本运行时,它会下载上面的文件和source's it。然后它会检查其 AMI 类型并为自己选择正确的设置脚本。
ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises

所以现在我可以有一个通用代码,它可以触发实例,而不管它们的 AMI 类型如何,并且实例可以自行处理。

【问题讨论】:

    标签: string bash


    【解决方案1】:

    在 bash 4.3+ 中,您可以使用 declare -n:

    #!/usr/bin/env bash
    
    var="this is the real value"
    var_name="var"
    
    declare -n var_ref=$var_name
    echo "${var_ref}"
    

    【讨论】:

      【解决方案2】:

      对于我的 zsh 用户,完成与接受的答案相同的事情的方法是使用:

      echo ${(P)a} # outputs 'this is the real value'
      

      它被恰当地称为Parameter name replacement

      这会强制将参数名称的值解释为 进一步的参数名称,其值将在适当的地方使用。 请注意,使用 typeset 系列命令之一设置的标志(在 特殊情况转换)不适用于 name 的值 以这种方式使用。

      如果与嵌套参数或命令替换一起使用,则结果 将以相同的方式将其作为参数名称。例如, 如果你有‘foo=bar’和‘bar=baz’,字符串${(P)foo}, ${(P)${foo}} 和 ${(P)$(echo bar)} 将扩展为“baz”。

      同样,如果引用本身是嵌套的,则带有 flag 被视为直接由参数名称替换。 如果这个嵌套替换产生一个包含更多 不止一个字。例如,如果‘name=assoc’,其中参数 assoc 是一个关联数组,那么‘${${(P)name}[elt]}’指的是 关联下标“elt”的元素。

      【讨论】:

        【解决方案3】:

        在 bash 4.3 中,引入了设置变量的“-v”测试。同时,增加了“nameref”声明。这两个特性与间接运算符 (!) 一起实现了前面示例的简化版本:

        get_value()
        {
          declare -n var_name=$1
          if [[ -v var_name ]]
          then
            echo "${var_name}"
          else
            echo "variable with name <${!var_name}> is not set"
          fi
        }
        
        test=123
        get_value test
        123
        
        test="\$(echo \"something nasty\")"
        get_value test
        $(echo "something nasty")
        
        unset test
        get_value test
        variable with name <test> is not set
        

        由于这种方法无需“评估”,因此更安全。 此代码在 bash 5.0.3(1) 下检查。

        【讨论】:

          【解决方案4】:

          对数组也有同样的问题,如果你也在操作数组,这里是如何做的:

          array_name="ARRAY_NAME"
          ARRAY_NAME=("Val0" "Val1" "Val2")
          
          ARRAY=$array_name[@]
          echo "ARRAY=${ARRAY}"
          ARRAY=("${!ARRAY}")
          echo "ARRAY=${ARRAY[@]}"
          echo "ARRAY[0]=${ARRAY[0]}"
          echo "ARRAY[1]=${ARRAY[1]}"
          echo "ARRAY[2]=${ARRAY[2]}"
          

          这将输出:

          ARRAY=ARRAY_NAME[@]
          ARRAY=Val0 Val1 Val2
          ARRAY[0]=Val0
          ARRAY[1]=Val1
          ARRAY[2]=Val2
          

          【讨论】:

          • +1 因为这是数组唯一正确的答案,为了更清楚,字符串必须有[@] 后缀才能获得完整的数组。
          【解决方案5】:

          根据答案:https://unix.stackexchange.com/a/111627

          ###############################################################################
          # Summary: Returns the value of a variable given it's name as a string.
          # Required Positional Argument: 
          #   variable_name - The name of the variable to return the value of
          # Returns: The value if variable exists; otherwise, empty string ("").
          ###############################################################################
          get_value_of()
          {
              variable_name=$1
              variable_value=""
              if set | grep -q "^$variable_name="; then
                  eval variable_value="\$$variable_name"
              fi
              echo "$variable_value"
          }
          
          test=123
          get_value_of test
          # 123
          test="\$(echo \"something nasty\")"
          get_value_of test
          # $(echo "something nasty")
          

          【讨论】:

            【解决方案6】:

            你可以使用${!a}:

            var1="this is the real value"
            a="var1"
            echo "${!a}" # outputs 'this is the real value'
            

            这是一个间接parameter expansion的例子:

            参数扩展的基本形式是${parameter}。的价值 parameter 被替换。

            如果parameter 的第一个字符是感叹号(!),它 引入了变量间接级别。 Bash 使用 由parameter 的其余部分组成的变量作为 多变的;然后扩展此变量,并将该值用于 其余的替换,而不是 parameter 本身的值。

            【讨论】:

            • 这适用于我在 OSX bash 中,但不适用于 debian?在 debian 上我得到 Bad substitution 错误。
            • @23inhouse 您是否使用/bin/sh 运行您的脚本?如果是这样,请尝试改用/bin/bash。从 Debian Squeeze 开始,/bin/sh 更改为 symlink for dash instead of bash。 dash 不支持这种特殊语法,会输出 Bad substitution 错误。
            • 有没有办法让属于数组的变量名工作?
            • 使用 bash 在 ubuntu 上工作
            • @DoronShai 这是一个间接参数扩展的例子,记录在 Bash 参考手册gnu.org/software/bash/manual/html_node/…
            【解决方案7】:
            X=foo
            Y=X
            eval "Z=\$$Y"
            

            将 Z 设置为“foo”

            小心使用eval,因为这可能允许通过${Y} 中的值意外执行代码。这可能会通过代码注入造成危害。

            例如

            Y="\`touch /tmp/eval-is-evil\`"
            

            将创建/tmp/eval-is-evil。当然,这也可能是一些rm -rf /。

            【讨论】:

            • 救了我的尾巴,ty
            【解决方案8】:

            现代 shell 已经支持数组(甚至关联数组)。所以请务必使用它们,并少用 eval。

            var1="this is the real value"
            array=("$var1")
            # or array[0]="$var1"
            

            然后当你想调用它时,回显 ${array[0]}

            【讨论】:

            • 如果我从命令行参数(例如$1)获取字符串,这会起作用吗?
            【解决方案9】:

            修改了我的搜索关键字并知道了 :)。

            eval a=\$$a
            感谢您的宝贵时间。

            【讨论】:

            • 使用 eval 时要小心,因为这可能允许通过 ${Y} 中的值意外执行代码。在用户“anon”的回答中查看我的补充。
            • 这是唯一有效的答案。讽刺的是它是你自己的!
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-06-28
            • 2014-02-12
            • 2022-01-23
            • 1970-01-01
            • 2013-08-27
            相关资源
            最近更新 更多