Bash'te bir değişkenin ayarlanıp ayarlanmadığı nasıl kontrol edilir?


1567

Bash'te bir değişkenin ayarlanıp ayarlanmadığını nasıl anlayabilirim?

Örneğin, kullanıcının bir işleve ilk parametreyi verip vermediğini nasıl kontrol ederim?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens

210
Çözüm arayanlar için not: Bu soruya "değişken boş değil" sorusunu cevaplayan çok sayıda yüksek puanlı cevap vardır. Daha fazla düzeltme çözümü ("değişken kümedir") aşağıdaki Jens ve Lionel tarafından verilen cevaplarda belirtilmiştir.
Nathan Kidd

8
Ayrıca Russell Harmon ve Seamus -vtestlerinde doğrudurlar , ancak bu görünüşte sadece yeni sürümlerinde mevcuttur bashve kabuklar arasında taşınabilir değildir.
Graeme

5
@NathanKidd'in de işaret ettiği gibi Lionel ve Jens tarafından doğru çözümler verilmektedir. prosseek, kabul ettiğiniz cevabı bunlardan birine çevirmelisiniz .
Garrett

3
... ya da yanlış cevap aramızda daha fazla ayırt edici tarafından reddedilebilir, çünkü @prosseek sorunu ele almıyor.
dan3

Yanıtlar:


2301

(Genellikle) Doğru yol

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

Burada ${var+x}, ayarlanmamışsa hiçbir şeyi değerlendirmeyen ve dizeyi aksi halde değiştiren bir parametre genişletmesi bulunur .varx

Alıntılar

Tırnaklar atlanabilir ( ${var+x}bunun yerine söyleyebiliriz "${var+x}"), çünkü bu sözdizimi ve kullanım yalnızca tırnak işareti gerektirmeyen bir şeye genişleyeceğini garanti eder (ya genişler (tırnak işaretleri gerektirmeyen xkelime tırnaklar içerir) veya hiçbir şey (bunun sonucu olarak [ -z ]aynı değerle (doğru) uygun olarak değerlendirilir) sonuçlanır [ -z "" ]).

Bununla birlikte, alıntılar güvenli bir şekilde atlanabilirken ve herkes için hemen açık değildi (bu büyük bir Bash kodlayıcı olan bu alıntı açıklamasının ilk yazarı için bile belirgin değildi ), bazen çözümü yazmak daha iyi olurdu tırnak olarak [ -z "${var+x}" ]bir O (1) hız cezasının çok küçük maliyetle. İlk yazar bunu, bu cevaba URL'yi veren bu çözümü kullanan kodun yanına bir yorum olarak da ekledi.

(Genellikle) Yanlış yol

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

Bu genellikle yanlıştır, çünkü ayarlanmamış bir değişken ile boş dizeye ayarlanmış bir değişken arasında ayrım yapmaz. Yani, eğer var=''yukarıdaki çözüm "var boş" olacaktır.

Unset ve "boş dizeye ayarla" arasındaki ayrım, kullanıcının bir uzantı veya ek özellikler listesi belirtmesi gerektiği ve bunları belirtmemesinin varsayılan olarak boş olmayan bir değere ayarlanması ve boş dizenin belirtilmesi gereken durumlarda önemlidir. komut dosyasının boş bir uzantı veya ek özellikler listesi kullanmasını sağlayın.

Ayrım her senaryoda önemli olmayabilir. Bu durumlarda [ -z "$var" ]gayet iyi olacak.


29
@Garrett, düzenlemeniz bu yanıtı yanlış yaptı, ${var+x}kullanılacak doğru ikame. Kullanmak [ -z ${var:+x} ]farklı bir sonuç vermez [ -z "$var" ].
Graeme

11
Bu işe yaramıyor. Ben var değil bir değere ayarlanır veya değil ("unset var" ile temizlenir, "echo $ var" hiçbir çıkış üretir) olursa olsun "ayarlanmadı" alıyorum.
Brent212

10
Çözümün $ {parametre + word} sözdizimi için resmi manuel bölüm gnu.org/software/bash/manual/… ; bununla birlikte, bir hata, bu sözdiziminden çok açık bir şekilde bahsetmez, sadece alıntı söyler (İki nokta üst üste [":"] parametresini atlamak yalnızca ayarlanmamış bir parametre için bir testle sonuçlanır. .. $ {parametre: + word} [anlamına gelir] Parametre null veya unset ise, hiçbir şey ikame edilmez, aksi takdirde kelimenin genişlemesi ikame edilir.); atıf yapılan ref pubs.opengroup.org/onlinepubs/9699919799/utilities/… (bilmek iyi) burada çok daha açık dokümanlara sahiptir.
Destiny Architect

