Bash'te dizinin boş olup olmadığını kontrol edin


110

Komut dosyam çalıştığı sırada farklı hata mesajlarıyla dolu bir dizilim var.

Senaryonun sonunda boş olmadığını kontrol etmek için bir yola ihtiyacım var ve eğer varsa belirli bir işlemi yapmalıyım.

Zaten normal bir VAR gibi davranmayı ve kontrol etmek için -z kullanarak denedim, ama bu işe yaramadı. Bir dizinin Bash'de boş olup olmadığını kontrol etmenin bir yolu var mı?

Yanıtlar:


143

Diyelim ki diziniz, $errorsöğelerin sayısının sıfır olup olmadığını kontrol edin.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

10
Lütfen bunun =bir string operatör olduğunu unutmayın . Bu durumda iyi çalışır, ancak -eqbunun yerine uygun aritmetik operatörünü kullanırdım (sadece -geveya -ltvb. Arasında geçiş yapmak istersem ).
musiphil

6
Çalışmaz set -u: "sınırsız değişken" - dizi boşsa.
Igor

@ Igor: Benim için Bash 4.4'te çalışıyor. set -u; foo=(); [ ${#foo[@]} -eq 0 ] && echo empty. Ben unset foo, sonra yazdırır foo: unbound variable, ancak bu farklıdır: dizi değişkeni, var olmaktan ve boş olmaktan ibaret değildir.
Peter Cordes

Ayrıca, kullanım sırasında Bash 3.2 (OSX) 'de test edilmiştir - set -uilk önce değişkeninizi bildirdiğiniz sürece, bu mükemmel bir şekilde çalışır.
zeroimpl

15

Genelde bu durumda aritmetik genişleme kullanıyorum:

