【问题标题】:Trying to assign a value to associative array within `eval` does not work尝试为“eval”中的关联数组赋值不起作用
【发布时间】:2022-11-17 23:15:49
【问题描述】:

当试图定义辅助函数来构建关联列表时,出现错误,我无法自行解决 (BASH 4.4):

/tmp/foo.sh: line 18: 'key': syntax error: operand expected (error token is "'key'")

对于 BASH 4.3,我得到了:

/tmp/foo.sh: line 18: key: unbound variable

这是测试用例:

#!/bin/bash
set -u

# add services list
add_list()
{
    local list="$1"

    eval "declare -a ${list}=(); declare -A ${list}_A=()"
}

# add services to list of services
add_service()
{
    local list="$1" def="$2"
    local s="${def%%:*}"

    eval "${list}+=('$def'); ${list}_A['$s']='$def'"
}

add_list TEST
add_service TEST 'key:value'

使用两个数组的原因是我想按键访问元素,并且我想保留原始顺序(实际上 ${list}+=('$s') 就足够了)。

这是 bash -x 的输出:

> bash -x /tmp/foo.sh 
+ set -u
+ add_list TEST
+ local list=TEST
+ eval 'declare -a TEST=(); declare -A TEST_A=()'
++ TEST=()
++ declare -a TEST
++ TEST_A=()
++ declare -A TEST_A
+ add_service TEST key:value
+ local list=TEST def=key:value
+ local s=key
+ eval 'TEST+=('\''key:value'\''); TEST_A['\''key'\'']='\''key:value'\'''
++ TEST+=('key:value')
++ TEST_A['key']=key:value
/tmp/foo.sh: line 18: 'key': syntax error: operand expected (error token is "'key'")

【问题讨论】:

    标签: bash eval associative-array


    【解决方案1】:

    在函数中声明的数组保持局部作用域,除非数组也用 global 标志声明;考虑以下:

    $ mytest() { typeset -a myarray; typeset -p myarray; echo "##### mytest(): exit"; }
                         ^^
    $ unset myarray
    $ mytest
    declare -a myarray                           # array exists while inside the function
    ##### mytest(): exit
    
    $ typeset -p myarray
    -bash: typeset: myarray: not found           # array no longer exists once outside the function
    

    现在添加 -g 标志:

    $ mytest() { typeset -ag myarray; typeset -p myarray; echo "##### mytest(): exit"; }
                         ^^^
    $ unset myarray
    $ mytest
    declare -a myarray                           # array exists while inside the function
    ##### mytest(): exit
    
    $ typeset -p myarray
    declare -a myarray                           # array still exists after leaving function
    

    添加g标志:OP的当前功能

    add_list()
    {
        local list="$1"
    
        eval "declare -ag ${list}=(); declare -Ag ${list}_A=()"
        #             ^^^                     ^^^
    }
    

    笔记:add_service 函数定义现在可以保持原样

    运行 OP 的测试:

    $ unset TEST TEST_A
    $ add_list TEST
    $ add_service TEST 'key:value'
    $ typeset -p TEST TEST_A
    declare -a TEST=([0]="key:value")
    declare -A TEST_A=([key]="key:value" )
    

    【讨论】:

      【解决方案2】:

      在函数中使用 declare 会使变量成为局部变量,因此可能无法在父母函数内的作用域。

      对于普通数组,可以只检查以下结果:

      #!/bin/bash
      
      make_it_an_array() {
          [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*$ ]] || return 1
          eval "$1=()"
      }
      
      fun1() {
          local arr # arr isn't an array, so what will happen?
          make_it_an_array arr
          printf '"%s" in %s: %s
      ' "$1" "$FUNCNAME" "$(declare -p arr 2> /dev/null)"
      }
      
      #############################################################
      
      fun1 arr
      printf '"arr" in main: %s
      ' "$(declare -p arr 2> /dev/null)"
      
      "arr" in fun1: declare -a arr='()'
      "arr" in main: 
      

      如果将 eval "$1=()" 替换为 declare -ga "$1=()",则结果为:

      "arr" in fun1: declare -a arr='()'
      "arr" in main: declare -a arr='()'
      

      这意味着父函数中变量的局部作用域丢失了


      在旁边:

      如果 $list 只包含一个变量的名称,那么你可以替换:

      eval "declare -a ${list}=(); declare -A ${list}_A=()"
      

      和:

      declare -a "$list=()"
      declare -A "${list}_A=()"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-24
        • 1970-01-01
        • 1970-01-01
        • 2011-10-07
        • 2018-07-16
        • 2015-03-21
        • 2021-03-16
        相关资源
        最近更新 更多