110

使用:

set -o nounset
  1. 有一个索引数组,如:

    myArray=( "red" "black" "blue" )
    

    检查是否设置了元素 1 的最短方法是什么?
    我有时会使用以下内容:

    test "${#myArray[@]}" -gt "1" && echo "1 exists" || echo "1 doesn't exist"
    

    我想知道是否有首选。

  2. 如何处理不连续的索引?

    myArray=()
    myArray[12]="red"
    myArray[51]="black"
    myArray[129]="blue"
    

    例如,如何快速检查51已经设置的?

  3. 如何处理关联数组?

    declare -A myArray
    myArray["key1"]="red"
    myArray["key2"]="black"
    myArray["key3"]="blue"
    

    例如,如何快速检查key2已经使用的?

4

10 回答 10

151

To check if the element is set (applies to both indexed and associative array)

[ "${array[key]+abc}" ] && echo "exists"

Basically what ${array[key]+abc} does is

  • if array[key] is set, return abc
  • if array[key] is not set, return nothing

References:
  1. See Parameter Expansion in Bash manual and the little note

if the colon is omitted, the operator tests only for existence [of parameter]

  1. This answer is actually adapted from the answers for this SO question: How to tell if a string is not defined in a bash shell script?

A wrapper function:

exists(){
  if [ "$2" != in ]; then
    echo "Incorrect usage."
    echo "Correct usage: exists {key} in {array}"
    return
  fi   
  eval '[ ${'$3'[$1]+muahaha} ]'  
}

For example

if ! exists key in array; then echo "No such array element"; fi 
于 2012-11-04T18:25:07.793 回答
49

man bash,条件表达式:

-v varname
              True if the shell variable varname is set (has been assigned a value).

例子:

declare -A foo
foo[bar]="this is bar"
foo[baz]=""
if [[ -v "foo[bar]" ]] ; then
  echo "foo[bar] is set"
fi
if [[ -v "foo[baz]" ]] ; then
  echo "foo[baz] is set"
fi
if [[ -v "foo[quux]" ]] ; then
  echo "foo[quux] is set"
fi

这将表明 foo[bar] 和 foo[baz] 都已设置(即使后者设置为空值)而 foo[quux] 未设置。

于 2017-07-29T05:00:27.120 回答
22

新答案

-v的 4.2 版(和更高版本)开始,内置 test命令有一个新选项。

从 4.3 版开始,此测试可以处理数组元素。

array=([12]="red" [51]="black" [129]="blue")

for i in 10 12 30 {50..52} {128..131};do
    if [ -v 'array[i]' ];then
        echo "Variable 'array[$i]' is defined"
    else
        echo "Variable 'array[$i]' not exist"
    fi
done
Variable 'array[10]' not exist
Variable 'array[12]' is defined
Variable 'array[30]' not exist
Variable 'array[50]' not exist
Variable 'array[51]' is defined
Variable 'array[52]' not exist
Variable 'array[128]' not exist
Variable 'array[129]' is defined
Variable 'array[130]' not exist
Variable 'array[131]' not exist

注意:关于ssc 的注释,我在test 中引号,以满足 shellcheck 的错误SC2208。这似乎在这里并不是真正需要的,因为无论如何都没有 glob 字符......'array[i]'-v array[i]

这以相同的方式与关联数组一起工作:

declare -A aArray=([foo]="bar" [bar]="baz" [baz]=$'Hello world\041')

for i in alpha bar baz dummy foo test;do
    if [ -v 'aArray[$i]' ];then
        echo "Variable 'aArray[$i]' is defined"
    else
        echo "Variable 'aArray[$i]' not exist"
    fi
done
Variable 'aArray[alpha]' not exist
Variable 'aArray[bar]' is defined
Variable 'aArray[baz]' is defined
Variable 'aArray[dummy]' not exist
Variable 'aArray[foo]' is defined
Variable 'aArray[test]' not exist