if (( ${#a[@]} )); then
    echo not empty
fi

Güzel ve temiz! Bunu sevdim. Ayrıca, dizinin ilk elemanı daima boş değilse, (( ${#a} ))(ilk elemanın uzunluğu) da çalışacağını unutmayın. Ancak, bu başarısız olur a=(''), oyunda (( ${#a[@]} ))verilen cevap başarılı olacaktır.
cxw

8

Diziyi basit bir değişken olarak da düşünebilirsiniz. Bu şekilde, sadece kullanarak

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

veya diğer tarafı kullanarak

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

Bu çözüm ile sorun bir dizi böyle beyan edilmiş olması halinde olmasıdır: array=('' foo). Bu kontroller diziyi boş olarak bildirir, açık olmasa da. (teşekkürler @musiphil!)

Kullanmak [ -z "$array[@]" ]da açıkça bir çözüm değildir. Kıvrımlı parantez belirtilmemesi $arraybir dize olarak yorumlanmaya çalışır ( [@]bu durumda basit bir değişmez dizedir) ve bu nedenle her zaman yanlış olarak bildirilir: "değişmez dize [@]boş mu?" Açıkça değil.


7
[ -z "$array" ]ya da [ -n "$array" ]çalışmıyor. Deneyin array=('' foo); [ -z "$array" ] && echo emptyve açıkça boş emptyolmasa bile yazdıracak array.
musiphil

2
[[ -n "${array[*]}" ]]tüm diziyi sıfır olmayan uzunluk için kontrol ettiğiniz bir dize olarak enterpolasyonlar. array=("" "")İki boş öğeye sahip olmak yerine boş olduğunu düşünüyorsanız , bu yararlı olabilir.
Peter Cordes

@PeterCordes Bunun işe yaradığını sanmıyorum. İfade tek bir boşluk karakterine göre değerlendirilir ve [[ -n " " ]]üzücü olan "true" ifadesidir . Yorumunuz tam olarak yapmak istediğim şey.
Michael,

@Michael: Crap, haklısın. Yalnızca 2 öğenin değil, boş bir dizenin 1 öğeli dizisiyle çalışır. Yaşlı bash'i bile kontrol ettim ve hala orada yanlış; Söylediğin gibi set -xnasıl genişlediğini gösteriyor. Sanırım göndermeden önce bu yorumu test etmedim. >. <Ayarlayarak çalışmasını sağlayabilirsiniz IFS=''(bu ifadeye kaydedin / geri yükleyin), çünkü "${array[*]}"genişletme öğeleri IFS'in ilk karakteriyle ayırır. (Veya ayarlanmamışsa boşluk). Ancak " IFS boş ise, parametreler ayırıcılara müdahale etmeden birleştirilir. " ($ * Konumsal paramlar için dokümanlar, ancak diziler için de aynı varsayılır).
Peter Cordes

@ Michael: Bunu yapan bir cevap eklendi.
Peter Cordes

3

Şunu kontrol ettim bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

ve bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

İkinci durumda, aşağıdaki yapıya ihtiyacınız vardır:

${array[@]:+${array[@]}}

boş veya ayarlanmamış dizide başarısız olmamak için. set -euGenelde benim yaptığım gibi yaparsan. Bu daha katı hata kontrolü sağlar. Gönderen docs :

-e

Tek bir basit komuttan (bkz. Basit Komutlar), bir listeden (bkz. Listeler) veya bir bileşik komuttan (bkz. Bileşik Komutlar) sıfırdan farklı bir durumdan oluşan bir boru hattı (bkz. Boru Hatları). Başarısız olan komut, bir süre sonra veya anahtar kelimeye kadar hemen ardından komut listesinin bir parçasıysa, bir if ifadesinde testin bir parçası, & & veya || final && veya || komutunu takip eden komut hariç, bir boru hattındaki herhangi bir komuttan başka bir komut yoksa veya komutun geri dönüş durumu! Alt kabuk dışındaki bir bileşik komut sıfır olmayan bir durum verirse -e ihmal edilirken bir komut başarısız olursa, kabuk çıkmaz. Kabuk çıkmadan önce, eğer ayarlanmışsa, ERR'deki bir tuzak yürütülür.

Bu seçenek kabuk ortamı ve her alt kabuk ortamı için ayrı ayrı uygulanır (bkz. Komut Yürütme Ortamı) ve alt kabuktaki tüm komutları çalıştırmadan önce alt kabukların çıkmasına neden olabilir.

Bir bileşik komut veya kabuk işlevi, -e'nin yoksayıldığı bir bağlamda yürütülürse, bileşik komut veya işlev gövdesinde yürütülen komutların hiçbiri, -e ayarlanmış olsa bile bir komut -e ayarından etkilenmez. başarısızlık durumu. Bir bileşik komut veya kabuk işlevi, -e'nin yoksayıldığı bir bağlamda yürütülürken -e ayarlarsa, bu ayar, bileşik komut veya işlev çağrısını içeren komut tamamlanıncaya kadar etkili olmaz.

-u

Parametre genişletme yaparken, ayarlanmamış değişkenleri ve '@' veya '*' özel parametreleri dışındaki parametreleri hata olarak kabul edin. Standart hataya bir hata mesajı yazılacak ve etkileşimli olmayan bir kabuk çıkacaktır.

Buna ihtiyacınız yoksa, atlamaktan çekinmeyin :+${array[@]}.

Ayrıca, [[burada operatörün kullanmanın şart olduğunu unutmayın [:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

İle -uaslında kullanmalıdır ${array[@]+"${array[@]}"}cf stackoverflow.com/a/34361807/1237617
Jakub Bochenski

@ JakubBochenski Bash'in hangi versiyonundan bahsediyorsun? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9
x-yuri

Tek parantez örneğindeki sorun @elbette. *Dizi genişlemesini kullanabilirsiniz [ "${array[*]}" ], değil mi? Yine [[de iyi çalışıyor. Her ikisinin de birden fazla boş dizgiye sahip bir dizi için davranışı biraz şaşırtıcıdır. Her ikisi de [ ${#array[*]} ]ve [[ "${array[@]}" ]]yanlıştır array=()ve array=('')bunun için doğrudur array=('' '')(iki veya daha fazla boş dizeler). Bir veya daha fazla boş dizginin hepsinin gerçek olmasını istiyorsanız, onu kullanabilirsiniz [ ${#array[@]} -gt 0 ]. Eğer hepsinin yanlış olmasını istiyorsan, belki //dışarı çıkarırsın .
eisd

@ eisd kullanabilirdim [ "${array[*]}" ], ancak böyle bir ifadeyle karşılaşacak olsaydım, ne yaptığını anlamam daha zor olurdu. Beri [...]enterpolasyon sonucu dizeleri açısından faaliyet göstermektedir. Buna karşılık [[...]], enterpolasyonun ne olduğunu anlayabilir. Yani, bir dizi geçtiğini biliyor olabilir. [[ ${array[@]} ]]bana "dizinin boş olmadığını kontrol edin", [ "${array[*]}" ]"tüm dizi öğelerinin enterpolasyonunun sonucu boş olmayan bir dize olup olmadığını kontrol edin" olarak okuyor .
x-yuri

... İki boş dizgeyle olan davranışa gelince, bu benim için hiç şaşırtıcı değil. Şaşırtıcı olan tek bir boş dize ile davranış. Ama tartışmalı olarak makul. Birincisi [ ${#array[*]} ], [ "${array[*]}" ]herhangi bir sayıdaki element için geçerli olduğu için muhtemelen demek istedin. Çünkü elemanların sayısı her zaman boş olmayan bir dizedir. İkincisinin iki eleman ile ilgili olarak, parantez içindeki ifade ' 'boş olmayan dize olana genişler . Gelince [[ ${array[@]} ]], sadece iki unsurdan oluşan herhangi bir dizinin boş olmadığını düşünüyorlar.
x-yuri,

2

Boş gibiarr=("" "") , boş gibi öğeleri içeren bir dizi tespit etmek istiyorsanızarr=()

Tüm elemanları birbirine yapıştırabilir ve sonucun sıfır uzunlukta olup olmadığını kontrol edebilirsiniz. (Dizi içeriğinin düzleştirilmiş bir kopyasını oluşturmak, dizi çok büyük olabilirse performans için ideal değildir. Ancak umarım böyle programlar için bash kullanmıyorsunuzdur ...)

Ancak "${arr[*]}", ilk karakteri ile ayrılan öğelerle genişler IFS. Dolayısıyla / kaydetmek IFS restore ve yapmanız gereken IFS=''bu işi yapmak için, ya da başka kontrol etmenizi dize uzunluğu == dizi öğelerinin # - 1. (bir dizi nelemanlarına sahiptir n-1ayırıcılar). Bu tek tek işle başa çıkabilmek için, birleştirmeyi 1 ile değiştirmek en kolay yoldur.

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

ile test durum set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

Ne yazık ki bu başarısız arr=(): [[ 1 -ne 0 ]]. Bu yüzden aslında önce boş dizileri ayrı ayrı kontrol etmeniz gerekir.


Veya ileIFS='' . Muhtemelen bir subshell kullanmak yerine IFS'yi kaydetmek / geri yüklemek istersiniz çünkü bir subshell'den kolayca sonuç alamazsınız.

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

örnek:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

yok çalışmak arr=()- yine sadece boş bir dize var.


Oyum kırıldı, ancak kullanmaya başladım [[ "${arr[*]}" = *[![:space:]]* ]], çünkü en az bir WS olmayan karaktere güvenebilirim.
Michael

@Michael: evet, reddetmene gerek yok, bu iyi bir seçenek arr=(" ").
Peter Cordes

0

Benim durumumda, ikinci cevap yeterli değildi, çünkü boşluklar olabilirdi. Birlikte geldim:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

echo | wckabuk yerleşiklerini kullanarak karşılaştırıldığında gereksiz yere verimsiz görünüyor.
Peter Cordes

@PeterCordes'i anladığımdan emin değilim, ikinci cevapları [ ${#errors[@]} -eq 0 ];boşluk problemi üzerinde çalışacak şekilde değiştirebilir miyim? Ben de yerleşik yapmayı tercih ederim.
Micha,

Boşluk tam olarak bir soruna nasıl yol açar? $#bir sayıya genişler ve daha sonra bile iyi çalışır opts+=(""). örneğin unset opts; opts+=("");opts+=(" "); echo "${#opts[@]}"ve anladım 2. İşe yaramayan bir şey örneği gösterebilir misiniz?
Peter Cordes

Uzun zaman önceydi. IIRC kaynak kaynak daima en az "" yazdırılır. Bu nedenle, opts = "" veya opts = ("") 1 satırına değil, boş satırsonunu veya boş dizgiyi görmezden geldim.
Micha

Tamam, opts=("")aynı şekilde opts=()mi davranman gerekiyor ? Bu boş bir dizi değil, ancak boş bir dizi veya boş bir ilk elemanı kontrol edebilirsin opts=(""); [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty. Şu anki cevabınız için "seçenek yok" yazdığını opts=("" "-foo")ve bunun tamamen sahte olduğunu unutmayın ve bu, bu davranışı yeniden üretir. Tüm dizi öğelerini sıfır olmayan uzunluğu denetleyen düz bir dizeye enterpolasyon yapmak için tahmin edebilirim . [[ -z "${opts[*]}" ]]-z İlk elemanı kontrol etmek yeterliyse -z "$opts"çalışır.
Peter Cordes

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.