Bir değişkenin nounset kabuk seçeneğiyle 4.2 sürümünden önce Bash'te tanımlanıp tanımlanmadığı nasıl test edilir?


16

"GNU bash, Sürüm 4.2" öncesi Bash sürümleri -viçin testkomut seçeneği için eşdeğer alternatifler var mı? Örneğin:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo

-vbir seçenek değil test, koşullu ifadeler için bir operatördür.
Tim

@Tim Bu, jeton, dize ve satırın bir parçası olmanın yanı sıra üç şeydir: Bir optionkomuta test -v, bir operatorto conditional expressionve a unary test primaryfor [ ]. İngilizce dilini kabuk tanımlarıyla karıştırmayın.

Yanıtlar:


36

Tüm POSIX mermilerine taşınabilir:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

Emin olun ${foobar:+1}Eğer tedavi etmek istiyorsanız foobaraynı şekilde boş ya da değil tanımlanmış olup olmadığı. Ayrıca tanımsız ${foobar-}olduğunda boş bir dize foobarve foobaraksi takdirde (veya değerinden sonra başka bir varsayılan değer koyabilirsiniz) almak için de kullanabilirsiniz -.

Ksh olarak, eğer foobarbildirilen değil, olduğu gibi tanımlanan typeset -a foobar, daha sonra ${foobar+1}boş bir dizeye genişler.

Zsh, bildirilen ancak ayarlanmayan değişkenlere sahip değil: typeset -a foobarboş bir dizi oluşturur.

Bash'da, diziler farklı ve şaşırtıcı bir şekilde davranır. ${a+1}yalnızca boş olmayan bir dizi 1olduğunda genişler a, ör.

typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1

İlişkilendirilebilir diziler için de aynı ilke geçerlidir: dizi değişkenleri, boş olmayan bir dizin dizisine sahiplerse tanımlandığı gibi işlenir.

Herhangi bir türün değişkeninin tanımlanıp tanımlanmadığını test etmenin farklı, bash'a özgü bir yolu, listede listelenip listelenmediğini kontrol etmektir . Bu, boş dizileri tanımlandığı gibi bildirir, ancak bildirilen ancak atanmamış değişkenleri ( ) tanımsız olarak bildirir .${!PREFIX*}${foobar+1}unset foobar; typeset -a foobar

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is defined";;
  *) echo "foobar is not defined";;
esac

Bu, bildirilen ancak atanmamış değişkenlerin başarısız olması dışında veya döndürülen değerinin test edilmesinetypeset -p foobardeclare -p foobar eşdeğerdir typeset -p foobar.

Bash öğesinde, ksh öğesinde olduğu gibi set -o nounset; typeset -a foobar; echo $foobar, tanımsız değişkeni genişletme girişiminde bir hata tetikler foobar. Ksh'den farklı olarak, set -o nounset; foobar=(); echo $foobar(veya echo "${foobar[@]}") bir hata da tetikler.

Burada açıklanan tüm durumlarda, ${foobar+1}yalnızca $foobaraltında bir hataya neden olacaksa boş dizeye genişlediğini unutmayın set -o nounset.


1
Diziler ne olacak? Bash sürümü "GNU Bash, sürüm 4.1.10 (4) -salınımı (i686-pc-cygwin)" olarak echo "${foobar:+1}"yazdırmıyor 1ise declare -a foobar, daha önce yayınlanmış ve böylece foobardizinlenmiş dizidir. declare -p foobardoğru raporlar declare -a foobar='()'. Does "${foobar:+1}"dizi olmayan değişkenler için tek işi?
Tim Friske

@TimFriske ${foobar+1}(olmadan, :orijinal cevabımda iki örneği tersine çevirdim ), “tanımlanmış” tanımınız “ $foobaraltında çalışır ” ise bash dizileri için doğrudur set -o nounset. Tanımınız farklıysa, bash biraz garip. Güncellenmiş cevabımı görün.
Gilles 'SO- kötü olmayı kes

1
"Bash'da, diziler farklı ve şaşırtıcı bir şekilde davranıyorlar" konusuyla ilgili olarak. davranış bash (1) manajları, "Diziler" bölümünden açıklanabilir. "Alt dizisi olmayan bir dizi değişkenine başvurmak, 0 dizisi içeren diziye başvurmakla eşdeğerdir" ifadesini belirtir. Dolayısıyla, ne bir 0dizin ne de bir anahtar doğru olarak tanımlanmazsa a=(), ${a+1}hiçbir şey doğru olarak döndürmez.
Tim Friske

1
@TimFriske Bash uygulamasının belgelerine uygun olduğunu biliyorum. Ancak boş bir diziye tanımsız bir değişken gibi davranmak gerçekten garip bir tasarımdır.
Gilles 'SO- kötü olmayı bırak'

Ben sonucu tercih: Bash bir değişkenin ayarlanmış olup olmadığını nasıl kontrol edilir? -> Doğru Yol ... Bunun nasıl çalışması gerektiğine dair bir akor vuruyorum . Cesaret diyorum ki, Windows'un bir definedoperatörü var. Test bunu yapabilirdi ; o (zor olamaz um ....)
olacaktır

6

Gilles'in cevabını özetlemek için aşağıdaki kurallarımı oluşturdum:

  1. [[ -v foobar ]]Bash sürümünde değişkenler için kullanın > = 4.2.
  2. declare -p foobar &>/dev/nullBash <4.2 sürümündeki dizi değişkenleri için kullanın .
  3. Sırasıyla indexed ( ) ve keyed ( ) dizilerinin ( ) abonelikleri için (( ${foo[0]+1} ))veya kullanın . Seçenek 1 ve 2 burada çalışmıyor.(( ${bar[foo]+1} ))-a-Adeclare

3

Ben bash tüm değişkenler için aynı tekniği kullanın ve çalışır, örneğin:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

çıktılar:

foobar is unset

İken

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

çıktılar:

foobar is set

Dizi birden fazla değere sahipse [@] kaldırmak zorunda kaldı.
Stein Inge Morisbak

1
Tanımlanmış olup olmadığını değil, bir değeri olup olmadığını test etmek istediğiniz sürece bu harika çalışır. Yani, foobar=""bunu rapor edeceğim foobar is unset. Bekleme yok, onu geri alıyorum. Gerçekten sadece ilk eleman boş olup olmadığını test eder, bu yüzden değişkenin bir dizi DEĞİL olduğunu biliyorsanız iyi bir fikir gibi görünüyor ve sadece tanım boşluğu değil, boşluğu önemsiyorsunuz.
Ron Burk

yalnızca komut dosyalarınız tanımsız değişkenlere izin verildiğinde çalışıyorsa (set -u yok)
Florian Heigl
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.