有一点不同:
在常规数组中,括号([i])之间的变量是整数,所以$不需要美元符号(),但是对于关联数组,因为key是一个单词,$所以需要([$i])!

V4.2 之前的旧答案

不幸的是,bash 无法区分变量和未定义变量。

但是有一些方法:

$ array=()
$ array[12]="red"
$ array[51]="black"
$ array[129]="blue"

$ echo ${array[@]}
red black blue

$ echo ${!array[@]}
12 51 129

$ echo "${#array[@]}"
3

$ printf "%s\n" ${!array[@]}|grep -q ^51$ && echo 51 exist
51 exist

$ printf "%s\n" ${!array[@]}|grep -q ^52$ && echo 52 exist

(不回答)

对于关联数组,您可以使用相同的:

$ unset array
$ declare -A array
$ array["key1"]="red"
$ array["key2"]="black"
$ array["key3"]="blue"
$ echo ${array[@]}
blue black red

$ echo ${!array[@]}
key3 key2 key1

$ echo ${#array[@]}
3

$ set | grep ^array=
array=([key3]="blue" [key2]="black" [key1]="red" )

$ printf "%s\n" ${!array[@]}|grep -q ^key2$ && echo key2 exist || echo key2 not exist
key2 exist

$ printf "%s\n" ${!array[@]}|grep -q ^key5$ && echo key5 exist || echo key5 not exist
key5 not exist

您可以在不需要外部工具的情况下完成这项工作(没有 printf|grep 作为纯 bash),为什么不将checkIfExist()构建为新的 bash 函数:

$ checkIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) return 0 ;;
        * ) return 1 ;;
      esac";
}

$ checkIfExist array key2 && echo exist || echo don\'t
exist

$ checkIfExist array key5 && echo exist || echo don\'t
don't

甚至创建一个新的getIfExist bash 函数,该函数返回所需的值并在所需的值不存在时以错误的结果代码退出:

$ getIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) echo \${$1[$2]};return 0 ;;
        * ) return 1 ;;
      esac";
}

$ getIfExist array key1
red
$ echo $?
0

$ # now with an empty defined value
$ array["key4"]=""
$ getIfExist array key4

$ echo $?
0
$ getIfExist array key5
$ echo $?
1
于 2012-11-04T15:15:32.250 回答
11

-n测试和操作员呢:-

例如,这个脚本:

#!/usr/bin/env bash

set -e
set -u

declare -A sample

sample["ABC"]=2
sample["DEF"]=3

if [[ -n "${sample['ABC']:-}" ]]; then
  echo "ABC is set"
fi

if [[ -n "${sample['DEF']:-}" ]]; then
  echo "DEF is set"
fi

if [[ -n "${sample['GHI']:-}" ]]; then
  echo "GHI is set"
fi

印刷:

ABC is set
DEF is set
于 2019-07-01T19:25:47.007 回答
5

在 bash 4.3.39(1)-release 中测试

declare -A fmap
fmap['foo']="boo"

key='foo'
# should echo foo is set to 'boo'
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi
key='blah'
# should echo blah is unset in fmap
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi
于 2015-11-13T03:28:48.937 回答
2

从Thamme重申这一点:

[[ ${array[key]+Y} ]] && echo Y || echo N

这将测试变量/数组元素是否存在,包括它是否设置为空值。这适用于比 -v 更广泛的 bash 版本,并且似乎对 set -u 之类的东西不敏感。如果您使用此方法看到“错误的数组下标”,请发布示例。

于 2021-02-23T12:37:02.240 回答
1

我写了一个函数来检查一个键是否存在于 Bash 的数组中:

# Check if array key exists
# Usage: array_key_exists $array_name $key
# Returns: 0 = key exists, 1 = key does NOT exist
function array_key_exists() {
    local _array_name="$1"
    local _key="$2"
    local _cmd='echo ${!'$_array_name'[@]}'
    local _array_keys=($(eval $_cmd))
    local _key_exists=$(echo " ${_array_keys[@]} " | grep " $_key " &>/dev/null; echo $?)
    [[ "$_key_exists" = "0" ]] && return 0 || return 1
}

