Le moyen le plus simple de rechercher un index ou une clé dans un tableau?

89

En utilisant:

set -o nounset
  1. Avoir un tableau indexé comme:

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

    Quel est le moyen le plus court pour vérifier si l'élément 1 est défini?
    J'utilise parfois ce qui suit:

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

    Je voudrais savoir s'il y en a un préféré.

  2. Comment gérer les index non consécutifs?

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

    Comment vérifier rapidement ce qui 51est déjà défini par exemple?

  3. Comment gérer les tableaux associatifs?

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

    Comment vérifier rapidement qui key2est déjà utilisé par exemple?

Luca Borrione
la source

Réponses:

130

Pour vérifier si l'élément est défini (s'applique à la fois au tableau indexé et associatif)

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

Fondamentalement, qu'est ${array[key]+abc}- ce que c'est

  • si array[key]est défini, retourneabc
  • si array[key] n'est pas défini, ne renvoie rien


Références:

  1. Voir l' extension des paramètres dans le manuel de Bash et la petite note

    si le deux-points est omis, l'opérateur ne teste que l'existence [du paramètre ]

  2. Cette réponse est en fait adaptée des réponses à cette question SO: Comment savoir si une chaîne n'est pas définie dans un script shell bash ?


Une fonction wrapper:

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

Par exemple

if ! exists key in array; then echo "No such array element"; fi 
doubleDown
la source
J'ai résolu de cette manière: if test "$ {myArray ['key_or_index'] + isset}"; puis echo "oui"; else echo "non"; Fi; Cela me semble le moyen le plus simple et il s'applique aux tableaux indexés et associatifs. Merci
Luca Borrione
1
@doubleDown Comment utiliser [$ {array [key] + abc}] dans une clause if pour ne faire quelque chose que si [$ {array [key] + abc}] n'existe pas?
olala
1
Ne fonctionne pas non plus lorsque vous interrogez accidentellement un tableau énuméré en tant que tableau associatif.
Tomáš Zato - Réintégrer Monica
1
@duanev: Sans +abc, [ ${array[key]} ]sera évalué à faux si l'élément est bien défini mais à une valeur vide, donc il teste en fait la valeur non vide plutôt que l'existence de la clé.
musiphil
@duanev Without a +abcégalement échoué lorsque array[key]n'est pas défini et set -uest effectif.
Ding-Yi Chen
35

De man bash , expressions conditionnelles:

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

exemple:

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

Cela montrera que foo [bar] et foo [baz] sont positionnés (même si ce dernier a une valeur vide) et foo [quux] ne l'est pas.

Vineet
la source
1
Je l'ai raté d'un coup d'œil; notez que la syntaxe d'extension de baie typique n'est pas utilisée.
Nathan Chappell
Avec set -u, pourquoi [[ -v "${foo[bar]}" ]]produit-il une erreur de variable non liée si elle barn'existe pas dans le dictionnaire? Fonctionne bien sans le ${}; Je suis juste habitué à l'utiliser pour tout par défaut.
bgfvdu3w
"${foo[bar]}"évalue d'abord la variable de tableau, donc la [[ -vcommande teste une variable avec le nom de cette valeur
andysh
10

Nouvelle réponse

À partir de la version 4.2 de (et plus récent), il existe une nouvelle -voption pour la testcommande intégrée .

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

Cela fonctionne avec les tableaux associatifs de la même manière:

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

Avec une petite différence:
dans les tableaux réguliers, la variable entre crochets ( [i]) est un entier, donc le symbole dollar ( $) n'est pas requis, mais pour les tableaux associatifs, comme la clé est un mot, $est requis ( [$i])!

Ancienne réponse pour avant V4.2

Malheureusement, bash ne laisse aucun moyen de faire la différence entre variable vide et une variable indéfinie .

Mais il y a quelques moyens:

$ 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

(ne donne pas de réponse)

Et pour le tableau associatif, vous pouvez utiliser le même:

$ 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

Vous pouvez faire le travail sans avoir besoin d'outils externes (pas de printf | grep comme pure bash ), et pourquoi pas, construire checkIfExist () comme une nouvelle fonction 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

ou même créer une nouvelle fonction bash getIfExist qui renvoie la valeur souhaitée et quitte avec un code de résultat faux si la valeur souhaitée n'existe pas:

$ 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
F. Hauri
la source
Ok pour les votes négatifs: Cette réponse a été publiée avant la V4.2 de bash ! Réponse modifiée!
F.Hauri
Ça ne marche pas bash 4.2.46. Fonctionne dessus bash 4.4.12.
Irfy
@Irfy Qu'est-ce qui ne fonctionne pas? -voption testou getIfExistfonction?
F.Hauri le
Le -vne fonctionne pas sur les tableaux sur mon CentOS 7.7.1908 avec bash 4.2.46. Le code de votre premier bloc de code s'imprime not existdans tous les cas sous ce bash. (Également essayé à la [$i]place de [i], aucune différence)
Irfy
5

testé dans la version 4.3.39 (1) de bash

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
gdoubleod
la source
Cela échoue lorsque la valeur de la clé est une chaîne vide. Pour contourner ce problème, vous pouvez utiliser l' +expansion des paramètres pour remplacer une valeur vide par un espace réservé comme un trait de soulignement. Par exemple declare -A a[x]=;[[ ${a[x]} ]];echo $?imprime 1, mais declare -A a[x]=;[[ ${a[x]+_} ]];echo $?imprime 0.
nisetama
3

Qu'en est-il d'un -ztest et du:- opérateur?

Par exemple, ce script:

#!/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

Impressions:

ABC is set
DEF is set
GuyPaddock
la source
Excellente solution compacte qui répond comme prévu pour une chaîne vide
Ryan Dugan
1

C'est le moyen le plus simple que j'ai trouvé pour les scripts.

<search>est la chaîne que vous voulez trouver, ASSOC_ARRAYle nom de la variable contenant votre tableau associatif.

Dépendez de ce que vous voulez réaliser:

la clé existe :

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

la clé n'existe pas :

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

la valeur existe :

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

la valeur n'existe pas :

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

J'ai écrit une fonction pour vérifier si une clé existe dans un tableau dans 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
}

Exemple

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

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

Testé avec GNU bash, version 4.1.5 (1) -release (i486-pc-linux-gnu)

Lucas Stad
la source