从函数输出创建Bash关联数组:为什么"declare -A foo=$(bar)"可以工作,但是"declare -A foo; foo=$(bar)"不行?

4

考虑以下bash片段。在其中,我以不同的方式创建了两个关联数组,然后打印了一个条目。

情况1。 声明并在一条语句中赋值。按预期工作:

make_person() { echo '([firstName]=Bob [lastName]=Black)'; }
declare -A person1=$(make_person)
echo "${person1[firstName]}"

输出:

Bob

情景2. 声明和赋值分两行。没有输出:

declare -A person2
person2=$(make_person)
echo "${person2[firstName]}"

输出:


为什么场景1成功打印[firstName],而场景2则不行?两个场景是否都定义且预期行为?
3个回答

7

因为 declare 重新解释和解析参数 以检测变量赋值中的 ( ),然后进行数组赋值。而在普通赋值中,var=anything 总是普通赋值,在 var=(anything) 中,( ) 在解析表达式时被检测到(当bash读取要执行的行/输入时)。declare 的工作更像 eval,它重新评估了输入。

什么是Bash特定的原因,使得方案1成功打印出 [firstName] 条目,而方案2则不成功?

"bash特定原因"我猜是程序员这样编程。原因也是为了与POSIX兼容,所以var=$(anything)应该像在POSIX中一样产生一个正常的变量。
“定义”有点过了,在bash文档中我没有看到关于这个特定语法的太多内容,但是。
"期望的行为是什么?"
是的,这是按预期工作的。

2
没错。在情况1中,shell会扩展$(make_person),因此declare接收到一个合法的复合赋值。在情况2中,由于等号后面没有跟着开括号,所以该赋值被视为标量,并且扩展是在之后执行的。 - oguz ismail

2

跟进KamilCuk的出色回答,这只是一个评论,但涉及格式问题。

要查看declare -A person2; person2=$(make_person)的效果,请使用declare -p

$ declare -p person1 person2
declare -A person1=([lastName]="Black" [firstName]="Bob" )
declare -A person2=([0]="([firstName]=Bob [lastName]=Black)" )

为了让这个“工作”,你需要使用eval
$ eval "person2=$(make_person)"
$ declare -p person1 person2
declare -A person1=([lastName]="Black" [firstName]="Bob" )
declare -A person2=([lastName]="Black" [firstName]="Bob" )

但是,就像您在第一种形式中发现的那样,使用declare的类似于eval的功能:declare -A ary=$(cmd)


1
作为对你已经得到的详尽答案的补充:
如果你要从一个函数中填充Bash关联数组,那么你也可以从Bash v4.2+的名称引用功能中受益,它具有附加的好处,即它不会分叉子shell。
#!/usr/bin/env bash

make_person() {
  local -n array=$1
  # shellcheck disable=SC2034 # shellcheck cannot track nameref
  array=([firstName]=Bob [lastName]=Black)
}

declare -A person1

make_person person1

declare -p person1

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