在我的Bash脚本中,如何检查关联数组元素是否存在?

15
我有一个动物数组:

I have an array of animals:

declare -A animals=()
animals+=([horse])

我想检查一个动物是否存在:

if [ -z "$animals[horse]"]; then
    echo "horse exists";
fi

但是这并不起作用。


你可能会在这个全面的答案集中找到好运。https://dev59.com/hGYs5IYBdhLWcg3wAfLO#68701581 - Anthony Rutledge
请参见 https://dev59.com/xaXja4cB1Zd3GeqPNjN1。 - U. Windl
4个回答

28
在bash 4.3中,可以对数组使用-v运算符。
declare -A animals
animals[horse]=neigh
# Fish are silent
animals[fish]=
[[ -v animals[horse] ]] && echo "horse exists"
[[ -v animals[fish] ]] && echo "fish exists"
[[ -v animals[unicorn] ]] || echo "unicorn does not exist" 

在之前的版本中,你需要更加小心地区分键不存在和键引用任何空字符串这两种情况。

animal_exists () {
    # If the given key maps to a non-empty string (-n), the
    # key obviously exists. Otherwise, we need to check if
    # the special expansion produces an empty string or an
    # arbitrary non-empty string.
    [[ -n ${animals[$1]} || -z ${animals[$1]-foo} ]]
}

animal_exists horse && echo "horse exists"
animal_exists fish && echo "fish exists"
animal_exists unicorn || echo "unicorn does not exist"

以上答案第一部分有错别字。最后一行应该是:[[ -v animals[unicorn] ]] || echo "unicorn does not exist"而不是[[ -v animals[unicorn] ]] && echo "unicorn does not exist"。此外,测试命令不需要使用双括号。单括号也可以。 - MrPotatoHead
1
我更喜欢使用双括号,因为“-v”不是POSIX标准的一部分,而如果你有“-v”,那么你也有“[[”。 - chepner
请注意,在脚本中使用set -u时,[[ -v foo[bar] ]]是有效的。否则,如果尝试扩展不存在的数组成员,将会出现错误,因此类似test -n "${foo[bar]}"的语句将无法正常工作。 - undefined

10

你的脚本中有几个拼写错误。

当我按照现有的代码运行时,从BASH获取以下错误信息:

1. animals: [horse]: must use subscript when assigning associative array
2. [: missing `]'
第一个说法是,如果你想将“horse”作为关联数组的索引使用,你必须给它赋一个值。空值(null)也可以。
-animals+=([horse])
+animals+=([horse]=)
第二个消息说,如果未用空格将要测试的值和方括号分开,方括号将被视为该值的一部分。
-if [ -z "$animals[horse]"]; then
+if [ -z "$animals[horse]" ]; then

最后,当关联数组中有一个值被赋值时,该元素就存在了(即使这个值是空的)。由于关于检测数组值是否已设置的问题已经在此网站上得到回答,因此我们可以借鉴解决方案。

-if [ -z "$animals[horse]"]; then
+if [ -n "${animals[horse]+1}" ]; then

为了方便起见,在这里提供完整的脚本:


declare -A animals=()
animals+=([horse]=)

if [ -n "${animals[horse] + 1}" ]; then
    echo "horse exists";
fi

4
与使用 += 相比,写 animals[horse]= 要简单得多。 - chepner
@chepner 好的,除非楼主想一次定义多个元素。 - Dima Chubarov

3
在BASH中,您可以执行以下操作:
declare -A animals=()
animals+=([horse]=)

[[ "${animals[horse]+foobar}" ]] && echo "horse exists"

"${animals[horse]+foobar}" 返回 foobar 如果 horse 是数组中的有效索引,否则它将返回空值。


抱歉,...在"animals+=([horse]=)"行末的=是什么意思? - sensorario
这是将空值分配给索引“horse”的赋值操作。否则,“animals+=([horse])”会导致语法错误。 - anubhava

0
有点老了,但当我在寻找答案时出现了这个问题,我的系统没有bash 4.3,我特别关心animals[horse-2]的例子。
另一种方法是通过将数组内容转储出来,然后检查子字符串是否匹配。
declare -A animals
animals[horse-2]=neigh-2
animals[dog]=woof
animals_as_string=$(declare -p animals)
other_animals="horse-2 moose cow"

for animal in $other_animals
do
  if [[ "$animal" == *$animals_as_string" ]]; then
    echo "found $animal"
  fi
done

假设你需要一些额外的东西来确保在搜索animals[horse](如果你的数组中有这个)时不会找到animals[horse-2]


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