例子

declare -A my_array
my_array['foo']="bar"

if [[ "$(array_key_exists 'my_array' 'foo'; echo $?)" = "0" ]]; then
    echo "OK"
else
    echo "ERROR"
fi

使用 GNU bash 测试,版本 4.1.5(1)-release (i486-pc-linux-gnu)

于 2017-02-22T10:45:34.070 回答
1

对于所有时间的人,一劳永逸。

有一个“干净的代码”很长的路要走,还有一个更短、更简洁、以 bash 为中心的方式。

$1= 您要查找的索引或键。

$2=通过引用传入的数组/映射。

function hasKey ()
{
    local -r needle="${1:?}"
    local -nr haystack=${2:?}

    for key in "${!haystack[@]}"; do
        if [[ $key == $needle ]] ;
            return 0
        fi
    done

    return 1
}

线性搜索可以用二分搜索代替,二分搜索在更大的数据集上表现更好。只需先对键进行计数和排序,然后随着您越来越接近答案,将干草堆进行经典的二进制减半。

现在,对于纯粹主义者来说,就像“不,我想要性能更高的版本,因为我可能不得不在 bash 中处理大型数组”,让我们看看一个更以 bash 为中心的解决方案,但它可以保持干净的代码和灵活性处理数组或映射。

function hasKey ()
{
    local -r needle="${1:?}"
    local -nr haystack=${2:?}

    [ -n ${haystack["$needle"]+found} ]
}

该行[ -n ${haystack["$needle"]+found} ]使用${parameter+word}bash 变量扩展的形式,而不是${parameter:+word}形式,它也试图测试一个键的值,这不是手头的事情

用法

local -A person=(firstname Anthony lastname Rutledge)

if hasMapKey "firstname" person; then
     # Do something
fi

当不执行子字符串扩展时,使用下面描述的形式(例如,':-'),Bash 测试未设置或为空的参数。省略冒号会导致仅对未设置的参数进行测试。换句话说,如果包含冒号,则运算符会测试两个参数的存在以及它的值是否不为空;如果省略冒号,则运算符仅测试是否存在。

${参数:-字}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

${参数:=字}

If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional

不能以这种方式分配参数和特殊参数。${参数:?word}

If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard

错误和外壳,如果它不是交互式的,则退出。否则,参数的值被替换。${参数:+字}

If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion

如果$needle不存在则展开为空,否则展开为长度非零的字符串“found”。如果事实确实存在(正如我所说的“找到”),这将使-n测试成功,否则失败。$needle

于 2021-08-08T14:11:39.317 回答
1

这是我为脚本找到的最简单的方法。

<search>是您要查找的字符串,ASSOC_ARRAY是保存关联数组的变量的名称。

取决于您想要实现的目标:

密钥存在

if grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key is present; fi

键不存在

if ! grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key not present; fi

值存在

if grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value is present; fi

值不存在

if ! grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value not present; fi
于 2016-08-15T14:58:36.537 回答
0

bad array subscript当我正在检查的密钥未设置时,我收到错误消息。所以,我写了一个循环遍历键的函数:

#!/usr/bin/env bash
declare -A helpList 

function get_help(){
    target="$1"

    for key in "${!helpList[@]}";do
        if [[ "$key" == "$target" ]];then
            echo "${helpList["$target"]}"
            return;
        fi
    done
}

targetValue="$(get_help command_name)"
if [[ -z "$targetvalue" ]];then
    echo "command_name is not set"
fi

它在找到时回显该值,而在未找到时不回显。我尝试过的所有其他解决方案都给了我这个错误。

于 2021-02-09T17:41:19.697 回答