【问题标题】: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=()"