7
Birden fazla ifade kullandığınızda tırnak işaretleri gereklidir, örneğin bash 4.3-ubuntu'ya [ -z "" -a -z ${var+x} ]girer bash: [: argument expected(ancak örneğin zsh değil). Bazı durumlarda işe yarayan bir sözdizimi kullanarak yanıttan gerçekten hoşlanmıyorum.
FauxFaux

41
Basit kullanmak [ -z $var ], tırnak işaretlerini atlamaktan daha "yanlış" değildir. Her iki durumda da, girdiniz hakkında varsayımlar yaparsınız. Boş bir dizgiye unset gibi davranıyorsanız, [ -z $var ]tek ihtiyacınız olan şey budur.
nshew

909

Boş olmayan / sıfır olmayan dize değişkenini kontrol etmek için (ayarlanmışsa)

if [ -n "$1" ]

Bunun tam tersi -z. Kendimi -ndaha fazlasını kullanarak buluyorum -z.

Bunu şöyle kullanırsınız:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
Genellikle tercih [[ ]]üzerinde [ ]gibi [[ ]]daha güçlü olduğu ve bazı durumlarda daha az sorunlara neden olmaktadır (bkz bu soruyu ikisi arasındaki farkın açıklaması için). Soru özellikle bir bash çözümü istiyor ve herhangi bir taşınabilirlik gereksiniminden bahsetmiyor.
Akış

18
Soru, bir değişkenin ayarlanıp ayarlanmadığını nasıl görebilir . Sonra değişken kabul edemeyiz edilir ayarlayın.
HelloGoodbye

7
Katılıyorum, []özellikle kabuk (bash olmayan) komut dosyaları için daha iyi çalışıyor.
Hengjie


63
-nVarsayılan test olduğunu unutmayın, daha basit [ "$1" ]veya [[ $1 ]]aynı zamanda çalışın. Ayrıca [[ ]] tırnak işareti ile gereksiz olduğunu unutmayın .
tne

478

Bir parametrenin ayarlanmamış veya boş ("Boş") olup olmadığını veya bir değerle ayarlanıp ayarlanmadığını şu şekilde test edebilirsiniz :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Kaynak: POSIX: Parametre Genişlemesi :

"İkame" ile gösterilen tüm durumlarda, ifade gösterilen değerle değiştirilir. "Ata" ile gösterilen tüm durumlarda, parametreye ifadenin de yerini alan bu değer atanır.

Bunu çalışırken göstermek için:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

5
@HelloGoodbye Evet işe yarıyor: set foo; echo ${1:-set but null or unset}echos "foo"; set --; echo ${1:-set but null or unset}echoes set but null ...
Jens

4
@HelloGoodbye Konumsal parametreler, uh, set:-) ile ayarlanabilir
Jens

95
Bu cevap çok kafa karıştırıcı. Bu tablonun nasıl kullanılacağına dair pratik örnekler var mı?
Ben Davis

12
@BenDavis Ayrıntılar, tablonun alındığı bölüme atıfta bulunan POSIX standardının bağlantısında açıklanmıştır. Yazdığım hemen her komut dosyasında kullandığım bir yapı, : ${FOOBAR:=/etc/foobar.conf}değişken veya boşsa bir değişken için varsayılan değer ayarlamaktır.
Jens

1
parameterherhangi bir değişken adıdır. wordkullandığınız tablodaki sözdizimine ve değişkenin $parameterayarlanmış, ayarlanmış ancak boş veya ayarlanmamış olmasına bağlı olarak değiştirilecek bir dizedir . İşleri daha karmaşık hale getirmek için değil word, değişkenleri de içerebilir! Bu, bir karakterle ayrılmış isteğe bağlı argümanları iletmek için çok yararlı olabilir. Örneğin: ayarlanmış ancak boş değilse ${parameter:+,${parameter}}virgülle ayrılmış çıktılar verir ,$parameter. Bu durumda, wordbir,${parameter}
TrinitronX

260

