Öğenin bash dizisinde olup olmadığını test edin


17

Bir dizinin bash öğesinde (döngüden daha iyi) bir öğe olup olmadığını kontrol etmenin güzel bir yolu var mı?

Alternatif olarak, bir sayı veya dizenin önceden tanımlanmış sabit kümelerinden herhangi birine eşit olup olmadığını kontrol etmenin başka bir yolu var mı?

Yanıtlar:


24

Bash 4'te ilişkilendirilebilir diziler kullanabilirsiniz:

# set up array of constants
declare -A array
for constant in foo bar baz
do
    array[$constant]=1
done

# test for existence
test1="bar"
test2="xyzzy"

if [[ ${array[$test1]} ]]; then echo "Exists"; fi    # Exists
if [[ ${array[$test2]} ]]; then echo "Exists"; fi    # doesn't

Diziyi başlangıçta ayarlamak için doğrudan atamalar da yapabilirsiniz:

array[foo]=1
array[bar]=1
# etc.

veya bu şekilde:

array=([foo]=1 [bar]=1 [baz]=1)

Aslında, değerin boş olması durumunda [[]] testi çalışmaz. Örneğin, "dizi ['test'] = ''". Bu durumda, 'test' anahtarı vardır ve $ {! Array [@]} ile listelendiğini, ancak "[[$ {array ['test']}]]; echo $?" yankılar 1, 0 değil.
haridsv

1
${array[$test1]}basit ama bir sorunu var: set -u"sınırsız değişken" alacağınız için komut dosyalarınızda (önerilen) kullanırsanız çalışmaz .
tokland

@tokland: Kim tavsiye ediyor? Kesinlikle bilmiyorum.
sonraki duyuruya kadar duraklatıldı.

@DennisWilliamson: Tamam, bazı insanlar bunu tavsiye ediyor, ancak sanırım bu bayrakların değerine bakılmaksızın çalışan bir çözüm bulmanın iyi olacağını düşünüyorum.
tokland


10

Eski bir soru, ama ben basit çözüm henüz görünmüyor ne düşünüyorum: test ${array[key]+_}. Misal:

declare -A xs=([a]=1 [b]="")
test ${xs[a]+_} && echo "a is set"
test ${xs[b]+_} && echo "b is set"
test ${xs[c]+_} && echo "c is set"

Çıktılar:

a is set
b is set

Bu eser kontrol nasıl çalıştığını görmek için bu .


2
Bilgi kılavuzu env, takma adlarda, programlarda ve "test" adını benimsemiş olabilecek diğer işlevlerdeki belirsizlikleri önlemek için kullanmanızı önerir. Yukarıdaki gibi env test ${xs[a]+_} && echo "a is set". Bu işlevi çift parantez kullanarak da alabilirsiniz, aynı hile sonra null olup olmadığını kontrol edebilirsiniz:[[ ! -z "${xs[b]+_}" ]] && echo "b is set"
A.Danischewski

Ayrıca daha basit kullanabilirsiniz[[ ${xs[b]+set} ]]
Arne L.

5

İlişkilendirilebilir bir dizinin bir öğesinin (ayarlanmadı) var olup olmadığını test etmenin bir yolu vardır, bu boştan farklıdır:

isNotSet() {
    if [[ ! ${!1} && ${!1-_} ]]
    then
        return 1
    fi
}

Sonra kullanın:

declare -A assoc
KEY="key"
isNotSet assoc[${KEY}]
if [ $? -ne 0 ]
then
  echo "${KEY} is not set."
fi

sadece bir not: -A beyanı bash 3.2.39 (debian lenny) üzerinde çalışmıyor, ancak bash 4.1.5 (debian squeeze) üzerinde çalışıyor
Paweł Polewicz

İlişkilendirilebilir diziler Bash 4'te tanıtıldı.
Diego F. Durán

1
if ! some_check then return 1= olduğuna dikkat edin some_check. Yani: isNotSet() { [[ ... ]] }. Aşağıdaki çözümümü kontrol edin, basit bir kontrolle yapabilirsiniz.
tokland

3

