如何使用命令输出填充bash关联数组?

11

我正在尝试使用命令的输出填充一个关联数组。如果没有命令,我可以这样做:

$ declare -A x=( [first]=foo [second]=bar )
$ echo "${x[first]}, ${x[second]}"
foo, bar

我可以使用以下命令将非关联数组填充为命令输出:

$ declare y=( $(echo 'foo bar') )
$ echo "${y[0]}, ${y[1]}"
foo, bar

但是,当我尝试在上述两者的基础上构建一个语句来从命令中填充关联数组时,我会收到以下错误消息:

$ declare -A z=( $(echo '[first]=foo [second]=bar') )
-bash: z: $(echo '[first]=foo [second]=bar'): must use subscript when assigning associative array

我为什么会收到那个错误消息,正确的语法是什么来填充一个关联数组与命令的输出?我试图避免使用eval(通常的原因),不想使用临时文件,当然,echo只是作为一个产生所需效果的命令的示例,实际命令会更加复杂。基于下面的几个答案,看起来问题只是我的引号有问题:
$ declare -A z="( $(echo '[first]=foo [second]=bar') )"
$ echo "${z[first]}, ${z[second]}"
foo, bar

并且在索引和值中带有空格:

$ declare -A z="( $(echo '[first field]="foo with space" [second]="space bar"') )"
$ echo "${z[first field]}, ${z[second]}"
foo with space, space bar

回应评论中的一个问题(如何使用命令输出填充Bash关联数组?),需要解释为什么引号是必要的 - 我不确定,但也许其他人可以使用此脚本的结果作为参考来解释(不希望在索引数组中使用指定的索引,它们只是作为字符串的一部分被填充为数组值):

$ cat tst.sh
#!/bin/env bash

set -x

printf 'Indexed, no quotes\n'
declare -a w=( $(echo '[first]=foo [second]=bar') )
declare -p w

printf '\n---\n'

printf 'Indexed, with quotes\n'
declare -a x="( $(echo '[first]=foo [second]=bar') )"
declare -p x

printf '\n---\n'

printf 'Associative, no quotes\n'
declare -A y="( $(echo '[first]=foo [second]=bar') )"
declare -p y

printf '\n---\n'

printf 'Associative, with quotes\n'
declare -A z=( $(echo '[first]=foo [second]=bar') )
declare -p z

.

$ ./tst.sh
+ printf 'Indexed, no quotes\n'
Indexed, no quotes
+ w=($(echo '[first]=foo [second]=bar'))
++ echo '[first]=foo [second]=bar'
+ declare -a w
+ declare -p w
declare -a w=([0]="[first]=foo" [1]="[second]=bar")
+ printf '\n---\n'

---
+ printf 'Indexed, with quotes\n'
Indexed, with quotes
++ echo '[first]=foo [second]=bar'
+ declare -a 'x=( [first]=foo [second]=bar )'
+ declare -p x
declare -a x=([0]="bar")
+ printf '\n---\n'

---
+ printf 'Associative, no quotes\n'
Associative, no quotes
++ echo '[first]=foo [second]=bar'
+ declare -A 'y=( [first]=foo [second]=bar )'
+ declare -p y
declare -A y=([second]="bar" [first]="foo" )
+ printf '\n---\n'

---
+ printf 'Associative, with quotes\n'
Associative, with quotes
+ z=($(echo '[first]=foo [second]=bar'))
./tst.sh: line 24: z: $(echo '[first]=foo [second]=bar'): must use subscript when assigning associative array
+ declare -A z
+ declare -p z
declare -A z=()
3个回答

12

这里是使用传统的while循环方法从命令输出中填充关联数组的方式:

while IFS= read -r; do
   declare -A z+="( $REPLY )"
done < <(printf '[first]=foo [second]=bar\n[third]=baz\n')

# check output
$> echo "${z[first]}, ${z[second]}, ${z[third]}"
foo, bar, baz

# or declare -p
$> declare -p z
declare -A z='([third]="baz" [second]="bar" [first]="foo" )'

编辑:您的原始尝试也将在使用正确引号的情况下起作用:

$> unset z

$> declare -A z="( $(echo '[first]=foo [second]=bar') )"

$> declare -p z
declare -A z='([second]="bar" [first]="foo" )'

可以这样做。我们需要在其中添加 IFS=,并且不能在整个内容周围使用 $'...',否则它会将 \t 转换为制表符等。但是,我们可能可以在每个数组元素之间获取文字换行符甚至 NUL 字符(并使用 NUL 标志读取)。还需要考虑一下,谢谢! - Ed Morton
1
我曾经在SO上被告知,如果我们使用内部的$REPLY变量,则不需要使用IFS=,但我想使用它也没有什么坏处。(已编辑) - anubhava
嗯,换行符实际上并没有起到分隔值的作用,当值只是以空格分隔时,脚本可以正常工作,printf 只需要在末尾加一个换行符即可。 - Ed Morton
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - anubhava
1
是的,我刚刚在我的问题的末尾发布了那个。简直不敢相信我没有引用某些东西而被咬了一口!现在需要扩展它以获得正确处理索引和/或值中的空白字符的语法。除非有人提出一个好的理由并提供更好的替代方案,否则我将在今天晚些时候接受这个答案。谢谢。 - Ed Morton
显示剩余3条评论

3
我想这可能有点脆弱,但是你可以将整个z=(...)赋值作为命令替换的结果。
declare -A "$(echo z="($(echo '[first]=foo [second]=bar'))")"

有趣。我没有想到过那个。可能不想使用 echo z=...,但某种变化的 printf 'z="%s" ....' 可能更加健壮。我得好好考虑一下!谢谢。 - Ed Morton
1
正确的结束应该是 )")" 而不是 "))",但这还不足以进行编辑 :) - Matei David
我猜因为没有非引用的(与非引用的)配对,所以它不需要被引用。不管怎样,还是修复了,因为我确定其他输入可能会使其从可工作变为不可工作。 - chepner
看起来我所需要的就是引用我的原始任务,参见https://dev59.com/DFkS5IYBdhLWcg3w05eT#39418782的底部。有点尴尬...再次感谢。 - Ed Morton
1
啊,我真的没有尝试过这里建议的嵌套怪物吗? - chepner

1

考虑到这个有效:

declare -A z=([first]=$(echo 'foo') [second]=$(echo 'bar'))

我猜Bash需要在进行任何替换之前看到关联数组初始化列表。所以我看不到避免使用eval的方法:
eval "declare -A z=($(echo '[first]=foo [second]=bar'))"

什么是避免使用 eval 的“常见原因”?

4
谷歌“shell eval evil”可了解有关使用“eval”存在的问题的讨论。感谢回复,仍希望有替代方案。 - Ed Morton
看起来我所需要的就是引用我的原始任务,参见https://dev59.com/DFkS5IYBdhLWcg3w05eT#39418782的底部。有点尴尬...再次感谢。 - Ed Morton

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