检查数组中的索引或键的最简单方法?


89

使用:

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已使用的?

Answers:


130

检查是否设置了元素(适用于索引数组和关联数组)

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

基本上,${array[key]+abc}做的是

  • 如果array[key]设置,返回abc
  • 如果array[key]未设置,则不返回任何内容


参考文献:

  1. 请参阅Bash手册中的参数扩展和小注释

    如果省略了冒号,则运算符仅测试[ parameter ]的存在

  2. 这个答案实际上是根据以下SO问题的答案改编的:如何判断bash shell脚本中是否未定义字符串


包装函数:

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

例如

if ! exists key in array; then echo "No such array element"; fi 

我是这样解决的:如果测试“ $ {myArray ['key_or_index'] + isset}”; 然后回声“是”;否则回声“否”;fi; 在我看来,这是最简单的方法,它适用于索引数组和关联数组。谢谢
Luca Borrione 2012年

1
@doubleDown如何在if子句中使用[$ {array [key] + abc}]仅在[$ {array [key] + abc}]不存在的情况下执行某些操作?
olala 2014年

1
当您不小心将枚举数组作为关联数组查询时,此方法也不起作用。
托马什Zato -恢复莫妮卡

1
@duanev:如果元素确实设置为空值,则不带+abc[ ${array[key]} ]它将评估为false,因此它实际上是在测试值的非空性而不是键的存在。
musiphil

@duanev Without+abcarray[key]未设置且set -u有效时也失败。
鼎陈义

35

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]未设置。


1
我一眼就错过了。请注意,没有使用典型的数组扩展语法。
内森·查佩尔

使用set -u[[ -v "${foo[bar]}" ]]如果bar字典中不存在,为什么会产生未绑定变量错误?没有可以正常工作${}; 我只是习惯于默认情况下将其用于所有内容。
bgfvdu3w

"${foo[bar]}"首先评估数组变量,因此该[[ -v命令测试具有该值名称的变量
andysh

10

新答案

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

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

可以通过以下方式使用关联数组

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])之间的变量是整数,因此$不需要美元符号(),但是对于关联数组,因为是一个单词,因此$是必需的([$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

可以接受投票:这个答案发布在bash V4.2之前!答案已编辑!
F. Hauri

不适用于bash 4.2.46。确实适用bash 4.4.12
Irfy

@Irfy什么不起作用?-v选项testgetIfExist功能?
F. Hauri

-v不会在阵列上我的CentOS 1908年7月7日在bash 4.2.46工作。not exist在所有情况下,第一个代码块中的代码都会以该bash打印。(也尝试[$i]替代[i],没有区别)
Irfy,

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

当键的值为空字符串时,该操作将失败。解决方法是,您可以使用+参数扩展功能将空值替换为下划线之类的占位符。例如declare -A a[x]=;[[ ${a[x]} ]];echo $?打印1,但是declare -A a[x]=;[[ ${a[x]+_} ]];echo $?打印0
nisetama 2015年

3

-z测试和:-操作员呢?

例如,此脚本:

#!/usr/bin/env bash

set -e
set -u

declare -A sample

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

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

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

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

印刷品:

ABC is set
DEF is set

出色的紧凑型解决方案,可对空字符串做出预期的响应
Ryan Dugan

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

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)-发行版(i486-pc-linux-gnu)测试

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.