Dizinin içeriğini grep'e ayırarak bir girişin olup olmadığını görebilirsiniz.

 printf "%s\n" "${mydata[@]}" | grep "^${val}$"

Ayrıca bir eşleşmenin satır numarasını döndüren grep -n ile bir girdinin dizinini de alabilirsiniz (sıfır tabanlı dizin elde etmek için 1 çıkarmayı unutmayın) Bu çok büyük diziler dışında oldukça hızlı olacaktır.

# given the following data
mydata=(a b c "hello world")

for val in a c hello "hello world"
do
           # get line # of 1st matching entry
    ix=$( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 )

    if [[ -z $ix ]]
    then
        echo $val missing
    else
         # subtract 1.  Bash arrays are zero-based, but grep -n returns 1 for 1st line, not 0 
        echo $val found at $(( ix-1 ))
    fi
done

a found at 0
c found at 2
hello missing
hello world found at 3

açıklama:

  • $( ... ) bir komutun bir değişkeni çıktısını yakalamak için ters tırnak kullanmakla aynıdır
  • printf verilerimi satır başına bir öğe çıktılar
  • ( @bunun yerine tüm alıntılar *. "merhaba dünya" nın 2 satıra bölünmesini önler)
  • greptam dize arar: ^ve $satırın başlangıcını ve sonunu eşleştirir
  • grep -n 4 numaralı hat # ile döner: merhaba dünya
  • grep -m 1 sadece ilk eşleşmeyi bulur
  • cut sadece satır numarasını çıkarır
  • döndürülen satır numarasından 1 çıkarın.

Elbette çıkartmayı komuta katlayabilirsiniz. Ama sonra -1 için eksik olup olmadığını test edin:

ix=$(( $( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 ) - 1 ))

if [[ $ix == -1 ]]; then echo missing; else ... fi
  • $(( ... )) tam sayı aritmetiği yapar

1

Dizide çok sınırlı veri olmadığı sürece döngü olmadan düzgün yapabileceğinizi sanmıyorum .

İşte basit bir varyant, bu "Super User"dizide var olduğunu doğru söyleyecektir . Ama aynı zamanda "uper Use"dizide olduğunu da söyleyebilirim .

MyArray=('Super User' 'Stack Overflow' 'Server Fault' 'Jeff' );
FINDME="Super User"

FOUND=`echo ${MyArray[*]} | grep "$FINDME"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

#
# If you where to add anchors < and > to the data it could work
# This would find "Super User" but not "uper Use"
#

MyArray2=('<Super User>' '<Stack Overflow>' '<Server Fault>' '<Jeff>' );

FOUND=`echo ${MyArray2[*]} | grep "<$FINDME>"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

Sorun diziyi döngü yanı sıra (düşünebileceğim) çapa eklemek için kolay bir yolu olmasıdır. Diziye koymadan önce ekleyemezseniz ...


Sabitler alfanümerik olduğunda (ile grep "\b$FINDME\b") güzel bir çözümdür . Muhtemelen boşlukları olmayan alfanümerik olmayan sabitlerle çalışabilir, "(^| )$FINDME(\$| )"(veya bunun gibi bir şey ... regexp
grep'in

1
#!/bin/bash
function in_array {
  ARRAY=$2
  for e in ${ARRAY[*]}
  do
    if [[ "$e" == "$1" ]]
    then
      return 0
    fi
  done
  return 1
}

my_array=(Drupal Wordpress Joomla)
if in_array "Drupal" "${my_array[*]}"
  then
    echo "Found"
  else
    echo "Not found"
fi

1
Bu yaklaşımı neden önerdiğinizi açıklayabilir misiniz? OP , dizi içinde döngü yapmadan bunu yapmanın bir yolu olup olmadığını sordu , yaptığınız şey bu in_array. Şerefe
bertieb

En azından bu döngü, birçok durumda (küçük veri kümeleriyle) yeterince iyi olabilecek ve bash 4+ gerektirmeyen bir işlevde kapsüllenmiştir. Muhtemelen ${ARRAY[@]}kullanılmalıdır.
Tobias
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.