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

时间:2012-11-04 14:57:04

标签: arrays bash indexing key

使用:

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'是否已被使用?

感谢

EDITED
最简单的方法在我看来:

if test "${myArray['key_or_index']+isset}"
    then
        echo "yes"
    else
        echo "no"
fi;

这适用于索引和关联数组。 set -o nounset没有显示错误。
感谢正面的doubleDown。

9 个答案:

答案 0 :(得分:99)

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

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

基本上${array[key]+abc}的作用是

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

<小时/> 参考文献:

  1. 请参阅Bash手册中的Parameter Expansion和小注

      

    如果省略冒号,则运算符仅测试是否存在[参数]

  2. 这个答案实际上是根据这个SO问题的答案改编的:How to tell if a string is not defined in a bash shell script


  3. 包装函数:

    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 
    

答案 1 :(得分:17)

来自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]则不是。

答案 2 :(得分:6)

不幸的是,bash没有办法在 empty undefined 变量之间产生差异。

但有一些方法:

$ 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 as 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函数,该函数返回所需的值,如果不存在所需的值,则使用false结果代码退出:

$ 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

答案 3 :(得分:5)

在bash 4.3.39(1)-exlease

中测试
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

答案 4 :(得分: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)

答案 5 :(得分:1)

从 Thamme 重申这一点:

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

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

答案 6 :(得分:0)

一个-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

答案 7 :(得分: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

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

答案 8 :(得分:0)

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

有一个“干净的代码”很长的路要走,还有一种更短、更简洁、以 bash 为中心的方式。下面的功能是两者之间的折衷。请注意,这些函数的目标只是返回一个布尔结果。然而,其他排列/形式也是可能的。关联数组/映射的解决方案必然不同于处理索引数组的解决方案。

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

$@ = 移动一次后传入的数组/映射。

function hasArrayKey ()
{
    local -r needle="${1:?}" # Ensures a needle cannot be the empty / null string

    shift 1 # So that the remaining params can be made into an array

    local -ar haystack=("$@")

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

    return 1
}

function hasMapKey ()
{
    local -r needle="${1:?}"

    shift 1

    local -Ar haystack=("$@")

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

    return 1
}

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

现在,对于那些喜欢“不,我想要性能更高的版本,因为我可能不得不在 bash 中处理大型数组”的纯粹主义者,让我们看看一个更以 bash 为中心的解决方案,但是一个维护干净代码的解决方案以及处理数组或映射的灵活性。

function hasArrayKey ()
{
    local -r needle="${1:?}"

    shift 1

    local -ar haystack=("$@")

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

function hasMapKey ()
{
    local -r needle="${1:?}"

    shift 1

    local -Ar haystack=("$@")

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

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

用法

local -A person=(firstname Anthony lastname Rutledge)

if hasMapKey "firstname" "${person[@]}"; then
     # Do something
fi

local -a person=(Anthony Rutledge)

if hasArrayKey "0" "${person[@]}"; then
     # Do something
fi
<块引用>

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

${parameter:-word}

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

${parameter:=word}

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

不能以这种方式分配参数和特殊参数。 ${parameter:?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

error 和 shell,如果它不是交互式的,则退出。否则, 参数的值被替换。 ${parameter:+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 测试成功,否则失败。