Il modo più semplice per verificare la presenza di un indice o di una chiave in un array?


92

Utilizzando:

set -o nounset
  1. Avere un array indicizzato come:

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

    Qual è il modo più breve per verificare se l'elemento 1 è impostato?
    A volte utilizzo quanto segue:

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

    Vorrei sapere se ce n'è uno preferito.

  2. Come gestire gli indici non consecutivi?

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

    Come controllare rapidamente che 51è già impostato, ad esempio?

  3. Come gestire gli array associativi?

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

    Come controllare rapidamente che key2è già utilizzato per esempio?

Risposte:


134

Per verificare se l'elemento è impostato (si applica sia all'array indicizzato che associativo)

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

Fondamentalmente quello che ${array[key]+abc}fa è

  • se array[key]è impostato, ritornaabc
  • se array[key]non è impostato, non restituisce nulla


Riferimenti:

  1. Vedi Parameter Expansion nel manuale Bash e la piccola nota

    se i due punti vengono omessi, l'operatore verifica solo l'esistenza [del parametro ]

  2. Questa risposta è in realtà adattata dalle risposte per questa domanda SO: come sapere se una stringa non è definita in uno script di shell bash ?


Una funzione wrapper:

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

Per esempio

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

Ho risolto in questo modo: if test "$ {myArray ['key_or_index'] + isset}"; quindi fare eco "sì"; altro echo "no"; fi; Mi sembra il modo più semplice e si applica agli array indicizzati e associativi. Grazie
Luca Borrione

1
@doubleDown Come usi [$ {array [key] + abc}] in una clausola if per fare qualcosa solo se [$ {array [key] + abc}] non esiste?
olala

1
Inoltre non funziona quando si interroga accidentalmente un array enumerato come associativo.
Tomáš Zato - Ripristina Monica il

1
@duanev: Without +abc, restituirà [ ${array[key]} ]false se l'elemento è effettivamente impostato ma su un valore vuoto, quindi sta effettivamente testando il valore non-vacuità piuttosto che l'esistenza della chiave.
musiphil

Anche @duanev Without +abcha avuto esito negativo quando array[key]non è impostato ed set -uè efficace.
Ding-Yi Chen

36

Da man bash , espressioni condizionali:

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

esempio:

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

Questo mostrerà che sia foo [bar] che foo [baz] sono impostati (anche se quest'ultimo è impostato su un valore vuoto) e foo [quux] non lo è.


1
Mi è mancato a una rapida occhiata; notare che la tipica sintassi di espansione dell'array non viene utilizzata.
Nathan Chappell

Con set -u, perché [[ -v "${foo[bar]}" ]]produce un errore di variabile non associata se barnon esiste nel dizionario? Funziona bene senza ${}; Sono abituato a usarlo per tutto per impostazione predefinita.
bgfvdu3w

"${foo[bar]}"valuta prima la variabile dell'array, quindi il [[ -vcomando verifica una variabile con il nome di quel valore
andysh

12

Nuova risposta

Dalla versione 4.2 di (e più recenti), c'è una nuova -vopzione per il testcomando integrato .

Dalla versione 4.3, questo test potrebbe indirizzare gli elementi degli array.

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

Funziona con gli array associativi allo stesso modo:

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

Con una piccola differenza:
negli array regolari, la variabile tra parentesi ( [i]) è intera, quindi il simbolo del dollaro ( $) non è richiesto, ma per gli array associativi, poiché la chiave è una parola, $è richiesto ( [$i])!

Vecchia risposta per prima della V4.2

Sfortunatamente, bash non dà modo di fare la differenza tra variabile vuota e non definita .

Ma ci sono alcuni modi:

$ 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

(non dare risposta)

E per l'array associativo, puoi usare lo stesso:

$ 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

Potresti fare il lavoro senza la necessità di strumenti esterni (no printf | grep come pura bash ) e, perché no, costruire checkIfExist () come una nuova funzione 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

o anche creare una nuova funzione bash getIfExist che restituisca il valore desiderato ed esca con un codice risultato falso se il valore desiderato non esiste:

$ 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

Ok per i voti negativi: questa risposta è stata pubblicata prima della V4.2 di bash ! Risposta modificata!
F. Hauri

Non funziona bash 4.2.46. Funziona bash 4.4.12.
Irfy

@Irfy Cosa non funziona? -vopzione testo getIfExistfunzione?
F. Hauri

1
-vè stato aggiunto a bash-4.2 MA il supporto per il controllo degli indici di array non è stato aggiunto fino a bash-4.3.
mr.spuratic

1
@ mr.spuratic Grazie, per il commento!
F. Hauri

5

testato in 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

Ciò non riesce quando il valore della chiave è una stringa vuota. Come soluzione alternativa, puoi utilizzare l' +espansione del parametro per sostituire un valore vuoto con un segnaposto come un trattino basso. Ad esempio , declare -A a[x]=;[[ ${a[x]} ]];echo $?stampa 1, ma declare -A a[x]=;[[ ${a[x]+_} ]];echo $?stampa 0.
nisetama

3

Che ne dici di un -ztest e :-dell'operatore?

Ad esempio, questo 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

Stampe:

ABC is set
DEF is set

Ottima soluzione compatta che risponde come previsto per una stringa vuota
Ryan Dugan

1

Questo è il modo più semplice che ho trovato per gli script.

<search> è la stringa che vuoi trovare, ASSOC_ARRAY il nome della variabile che array associativo.

Dipende da ciò che vuoi ottenere:

la chiave esiste :

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

la chiave non esiste :

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

valore esiste :

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

valore non esiste :

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

1

Ho scritto una funzione per verificare se esiste una chiave in un array in 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
}

Esempio

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

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

Testato con GNU bash, versione 4.1.5 (1) -release (i486-pc-linux-gnu)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.