Najłatwiejszy sposób sprawdzenia indeksu lub klucza w tablicy?

89

Za pomocą:

set -o nounset
  1. Posiadanie tablicy indeksowanej, takiej jak:

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

    Jaki jest najkrótszy sposób sprawdzenia, czy element 1 jest ustawiony?
    Czasami używam następujących:

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

    Chciałbym wiedzieć, czy istnieje preferowany.

  2. Jak radzić sobie z indeksami nieciągłymi?

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

    Jak na przykład szybko sprawdzić, czy 51jest już ustawione?

  3. Jak radzić sobie z tablicami asocjacyjnymi?

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

    Jak na przykład szybko sprawdzić, co key2jest już używane?

Luca Borrione
źródło

Odpowiedzi:

130

Aby sprawdzić, czy element jest ustawiony (dotyczy zarówno tablicy indeksowanej, jak i asocjacyjnej)

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

Zasadniczo to, co ${array[key]+abc}jest

  • jeśli array[key]jest ustawiona, zwracaabc
  • jeśli array[key]nie jest ustawiona, nic nie zwraca


Bibliografia:

  1. Zobacz Rozwijanie parametrów w podręczniku Bash i mała uwaga

    jeśli dwukropek zostanie pominięty, operator sprawdza tylko istnienie [ parametru ]

  2. Ta odpowiedź jest faktycznie zaadaptowana z odpowiedzi na to pytanie SO: Jak sprawdzić, czy ciąg nie jest zdefiniowany w skrypcie powłoki bash ?


Funkcja opakowania:

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

Na przykład

if ! exists key in array; then echo "No such array element"; fi 
doubleDown
źródło
Rozwiązałem w ten sposób: if test "$ {myArray ['key_or_index'] + isset}"; następnie powtórz „tak”; w przeciwnym razie echo "nie"; fi; Wydaje mi się, że jest to najprostszy sposób i dotyczy tablic indeksowanych i asocjacyjnych. Dziękuję
Luca Borrione
1
@doubleDown Jak użyć [$ {array [key] + abc}] w klauzuli if, aby zrobić coś tylko wtedy, gdy [$ {array [key] + abc}] nie istnieje?
olala
1
Nie działa również, gdy przypadkowo wykonasz zapytanie o tablicę wyliczoną jako asocjacyjną.
Tomáš Zato - Przywróć Monikę
1
@duanev: Without +abc, [ ${array[key]} ]oceni jako false, jeśli element jest rzeczywiście ustawiony, ale ma wartość pustą, więc w rzeczywistości testuje wartość nie-pustkę, a nie istnienie klucza.
musiphil
@duanev Without +abcrównież nie powiodło się, gdy array[key]nie jest ustawione i set -ujest skuteczne.
Ding-Yi Chen
35

Od man bash , wyrażenia warunkowe:

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

przykład:

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

To pokaże, że zarówno foo [bar], jak i foo [baz] są ustawione (nawet jeśli ta ostatnia ma wartość pustą), a foo [quux] nie.

Vineet
źródło
1
Przeoczyłem to na pierwszy rzut oka; zwróć uwagę, że nie jest używana typowa składnia rozszerzania tablicy.
Nathan Chappell
Z set -u, dlaczego [[ -v "${foo[bar]}" ]]generuje niepowiązany błąd zmiennej, jeśli barnie istnieje w słowniku? Działa dobrze bez ${}; Po prostu używam go domyślnie do wszystkiego.
bgfvdu3w
"${foo[bar]}"najpierw ocenia zmienną tablicową, więc [[ -vpolecenie sprawdza zmienną o nazwie tej wartości
andysh
10

Nowa odpowiedź

Od wersji 4.2 (i nowsze), dostępna jest nowa -vopcja wbudowanego testpolecenia.

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

To działa z tablicami asocjacyjnymi w ten sam sposób:

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

Z małą różnicą: w
zwykłych tablicach zmienna w nawiasach kwadratowych ( [i]) jest liczbą całkowitą, więc symbol dolara ( $) nie jest wymagany, ale w przypadku tablicy asocjacyjnej, ponieważ klucz jest słowem, $jest wymagany ( [$i])!

Stara odpowiedź na przed V4.2

Niestety, bash nie daje żadnej możliwości zrobienia różnicy między pustą a niezdefiniowaną zmienną.

Ale jest kilka sposobów:

$ 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

(nie dawaj odpowiedzi)

A dla tablicy asocjacyjnej możesz użyć tego samego:

$ 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

Możesz wykonać to zadanie bez konieczności używania zewnętrznych narzędzi (bez printf | grep jako czystego bash ) i dlaczego nie, zbuduj checkIfExist () jako nową funkcję basha:

$ 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

lub nawet utwórz nową funkcję bash getIfExist, która zwraca żądaną wartość i kończy pracę z fałszywym kodem wyniku, jeśli żądana wartość nie istnieje:

$ 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
źródło
Ok za głosy przeciwne: ta odpowiedź została opublikowana przed wersją 4.2 programu bash ! Odpowiedź edytowana!
F. Hauri
Nie działa dalej bash 4.2.46. Działa dalej bash 4.4.12.
Irfy
@Irfy What does'nt work? -vopcja testczy getIfExistfunkcja?
F. Hauri
-vNie działa na tablicach na moich CentOS 7.7.1908 z bash 4.2.46. Kod z pierwszego bloku kodu jest drukowany not existwe wszystkich przypadkach pod tym bashem. (Próbowano też [$i]zamiast [i], bez różnicy)
Irfy
5

testowane w 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
gdoubleod
źródło
To kończy się niepowodzeniem, gdy wartość klucza jest pustym ciągiem. Aby obejść ten problem, można użyć +rozwinięcia parametrów, aby zastąpić pustą wartość jakimś symbolem zastępczym, takim jak podkreślenie. Na przykład declare -A a[x]=;[[ ${a[x]} ]];echo $?wydruki 1, ale declare -A a[x]=;[[ ${a[x]+_} ]];echo $?wydruki 0.
nisetama
3

A co z -ztestem i :-operatorem?

Na przykład ten skrypt:

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

Wydruki:

ABC is set
DEF is set
GuyPaddock
źródło
Świetne kompaktowe rozwiązanie, które reaguje zgodnie z oczekiwaniami dla pustej struny
Ryan Dugan
1

To najłatwiejszy sposób, jaki znalazłem dla skryptów.

<search>jest ciągiem, który chcesz znaleźć, ASSOC_ARRAYnazwą zmiennej przechowującej twoją tablicę asocjacyjną.

W zależności od tego, co chcesz osiągnąć:

klucz istnieje :

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

klucz nie istnieje :

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

wartość istnieje :

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

wartość nie istnieje :

if ! grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value not present; fi
sjas
źródło
1

Napisałem funkcję, aby sprawdzić, czy klucz istnieje w tablicy w 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
}

Przykład

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

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

Testowany z GNU bash, wersja 4.1.5 (1) -release (i486-pc-linux-gnu)

Lucas Stad
źródło