Bash,使用变量作为关联数组的名称时调用值

3
这个问题 bad substitution shell- trying to use variable as name of array 与我的需求类似,而且是关于数组的。我对bash脚本编程非常陌生,我需要做的事情如下:
# input
humantocheck="human1"

declare -A human1
declare -A human2

human1=( ["records_file"]="xxxxx.txt")
human2=( ["records_file"]="yyyyy.txt")

echo ${$humantocheck[records_file]}

预期输出为:
xxxxx.txt

但是当我尝试这样做时,会出现“bad substitution”错误。

你会; humantocheck不是一个数组。如果有任何东西可以工作,它将是${${!humantocheck}[records_file]}的变体,但我并不自信(我没有找到魔术组合)。 (参见shell参数扩展。) - Jonathan Leffler
@JonathanLeffler 看一下吧。对于bash还是很陌生,所以这些都是希腊文 -) - Jonathan
希腊手册中的另一个相关部分是数组。一种可能性是从human1human2创建一个通用的关联数组human,然后引用它。不幸的是,我认为没有一种方法可以一次性地分配整个关联数组。我想到的最好办法是使用循环逐个复制关联数组的元素:for key in "${!human1[@]}"; do human[$key]="${human1[$key]}"; done。一般来说,使用间接变量名会导致复杂性,并需要重新考虑/重写。 - Jonathan Leffler
2个回答

8

这正是bash 4.3特性namevars(从ksh93借鉴而来)旨在解决的场景。Namevars允许赋值,而不仅仅是查找,因此比${!foo}语法更加灵活。

# input
humantocheck="human1"

declare -A human1=( ["records_file"]="xxxxx.txt" )
declare -A human2=( ["records_file"]="yyyyy.txt" )

declare -n human=$humantocheck # make human an alias for, in this case, human1
echo "${human[records_file]}"  # use that alias
unset -n human                 # discard that alias

请参阅BashFAQ #6,详细讨论关联数组和间接扩展。


从我的角度来看,这应该是被接受的答案。 - rexford

3

使用间接引用的一种方法是:

ref=$humantocheck[records_file]
echo ${!ref}

Bash:间接拓展探索是一个关于在Bash中间接访问变量的优秀参考。

请注意,本来只是对原始代码进行最小限度修改的echo命令,在多个方面存在不安全因素。一个安全的替代方法是:

printf '%s\n' "${!ref}"

请参见为什么printf比echo更好?

1
对我来说它有效。这很有趣 - 我正在努力理解它对实现的意义。干得好!我可能会使用ref="$humantocheck[records_file]",用双引号来防止捣乱。当然,键也可以是一个变量:ref="$humantocheck[$key]" - Jonathan Leffler
太棒了!谢谢!我在数组中只有一小部分键,所以我能够在文件顶部创建像human_file=$humantocheck[human_name]这样的变量,并在整个文件中使用${!human_file}作为一种模板变量来执行其他命令!真的很酷。 - Jonathan
在赋值中,对于扩展部分不会进行单词拆分。因此,ref=$humantocheck[$key]ref="$humantocheck[$key]" 的安全性并无差异。 - pjh
@pjh 问题,使用这种语法,如何检查一个键是否存在?目前 deps_file=$app_type[deps_file]if [ -n ${!deps_file+1} ]; then echo Not set...; else echo key is set...; fi 对我来说不起作用。 - Jonathan
@Jonathan,代码中缺少引号,并且echo语句的顺序颠倒了。尝试使用if [ -n "${!deps_file+1}" ]; then echo 'Key is set' ; else echo 'Key is not set' ; fi。在Bash代码中最好使用[[...]]而不是[...],因为除了其他优点外,你大多数时候不必担心引号问题。 - pjh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接