Burada belirtilen tekniklerin çoğu doğru olsa da, bash 4.2 , değişkenin değerini test etmek yerine bir değişkenin ( man bash ) varlığı için gerçek bir testi destekler .

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

Özellikle, bu yaklaşım, kullanım gibi diğer birçok yaklaşımın aksine , set -u/ set -o nounsetkipinde ayarlanmamış bir değişkeni kontrol etmek için kullanıldığında hataya neden olmaz [ -z.


9
Bass 4.1.2'de, değişkenin ayarlanıp ayarlanmadığına bakılmaksızın, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan

32
'Test' yerleşkesine '-v' argümanı bash 4.2'de eklendi.
Ti Strga

19
-v argümanı, -u kabuk seçeneğine (nounset) bir tamamlayıcı olarak eklendi. 'İsim seti' açıldığında (set -u) kabuk etkileşimli değilse bir hata döndürür ve sonlanır. $ #, konum parametrelerini kontrol etmek için iyi oldu. Bununla birlikte, adlandırılmış değişkenlerin kümelenme dışında, kümelenmemiş olarak tanımlanmaları için başka bir yola ihtiyacı vardı. Bu özelliğin bu kadar geç gelmesi talihsiz bir durumdur, çünkü birçoğu daha önceki sürümle uyumlu olmak zorundadır, bu durumda kullanamazlar. Diğer tek seçenek, yukarıda gösterildiği gibi dolaşmak için hileler veya 'kesmek' kullanmaktır, ancak en yetersizdir.
osirisgothra

2
Bunun bildirilen ancak ayarlanmamış değişkenler için çalışmadığını unutmayın. ör.declare -a foo
miken32

17
Bu yüzden kabul edilen / en çok oy alan cevaptan sonra okumaya devam ediyorum.
Joe

176

Bunu aşağıdakilerden biri ile yapmanın birçok yolu vardır:

if [ -z "$1" ]

$ 1 boş veya ayarlanmamışsa bu başarılı olur


46
Unset parametresi ile null değeri olan bir parametre arasında bir fark vardır.
chepner

21
Sadece bilgiçlikçi olmak istiyorum ve bunun sorunun ortaya koyduğu zıt cevap olduğunu belirtmek istiyorum. Sorular değişkenin ayarlanıp ayarlanmadığını sorar, değişkenin ayarlanmadığını sorar.
Philluminati

47
[[ ]]taşınabilirlik tuzaklarından başka bir şey değildir. if [ -n "$1" ]burada kullanılmalıdır.
Jens

73
Bu cevap yanlış. Soru, bir değişkenin boş olmayan bir değere ayarlanıp ayarlanmadığını söylemenin bir yolunu sorar. Verilen foo="", $foobir değeri var, ama -zsadece boş olduğunu bildirecek. Ve -z $fooeğer varsa patlayacak set -o nounset.
Keith Thompson

4
"Değişken ayarlandı" tanımına bağlıdır. Değişkenin sıfırdan farklı bir dizeye ayarlanıp ayarlanmadığını kontrol etmek istiyorsanız , mbranning yanıtı doğrudur. Ancak değişkenin bildirildiğini ancak başlatılmadığını (yani foo=) kontrol etmek istiyorsanız , Russel'in cevabı doğrudur.
Akış

77

POSIX tablosunu her zaman diğer cevapta grok'a yavaş buluyorum, işte benim almam:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Her grubun (önceki iki nokta üst üste olan ve olmayan) aynı ayarlanmış ve ayarlanmamış vakalara sahip olduğunu unutmayın, bu nedenle farklı olan tek şey boş vakaların nasıl ele alınacağıdır .

Önceki kolon ile, boş ve ayarlanmamış vakalar aynıdır, bu yüzden mümkünse bunları kullanırım (yani :=, sadece =boş kasa tutarsız olduğu için kullanmayın).

Başlıklar:

  • set araç VARIABLEboş değil ( VARIABLE="something")
  • boş araçlar VARIABLEboş / null ( VARIABLE="")
  • ayarlanmamış araçlar VARIABLEmevcut değil ( unset VARIABLE)

Değerler:

  • $VARIABLE sonucun değişkenin orijinal değeri olduğu anlamına gelir.
  • "default" sonuç, sağlanan yeni dize anlamına gelir.
  • "" sonucun boş (boş bir dize) olduğu anlamına gelir.
  • exit 127 komut dosyasının çıkış kodu 127 ile çalışmayı durdurduğu anlamına gelir.
  • $(VARIABLE="default")sonucun değişkenin orijinal değeri olduğu ve sağlanan değiştirme dizesinin ileride kullanılmak üzere değişkene atandığı anlamına gelir .

1
Yalnızca gerçek kodlama hataları olanları düzelttiniz (değişkenlerle çalışırken istenmeyen dolaylama örnekleri). Doların açıklamayı yorumlamada fark yarattığı birkaç durumu düzeltmek için bir düzenleme yaptım. BTW, tüm kabuk değişkenleri (diziler hariç) dize değişkenleridir; sadece sayı olarak yorumlanabilecek bir dize içeriyor olabilirler. Dizeler hakkında konuşmak sadece iletiyi sisler. Tırnakların dizelerle ilgisi yoktur, sadece kaçmaya alternatiftir. VARIABLE=""olarak yazılabilir VARIABLE=. Yine de, birincisi daha okunabilir.
Palec

Tablo için teşekkürler çok yararlı, ancak ben unset veya boş komut dosyası çıkışı olmaya çalışıyorum. Ama aldığım çıkış kodu 1 değil 127.
Astronot

64

Bir değişkenin boş olup olmadığını görmek için

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

Bir değişkenin ayarlanmamış veya boş olup olmadığının tersi testler:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Bir değişkenin (boş veya boş olmayan) ayarlandığını görmek için şunu kullanıyorum:

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

Bir değişken belirlenmemişse ters testler:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

Ben de bunu bir süredir kullanıyorum; sadece azaltılmış bir şekilde:[ $isMac ] && param="--enable-shared"

@Bhaskar Sanırım bu cevap zaten yarım yıl önce aynı cevabı ( iff ${variable+x}almak için kullanıldı) zaten sağlanmıştı . Ayrıca neden doğru olduğunu açıklayan çok daha fazla ayrıntı var. x$variable
Mark Haferkamp

ancak ${variable+x}daha az okunabilir (& açık)
knocte

35

Bash'in modern bir sürümünde (4.2 veya üstü sanırım; emin değilim), bunu denerdim:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

5
Ayrıca [ -v "$VARNAME" ]bunun yanlış olmadığını, ancak farklı bir test gerçekleştirdiğini unutmayın . Varsayalım VARNAME=foo; fooayarlanmış bir değişken olup olmadığını kontrol eder .
chepner

5
Taşınabilirlik isteyenler -viçin not POSIX uyumlu değildir
kzh

Biri alırsa [: -v: unexpected operator, bashbunun yerine kullanmak gerekir sh.
koppor

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Her ne kadar argümanlar için normalde en iyisi, argüman sayısı olan $ # 'ı test etmek en iyisidir.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
İlk test geriye dönüktür; Sanırım istediğin [ "$1" != "" ](ya da [ -n "$1" ]) ...
Gordon Davisson

10
$1Boş dizeye ayarlanırsa bu başarısız olur .
HelloGoodbye

2
Cevabın ikinci kısmı (parametreleri saymak), $1boş bir dizeye ayarlandığında cevabın çalışmayan ilk kısmı için yararlı bir çözümdür .
Mark Berry

Açıksa bu başarısız olur set -o nounset.
Flimm

21

Not

bashEtiketi nedeniyle ağır Bash odaklı bir cevap veriyorum .

Kısa cevap

Bash'de yalnızca adlandırılmış değişkenlerle uğraştığınız sürece, bu işlev boş bir dizi olsa bile değişkenin ayarlanmış olup olmadığını her zaman size bildirmelidir.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Bu neden çalışıyor?

Bash'te (en azından 3.0'a kadar), varbildirilen / ayarlanan bir değişkense, geçerli türü ve değeri ne olursa olsun değişkeni ayarlayacak declare -p varbir declarekomut verir varve durum kodu 0(başarı) döndürür . Eğer varbildirilmeyen, o zaman declare -p varbir hata mesajı verir stderrve getiri durum kodu 1. Kullanılması &>/dev/null, hem normal yönlendirir stdoutve stderrçıktı /dev/null, görülecek asla ve durum kodu değiştirmeden. Böylece işlev yalnızca durum kodunu döndürür.

Bash'de neden diğer yöntemler (bazen) başarısız oluyor?

  • [ -n "$var" ]: Bu sadece ${var[0]}boş olup olmadığını kontrol eder . (Bash'te $varaynıdır ${var[0]}.)
  • [ -n "${var+x}" ]: Bu yalnızca ${var[0]}ayarlı olup olmadığını kontrol eder .
  • [ "${#var[@]}" != 0 ]: Bu yalnızca en az bir dizinin $varayarlanıp ayarlanmadığını kontrol eder .

Bu yöntem Bash'te başarısız olduğunda

Bu yalnızca (dahil adında değişkenler için çalışır $_) değil, bazı özel değişkenler ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., ve herhangi ben unutmuş olabilir). Bunların hiçbiri diziler olmadığından, POSIX stili [ -n "${var+x}" ]tüm bu özel değişkenler için çalışır. Ancak, birçok özel değişken, işlevler çağrıldığında değerleri / varlığı değiştirdiği için bir işleve sarmaya dikkat edin.

Kabuk uyumluluk notu

Komut dosyanız diziler içeriyorsa ve mümkün olduğunca çok sayıda kabukla uyumlu hale getirmeye çalışıyorsanız, typeset -pbunun yerine kullanmayı düşünün declare -p. Ksh'ın sadece eskisini desteklediğini okudum, ancak bunu test edemedim. Bash 3.0+ ve Zsh 5.5.1'in her ikisini de desteklediğini typeset -pve declare -psadece hangisinin diğeri için alternatif olduğu konusunda farklı olduğunu biliyorum . Ancak bu iki anahtar kelimenin ötesindeki farklılıkları test etmedim ve başka kabukları da test etmedim.

Komut dosyanızın POSIX sh uyumlu olması gerekiyorsa dizileri kullanamazsınız. Diziler olmadan [ -n "{$var+x}" ]çalışır.

Bash'te farklı yöntemler için karşılaştırma kodu

Bu fonksiyon, unsets değişken var, evalgeçti kod s olmadığını belirlemek için testler çalışan vartarafından ayarlanır evald kodu ve son olarak, farklı testler için elde edilen durum kodlarını göstermektedir.

I atlama ediyorum test -v var, [ -v var ]ve [[ -v var ]]bunlar POSIX standardına aynı sonuçlar için [ -n "${var+x}" ], Bash 4.2+ gerektirirken. Ayrıca atlıyorum typeset -pçünkü declare -ptest ettiğim kabuklardakiyle aynı (Bash 3.0 - 5.0 ve Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Test senaryosu kodu

Değişken ilişkilendirilebilir bir dizi olarak bildirilmemişse, sayısal olmayan dizi indekslerinin Bash tarafından "0" olarak işlenmesi nedeniyle test sonuçlarının beklenmedik olabileceğini unutmayın. Ayrıca, ilişkilendirilebilir diziler yalnızca Bash 4.0+ için geçerlidir.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Test çıktısı

Başlık satırı tekabül test Simgesellerine [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ]ve declare -p varsırasıyla.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

özet

  • declare -p var &>/dev/null (% 100?) en az 3.0'dan beri Bash'te adlandırılmış değişkenleri test etmek için güvenilirdir.
  • [ -n "${var+x}" ] POSIX uyumlu durumlarda güvenilirdir, ancak dizileri işleyemez.
  • Bir değişkenin boş olup olmadığını ve diğer kabuklarda bildirilen değişkenleri kontrol etmek için başka testler de mevcuttur. Ancak bu testler ne Bash ne de POSIX komut dosyaları için uygun değildir.

3
Denediğim cevaplardan, bu ( declare -p var &>/dev/null) çalışan SADECE. TEŞEKKÜR EDERİM!
Kullanıcı1387866

2
@ mark-haferkamp Okumak için işlev kodunuza kritik "/" karakterini eklemek için cevabınızı düzenlemeye çalıştım, ... &> /dev/nullancak SO tek bir karakter düzenlemesi yapmama izin vermedi 🙄 (> 6 karakter olmalı - hatta boşluk eklemeyi denemelisiniz) ama işe yaramadı). Ayrıca, işaret ettiğiniz gibi $1değişken (örn. OUTPUT_DIR) Adlı bir örnek için işlevi değiştirme işlevini değiştirmeye değebilir, çünkü belirttiğiniz gibi $1, buradaki özel değişkenler çalışmaz. Her neyse, bu kritik bir düzenleme, aksi takdirde kod adlı nullgöreli bir dizinde adlı bir dosya oluşturmak istiyor dev. BTW - harika cevap!
joehanna

Ayrıca, $ öneki olmadan değişkenin adını kullanarak da çalışır: declare -p PATH &> /dev/nullolarak döndürür 0 declare -p ASDFASDGFASKDF &> /dev/nulldöndürür 1. (bash 5.0.11 (1) -salınımı üzerine)
joehanna

1
Harika iş! Dikkatli 1 kelime: declare varBir değişken var bildirir ama yok değil açısından ayarlayın set -o nounset: ile Testideclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche

@joehanna Eksik olanı yakaladığınız için teşekkürler /! Bunu düzelttim, ama mantığın, özellikle @ xebeche'nin yorumu ışığında, bir işlevi garanti edecek kadar kafa karıştırıcı olduğunu düşünüyorum. @xebeche Bu benim cevap için sakıncalıdır. ... Ben böyle bir şey denemek gerekecek gibi görünüyor declare -p "$1" | grep =ya test -v "$1".
Mark Haferkamp

19

Aşağıdakileri içeren bir komut dosyasında ayarlanmamış veya boş olup olmadığını kontrol etmek isteyenler için set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

Düzenli [ -z "$var" ]kontrol var; unbound variableif ile başarısız olur set -uancak başarısız olursa [ -z "${var-}" ] ayarlanamazsa boş dizeye genişlervar .


İki nokta üst üste eksik. Olmalı ${var:-}, değil ${var-}. Ancak Bash'te, iki nokta üst üste olmadan bile çalıştığını doğrulayabilirim.
Palec

3
${var:-}ve ${var-}farklı şeyler demek. ${var:-}eğer dizesini boşaltmak için genişleyecektir varayarlanmazsa veya boş ve ${var-}sadece atanmamış ise boş bir dizeye genişleyecektir.
RubenLaguna

Sadece yeni bir şey öğrendim, teşekkürler! İki nokta üst üste işareti atlandığında yalnızca ayarlanmamış bir parametre için sınama yapılır. Kolon dahil edilirse, operatör her iki parametrenin varlığını ve değerinin boş olmadığını test eder; iki nokta üst üste işareti atlanırsa, operatör yalnızca varlığını test eder. Burada bir fark yaratmaz, ama bilmek güzeldir.
Palec

17

Eğer ayarlanmamışsa çıkmak istersiniz

Bu benim için çalıştı. Bir parametre ayarlanmamışsa komut dosyamın bir hata mesajıyla çıkmasını istedim.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Bu, çalıştırıldığında bir hata ile döner

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Yalnızca kontrol edin, çıkış yok - Boş ve Ayarsız GEÇERSİZ

Yalnızca set = VALID veya unset / empty = INVALID değerinin ayarlanıp ayarlanmadığını kontrol etmek istiyorsanız bu seçeneği deneyin.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Veya kısa testler bile ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Yalnızca kontrol edin, çıkış yok - Yalnızca boş GEÇERSİZ

Ve bu sorunun cevabı. Sadece set / empty = VALID veya unset = INVALID değerinin olup olmadığını kontrol etmek istiyorsanız bunu kullanın.

NOT, "..- 1}" içindeki "1" önemsizdir, herhangi bir şey olabilir (x gibi)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Kısa testler

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Bu yanıtı, soruyu doğru bir şekilde yanıtlamam için bana meydan okuyan @ mklement0'a (yorumlar) adadım.

Referans http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

Bir değişkenin boş olmayan bir değerle ayarlanıp ayarlanmadığını kontrol etmek için [ -n "$x" ], diğerlerinin belirttiği gibi kullanın .

Çoğu zaman, boş bir değere sahip bir değişkeni, ayarlanmamış bir değişkenle aynı şekilde ele almak iyi bir fikirdir. : Gerekirse Ama ikisini ayırt edebilir [ -n "${x+set}" ]( "${x+set}"genişler seteğer xvarsa boş dize ayarlanır ve xayarlanmazsa).

Bir parametrenin iletilip iletilmediğini kontrol etmek $#için, işleve (veya bir işlevde değilken betiğe) geçirilen parametre sayısı olan testi yapın ( Paul'ün cevabına bakın ).


14

Kılavuz bashsayfasının "Parametre Genişletme" bölümünü okuyun . Parametre genişletme, ayarlanan bir değişken için genel bir test sağlamaz, ancak ayarlanmamışsa bir parametrede yapabileceğiniz birkaç şey vardır.

Örneğin:

function a {
    first_arg=${1-foo}
    # rest of the function
}

atanırsa first_argeşit olarak ayarlanır $1, aksi takdirde "foo" değerini kullanır. Eğer akesinlikle tek bir parametre almak ve iyi bir kusur mevcut olmalıdır hiçbir parametre verildiğinde, bir hata mesajı ile çıkabilirsiniz:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(Kullanımının : Yalnızca bağımsız değişkenlerinin değerlerini genişleten bir null komutu olarak edin $1. Bu örnekte hiçbir şey yapmak istemiyoruz , ayarlanmadıysa çıkın)


1
Şaşırdım, diğer cevapların hiçbiri basit ve zariften bahsetmiyor : ${var?message}.
üçlü

Bunu nereden aldın? Olağanüstü! Bu justa çalışıyor! Ve kısa ve zarif! Teşekkür ederim!
Roger

13

Bash'da yerleşkenin -viçinde kullanabilirsiniz [[ ]]:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

Kullanmak [[ -z "$var" ]], bir değişkenin ayarlanıp ayarlanmadığını bilmenin en kolay yoludur, ancak bu seçenek -zayarlanmamış bir değişken ile boş bir dizeye ayarlanmış bir değişken arasında ayrım yapmaz:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

Değişken türüne göre kontrol etmek en iyisidir: env değişkeni, parametre veya normal değişken.

Env değişkeni için:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Bir parametre için (örneğin, parametrenin varlığını kontrol etmek için $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Düzenli bir değişken için (yardımcı bir işlev kullanarak, zarif bir şekilde yapmak için):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Notlar:

  • $#: konumsal parametre sayısını verir.
  • declare -p: parametre olarak iletilen değişkenin tanımını verir. Varsa 0 döndürür, değilse 1 döndürür ve bir hata iletisi yazdırır.
  • &> /dev/null:declare -p dönüş kodunu etkilemeden çıktıyı bastırır .

5

Bash seçeneği set -uetkinleştirildiğinde yukarıdaki yanıtlar çalışmaz . Ayrıca, dinamik değiller, örneğin, "kukla" adı ile değişken test nasıl tanımlanır? Bunu dene:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

İlgili: Bash'te, "-u" modunda bir değişkenin tanımlanmış olup olmadığını nasıl test ederim?


1
Bu cevabın neden reddedildiğini çok fazla bilmek istiyorum ... Benim için harika çalışıyor.
LavaScornedOven

Bash, bir eval olmadan aynı şeyi: Değişimi eval "[ ! -z \${$1:-} ]"dolaylı değerlendirmeye: [ ! -z ${!1:-} ];.

@BinaryZebra: İlginç bir fikir. Ayrı bir cevap olarak yayınlamanızı öneririm.
kevinarpe

"Eval" kullanmak bu çözümü kod enjeksiyonuna karşı savunmasız hale getirir: $ is_var_defined 'x}]; echo "gotcha"> & 2; # 'gotcha
tetsujin


4

Tercih ettiğim yol şudur:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Yani, temel olarak, bir değişken ayarlanırsa, "sonuçta bir olumsuzlama false" olur (ne olacak true= "ayarlanır").

Ve ayarlanmamışsa, "sonuçta olumsuzlama true" olur (boş sonuç olarak değerlendirilir true) (böylece false= = NOT set " olarak sona erer ).



1

Kabukta -z, dize uzunluğu sıfırsa Doğru olan işlecini kullanabilirsiniz .

Ayarlanmadıysa varsayılanı ayarlamak için basit bir astar MY_VAR, aksi takdirde isteğe bağlı olarak mesajı görüntüleyebilirsiniz:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1, $2ve değerine kadar $Nher zaman ayarlanır. Üzgünüm, bu başarısız.

Denedim ve işe yaramadı, belki bash versiyonumda destek yok. Idk, yanılıyorsam özür dilerim. :)

0

Herhangi bir şey kontrol etmek istiyorsanız bunu yapmak için (çok) daha iyi bir kod buldum $@.

eğer [[$ 1 = ""]]
sonra
  echo '$ 1 boş'
Başka
  echo '$ 1 dolu'
fi

Neden hepsi bu? Her şey de $@Bash mevcut fakat varsayılan olarak 's boş, bu nedenle test -zve test -nsize yardımcı olabilir.

Güncelleme: Bir parametredeki karakter sayısını da sayabilirsiniz.

[$ {# 1} = 0] ise
sonra
  echo '$ 1 boş'
Başka
  echo '$ 1 dolu'
fi

Biliyorum, ama hepsi bu. Bir şey boşsa bir hata alırsınız! Afedersiniz. : P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
StackOverflow'a hoş geldiniz. Cevabınız takdir edilirken, sadece koddan daha fazlasını vermek en iyisidir. Cevabınıza muhakeme veya küçük bir açıklama ekleyebilir misiniz? Yanıtınızı genişletmeyle ilgili diğer fikirler için bu sayfaya bakın .
Celeo

0

Her gün kullandığım şey bu:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Bu Linux ve Solaris altında bash 3.0'a kadar iyi çalışıyor.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
Bash 2.0 ve sonrası Dolaylı genişleme mevcuttur. test -n "$(eval "echo "\${${1}+x}"")"Eşdeğeri için uzun ve karmaşık (bir eval de dahil) değiştirebilirsiniz :[[ ${!1+x} ]]

0

Bash'ın ham ayrıntılarını gizlemek için yardımcı fonksiyonları seviyorum. Bu durumda, bunu yapmak daha da fazla (gizli) gaddarlık ekler:

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Bu uygulamada (Jens ve Lionel'in cevaplarından esinlenerek) ilk hata yaptığım için farklı bir çözüm buldum:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Daha açık, daha basit ve anlaşılması / hatırlanması daha kolay buluyorum. Test durumu eşdeğer olduğunu gösterir:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

Ayrıca test durumda gösterir local varetmez değil ( '=' takip edilmiştir) değişken bildirin. Bir süredir değişkenleri bu şekilde ilan ettiğimi sanıyordum, sadece şimdi niyetimi ifade ettiğimi keşfetmek için ... Sanırım hayır.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky çubuğu: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared çubuğu: 0
IsDeclared baz: 0

İLAVE: usecase

Çoğunlukla bu testi, işlevlere biraz "zarif" ve güvenli bir şekilde (ve neredeyse bir arabirime benzeyen ...) parametreler vermek (ve döndürmek) için kullanıyorum :

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Tümü ile çağrılmışsa, bildirilen değişkenler gerekir:

Selam Dünya!

Başka:

Hata: değişken bildirilmedi: outputStr


0

Tüm cevapları gözden geçirdikten sonra, bu da işe yarar:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

SOME_VARsahip olduğum yerine koymak istemezseniz $SOME_VAR, boş bir değere ayarlayacaktır; $bunun çalışması için gereklidir.


Eğer varsa çalışmaz set -uyazdırır hangi yürürlükte unboud variablehatayı
Michael Heuberger

-1

Bir değişkenin ayarlanıp ayarlanmadığını kontrol etmek için

var=""; [[ $var ]] && echo "set" || echo "not set"

3
$varBoş dizeye ayarlanırsa bu başarısız olur .
HelloGoodbye

-1

Bir değişkenin bağlı veya bağlı olmadığını test etmek isterseniz, nounset seçeneğini açtıktan sonra bile bu işe yarar:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

Bence demek istiyorsun set -o nounset, değil set -o noun set. Bu yalnızca düzenlenmiş değişkenler için geçerlidir export. Ayrıca ayarlarınızı geri almak zor bir şekilde değiştirir.
Keith Thompson

-1

Her zaman kodu ilk kez gören herkes tarafından anlaşılması kolay göründüğü gerçeğine dayanarak bunu kullanıyorum:

if [ "$variable" = "" ]
    then
    echo "Variable X is empty"
fi

Ve boş olup olmadığını kontrol etmek istiyorsanız;

if [ ! "$variable" = "" ]
    then
    echo "Variable X is not empty"
fi

Bu kadar.


4
Soru, bir değişkenin boş olup olmadığını nasıl kontrol edeceğinizi sorar.
HelloGoodbye

Muhtemelen [[ ... ]]yerine kullanmanız gerekir [ ... ]. İkincisi aslında harici bir komuttur ve bir değişkenin ayarlanıp ayarlanmadığı konusunda herhangi bir görünürlüğü olmaz.
solidsnack
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.