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

Posted

技术标签:

【中文标题】检查数组中的索引或键的最简单方法是啥?【英文标题】:Easiest way to check for an index or a key in an array?检查数组中的索引或键的最简单方法是什么? 【发布时间】:2012-10-24 13:15:41 【问题描述】:

使用:

set -o nounset

    有一个像这样的索引数组:

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

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

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

    我想知道是否有首选。

    如何处理不连续的索引?

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

    例如,如何快速检查51 是否已设置?

    如何处理关联数组?

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

    例如,如何快速检查key2 是否已被使用?

【问题讨论】:

【参考方案1】:

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

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

基本上$array[key]+abc 所做的是

如果设置了array[key],则返回abc 如果没有设置array[key],则不返回任何内容


参考:

    参见 Bash 手册中的 Parameter Expansion 和小注释

    如果省略冒号,则运算符仅测试 [of parameter]

    这个答案实际上改编自这个 SO 问题的答案:How to tell if a string is not defined in a bash shell script?


包装函数:

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 

【讨论】:

我是这样解决的:if test "$myArray['key_or_index']+isset";然后回显“是”;否则回显“否”;菲;在我看来,这是最简单的方法,它适用于索引数组和关联数组。谢谢 @doubleDown 如何在 if 子句中使用 [ $array[key]+abc ] 以仅在 [ $array[key]+abc ] 不存在的情况下执行某些操作? 当您不小心将枚举数组查询为关联数组时也不起作用。 @duanev:如果没有+abc[ $array[key] ] 将在元素确实设置但为空值时评估为 false,因此它实际上是在测试值非空而不是键是否存在。 eval 是邪恶的!!试试这个:exists foo in 'O;cat /etc/passwd;echo -e \\e[5m' 示例!!【参考方案2】:

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

【讨论】:

看了一眼就错过了;注意没有使用典型的数组扩展语法。 对于set -u,如果字典中不存在bar,为什么[[ -v "$foo[bar]" ]] 会产生未绑定变量错误?没有$ 也能正常工作;我只是习惯于默认使用它。 "$foo[bar]" 首先评估数组变量,因此[[ -v 命令测试具有该值名称的变量 key 值的存在与否不是这里的问题。确定密钥是否存在就足够了。这实际上是一个错误的答案,因为-v 仅返回“true if 已设置变量名(已分配值”。这超出了此处的要求。【参考方案3】:

新答案

从bash(及更高版本)的 4.2 版开始,内置了一个新的 -v 选项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's comment,我在-v 测试中引用了'array[i]',以满足shellcheck 的错误 SC2208。这似乎在这里并不真正需要,因为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])!

bash 在 V4.2 之前的旧答案

不幸的是,bash 无法区分 emptyundefined 变量。

但是有一些方法:

$ 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 作为 pure 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 之前发布的!答案已编辑! -v 已添加到 bash-4.2 BUT 直到 bash-4.3 才添加对检查数组索引的支持。 @mr.spuratic 感谢您的评论! 整个页面都证明了 bash 的巨大失败。对于最基本的东西,它充满了 20 种违反直觉的方法,并且都带有诸如“(不)对我/这个或那个版本工作”之类的 cmets。 谢谢,在 macOS / brew bash 5.1.8 上非常适合我 :-) shellcheck 报告 SC2208 两个 新答案 代码示例:显然,@ 987654346@ 应该使用[[ ... ]] 而不是[ ... ] 或者-v 之后的表达式应该被引用,例如if [[ -v aArray[$i] ]]if [ -v 'aArray[$i]' ]。打败我,我通常只是按照shellcheck 告诉我的...【参考方案4】:

-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

【讨论】:

非常紧凑的解决方案,可以按预期响应空字符串 对这个与 bash 4.2 中的 set -u 一起使用的解决方案投了极大的赞成票。现在,我在 Red Hat 7 上使用 Oracle 数据库,并在那里安装了 bash 4.2。 这应该是公认的答案!为我工作(bash 4.2.46),而接受的 -v 答案没有。 @GuyPaddock 你确定在你的数组变量中包含“:”会给你想要的响应吗? Null 或空字符串值是数组元素的有效值,我们不想测试它们。我们只想测试索引或键是否存在。 “如果包含冒号,则运算符测试两个参数是否存在并且其值不为空”gnu.org/savannah-checkouts/gnu/bash/manual/… @AnthonyRutledge 就我而言,我也不想要空值。【参考方案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【参考方案6】:

从 Thamme 重申这一点:

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

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

【讨论】:

【参考方案7】:

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

<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

【讨论】:

【参考方案8】:

我写了一个函数来检查一个键是否存在于 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)

【讨论】:

【参考方案9】:

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

有一种“干净的代码”很长的路要走,还有一种更短、更简洁、以 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 ] 行使用 bash 变量扩展的 $parameter+word 形式,而不是 $parameter:+word 形式,它也尝试测试键的值,这不是手头的问题 em>。

用法

local -A person=(firstname Anthony lastname Rutledge)

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

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

$参数:-word

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

错误和shell,如果它不是交互式的,则退出。否则,该 参数的值被替换。 $参数:+word

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

【讨论】:

【参考方案10】:

当我检查的密钥未设置时,我收到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

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

【讨论】:

以上是关于检查数组中的索引或键的最简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在Javascript中更新对象数组中的键的最有效方法是啥? [复制]

索引(用于搜索索引)70,000 个节点的最简单方法是啥?

将hdf5文件中的uint32数据写入java中的数组的最简单方法是啥?

Meteor 模板:检查用户是不是登录的最简单方法是啥?

将数组转换为向量的最简单方法是啥?

将数组转换为向量的最简单方法是啥?