Bir dizideki bir dizini veya anahtarı kontrol etmenin en kolay yolu?


92

Kullanarak:

set -o nounset
  1. Aşağıdaki gibi dizinlenmiş bir diziye sahip olmak:

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

    1. öğenin ayarlanmış olup olmadığını kontrol etmenin en kısa yolu nedir?
    Bazen aşağıdakileri kullanıyorum:

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

    Tercih edilen bir tane olup olmadığını bilmek isterim.

  2. Ardışık olmayan dizinler ile nasıl başa çıkılır?

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

    51Örneğin, önceden ayarlanmış hızlı kontrol nasıl yapılır ?

  3. İlişkili dizilerle nasıl başa çıkılır?

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

    key2Örneğin halihazırda kullanılan hızlı kontrol nasıl yapılır ?

Yanıtlar:


135

Öğenin ayarlanıp ayarlanmadığını kontrol etmek için (hem dizinlenmiş hem de ilişkilendirilebilir dizi için geçerlidir)

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

Temelde ne ${array[key]+abc}yapar

  • eğer array[key]ayarlanırsa, dönüşabc
  • eğer array[key]ayarlı değil, hiçbir şey dönmek


Referanslar:

  1. Bash kılavuzundaki Parametre Genişletme bölümüne ve küçük nota bakın

    iki nokta üst üste belirtilmezse, operatör yalnızca [ parametrenin ] varlığını test eder

  2. Bu cevap aslında şu SO sorusunun cevaplarından uyarlanmıştır: Bir dizgenin bir bash kabuğunda tanımlı olup olmadığı nasıl anlaşılır ?


Bir sarmalayıcı işlevi:

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

Örneğin

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

Bu şekilde çözdüm: "$ {myArray ['key_or_index'] + isset}" test ederseniz; sonra echo "evet"; aksi takdirde yankı "hayır"; fi; Bana en basit yol gibi görünüyor ve indekslenmiş ve ilişkilendirilebilir diziler için geçerli. Teşekkür ederim
Luca Borrione

1
@doubleDown [$ {array [key] + abc}] if cümlesinde, yalnızca [$ {array [key] + abc}] yoksa bir şeyler yapmak için nasıl kullanılır?
olala

1
Ayrıca numaralandırılmış diziyi yanlışlıkla ilişkilendirilmiş bir dizi olarak sorguladığınızda da çalışmaz.
Tomáš ZATO - Eski Monica

1
@duanev: Olmadan +abc, [ ${array[key]} ]öğe gerçekten ayarlanmışsa, ancak boş bir değere ayarlanmışsa yanlış olarak değerlendirilecektir, bu nedenle aslında anahtar varoluştan ziyade boş olmayan değeri test etmektedir.
musiphil

@duanev Belirlenmediğinde +abcde başarısız olur array[key]ve set -uetkilidir.
Ding-Yi Chen

36

Gönderen adam Bash , koşullu ifadeler:

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

misal:

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

Bu, hem foo [bar] hem de foo [baz] 'ın ayarlandığını (ikincisi boş bir değere ayarlanmış olsa bile) ve foo [quux]' un olmadığını gösterir.


1
Hızlı bir bakışta kaçırdım; tipik dizi genişletme sözdiziminin kullanılmadığına dikkat edin.
Nathan Chappell

İle set -u, sözlükte yoksa neden [[ -v "${foo[bar]}" ]]bir ilişkisiz değişken hatası üretiyor bar? Olmadan iyi çalışır ${}; Varsayılan olarak her şey için kullanmaya alışkınım.
bgfvdu3w

"${foo[bar]}"önce dizi değişkenini değerlendirir, bu nedenle [[ -vkomut o değere sahip bir değişkeni test eder
andysh

12

Yeni cevap

4.2 sürümünden itibaren (ve daha yenisi), -vyerleşik testkomut için yeni bir seçenek var .

4.3 sürümünden itibaren, bu test dizilerin öğelerini ele alabilir.

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

Bu, ilişkilendirilebilir dizilerle aynı şekilde çalışır:

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

Küçük bir farkla:
Normal dizilerde, parantezler ( [i]) arasındaki değişken tamsayıdır, bu nedenle dolar sembolü ( $) gerekli değildir, ancak anahtar bir kelime olduğu için ilişkilendirilebilir dizi $için gereklidir ( [$i])!

İçin eski cevap V4.2 öncesi

Ne yazık ki, bash boş ve tanımsız değişkenler arasında fark yaratmanın bir yolunu vermez .

Ancak bazı yollar var:

$ 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

(cevap vermeyin)

İlişkilendirilebilir dizi için de aynısını kullanabilirsiniz:

$ 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

İşi harici araçlara ihtiyaç duymadan yapabilirsiniz ( saf bash olarak printf | grep yok ) ve neden olmasın, checkIfExist () ' i yeni bir bash işlevi olarak oluşturun:

$ 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

hatta istenen değeri döndüren ve istenen değer yoksa yanlış sonuç koduyla çıkan yeni bir getIfExist bash işlevi oluşturun:

$ 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

Olumsuz oylar için uygundur: Bu cevap, Bash ! Cevap düzenlendi!
F.Hauri

Üzerinde çalışmıyor bash 4.2.46. Üzerinde çalışıyor bash 4.4.12.
Irfy

@Irfy Ne işe yaramaz? -vseçeneği testveya getIfExistişlevi?
F.Hauri

1
-vbash-4.2'ye eklendi ancak dizi indekslerini kontrol etmek için destek, bash-4.3'e kadar eklenmedi.
mr.spuratic

1
@ mr.spuratic Yorumlarınız için teşekkürler!
F. Hauri

5

bash 4.3.39 (1) yayınında test edildi

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

Anahtarın değeri boş bir dizge olduğunda bu başarısız olur. Geçici bir çözüm olarak +, boş bir değeri alt çizgi gibi bir yer tutucuyla değiştirmek için parametre genişletmeyi kullanabilirsiniz . Örneğin declare -A a[x]=;[[ ${a[x]} ]];echo $?yazdırır 1, ancak declare -A a[x]=;[[ ${a[x]+_} ]];echo $?yazdırır 0.
nisetama

3

Bir -ztest ve :-operatör ne olacak?

Örneğin, bu komut dosyası:

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

Baskılar:

ABC is set
DEF is set

Boş bir dizide beklendiği gibi yanıt veren harika kompakt çözüm
Ryan Dugan

1

Komut dosyaları için bulduğum en kolay yol bu.

<search> bulmak istediğiniz dize ASSOC_ARRAY , ilişkisel dizinizi tutan değişkenin adıdır.

Neye ulaşmak istediğinize bağlı olarak:

anahtar var :

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

anahtar mevcut değil :

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

değer var :

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

değer yoktur :

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

1

Bash'de bir dizide bir anahtar olup olmadığını kontrol etmek için bir işlev yazdım:

# 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
}

Misal

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, sürüm 4.1.5 (1) -release (i486-pc-linux-gnu) ile test edilmiştir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.