Bir dizenin Bash kabuk betiğinde tanımlanıp tanımlanmadığı nasıl anlaşılır


151

Boş dizeyi kontrol etmek istersem yapardım

[ -z $mystr ]

ama değişkenin tanımlanmış olup olmadığını kontrol etmek istersem ne olur? Yoksa Bash kodlamasında bir ayrım yok mu?


27
lütfen [-z $ mystr] yerine [-z "$ mystr"] kullanma alışkanlığı içinde olun
Charles Duffy

ters şey nasıl yapılır? Dize boş olmadığında yani
yolu açın

1
@flow: Ne oldu[ -n "${VAR+x}"] && echo not null
Lekensteyn

1
@CharlesDuffy Bunu daha önce birçok çevrimiçi kaynakta okudum. Bu neden tercih edilir?
13'te

6
@Ayos çünkü tırnaklarınız yoksa, değişkenin içeriği dize bölünmüş ve globbed edilmiştir; boş bir dize [ -z ]ise [ -z "" ]; boşlukları varsa, onun [ -z "my" "test" ]yerine olur [ -z my test ]; o eğer ve [ -z * ]sonra *da dizindeki dosyaların adlarıyla değiştirilir.
Charles Duffy

Yanıtlar:


138

Ben tarafından (belirtilmediği sürece) sonra olan cevap ima düşünüyorum Vinko 'ın cevabı basitçe dile olmasa da. VAR'ın ayarlanmış ancak boş olup olmadığını ayırt etmek için şunları kullanabilirsiniz:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Muhtemelen ikinci satırdaki iki testi bir ile birleştirebilirsiniz:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Ancak, Autoconf belgelerini okursanız, terimlerin ' -a' ile birleştirilmesini önermediklerini ve ayrı basit testlerin birlikte kullanılmasını öneriyoruz &&. Sorun olan bir sistemle karşılaşmadım; bu, eskiden var olmadıkları anlamına gelmez (ancak uzak geçmişte nadir olmasalar bile, bu günlerde muhtemelen çok nadirdirler).

Bunların ayrıntılarını ve diğer ilgili shell parametre genişletmelerini , testveya[ komutunu ve koşullu ifadeleri Bash kılavuzunda bulabilirsiniz.


Kısa süre önce şu soruyla ilgili e-postayla soru soruldu:

İki test kullanıyorsunuz ve ikincisini iyi anlıyorum, ama birincisini değil. Daha doğrusu değişken genişleme ihtiyacını anlamıyorum

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

Bu aynı şeyi yapamaz mı?

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

Adil soru - cevap 'Hayır, daha basit alternatifiniz aynı şeyi yapmaz'.

Bunu testinizden önce yazdığımı varsayalım:

VAR=

Testiniz "VAR hiç ayarlanmadı" diyecek, ama benimki (ima yoluyla hiçbir şey yankılamadığı için) "VAR ayarlandı ancak değeri boş olabilir" diyecektir. Bu komut dosyasını deneyin:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

Çıktı:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

İkinci test çiftinde, değişken ayarlanır, ancak boş değere ayarlanır. Bu ${VAR=value}ve ${VAR:=value}notasyonların yaptığı ayrımdır . Ditto ${VAR-value}ve ${VAR:-value}ve ${VAR+value}ve ${VAR:+value}ve böyle devam eder.


As Gili onun içinde işaret cevap koşarsan, bashile set -o nounsetseçeneği, daha sonra temel cevap yukarıdaki başarısız olur unbound variable. Kolayca giderilebilir:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Veya set -o nounsetseçeneği set +u( set -uile eşdeğer set -o nounset) ile iptal edebilirsiniz .


7
Bu cevapla ilgili tek sorun, görevini oldukça dolaylı ve belirsiz bir şekilde yerine getirmesi.
İsviçre

3
Bash man sayfasında yukarıdakilerin ne anlama geldiğini açıklamaya bakmak isteyenler için, "Parametre Genişletme" bölümünü arayın ve sonra bu metin için: "Alt dize genişletme gerçekleştirmezken, aşağıda belirtilen formları kullanarak, unset veya null olan bir parametre [boş dize anlamına gelen 'null'. null veya unset, hiçbir şey değiştirilmez, aksi takdirde kelimenin genişletilmesi değiştirilir. "
David Tonhofer

2
@Swiss Bu ne belirsiz ne de dolaylı değil, deyimsel. Belki de aşina olmayan bir programcı için ${+}ve ${-}belirsizdir, ancak kabuğun yetkin bir kullanıcısı olacaksa, bu yapılara aşinalık önemlidir.
William Pursell

Bu seçeneği etkin olarak uygulamak istiyorsanız stackoverflow.com/a/20003892/14731 adresine bakın set -o nounset.
Gili

Bu konuda birçok test yaptım; çünkü senaryolarımdaki sonuçlar tutarsızdı. Ben bash -s v4.2 ve ötesinde " [ -v VAR ]" yaklaşımına halk bakışını öneririm .
olacak

38
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-z tanımsız değişkenler için de çalışır. Tanımsız ve tanımlanmış arasında bir ayrım yapmak için , burada listelenen veya burada daha net açıklamalar ile kullanılanları kullanırsınız .

En temiz yol, bu örneklerde olduğu gibi genişlemeyi kullanmaktır. Tüm seçeneklerinizi almak için kılavuzun Parametre Genişletme bölümünü kontrol edin.

Alternatif kelime:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

Varsayılan değer:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

Elbette bunlardan birini farklı şekilde kullanırsınız, 'varsayılan değer' yerine istediğiniz değeri koyar ve uygunsa genişletmeyi doğrudan kullanırsınız.


1
Ama ben dize "" olup olmadığını veya hiç tanımlanmamış olup olmadığını ayırt etmek istiyorum. Mümkün mü?
Setjmp

1
bu cevap bu iki vakayı birbirinden nasıl ayıracağımızı anlatıyor; daha fazla tartışma için sağladığı bash sss bağlantısını takip edin.
Charles Duffy


2
Tüm bu "hileler" için bash man sayfasında "Parameter Expansion" bölümüne bakın. Örneğin, varsayılan bir değer kullanmak için $ {foo: -default}, varsayılan değeri atamak için $ {foo: = default}, foo ayarlanmamışsa bir hata mesajı görüntülemek için $ {foo:? Hata mesajı}
Jouni K Seppänen

18

Gelişmiş bash komut dosyası kılavuzu, 10.2. Parametre Değiştirme:

  • $ {var + blahblah}: var tanımlanırsa, ifade için 'blahblah' yerine başka bir değer atanır
  • $ {var-blahblah}: var tanımlanmışsa, kendisi ikame edilir, başka 'blahblah' ikame edilir
  • $ {var? blahblah}: var tanımlanırsa, onun yerine kullanılır, aksi takdirde işlev bir hata mesajı olarak 'blahblah' ile birlikte bulunur.


program mantığınızı $ mystr değişkeninin tanımlanmış olup olmadığına dayandırmak için aşağıdakileri yapabilirsiniz:

isdefined=0
${mystr+ export isdefined=1}

şimdi isdefined = 0 ise değişken tanımsızdır, isdefined = 1 ise değişken tanımlanmıştır

Değişkenleri kontrol etmenin bu yolu yukarıdaki yanıttan daha iyidir, çünkü daha zarif, okunabilirdir ve bash kabuğunuz tanımsız değişkenlerin kullanımında hata yapacak şekilde yapılandırılmışsa (set -u), komut dosyası erken sona erer.


Diğer faydalı şeyler:

tanımlanmamışsa varsayılan değer $ mystr'a atanmışsa ve aksi halde bozulmadan bırakılırsa:

mystr=${mystr- 7}

bir hata mesajı yazdırmak ve değişken tanımsızsa işlevden çıkmak için:

: ${mystr? not defined}

$ Mystr içeriğinin tanımlanması durumunda komut olarak çalıştırılmaması için ':' kullandığım konusunda dikkatli olun.


Hakkında bilmiyordum? BASH değişkenleri için sözdizimi. Güzel bir yakalama.
İsviçre

Bu, dolaylı değişken referanslarıyla da çalışır. Bu geçirir: var= ; varname=var ; : ${!varname?not defined}bu son bulur: varname=var ; : ${!varname?not defined}. Ama set -uaynı şeyi daha kolay yapan iyi bir alışkanlıktır .
ceving

11

Testlerin özeti.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"

Bash iyi uygulama. Bazen dosya yollarında boşluklar vardır veya komut enjeksiyonuna karşı korumak için
k107

1
${var+x}Değişken isimlerinin içinde boşluk olmasına izin verilmesi dışında bunun gerekli olmadığını düşünüyorum .
Anne van Rossum

Sadece iyi uygulama değil; o oluyor gerekli olan [doğru sonuçlar elde etmek için. Eğer varayarlanmadan ya da boş, [ -n $var ]azaltır [ -n ]ise, bu çıkış durumu 0 sahiptir [ -n "$var" ]1. beklenen (ve doğru) çıkış durumu
chepner

5

https://stackoverflow.com/a/9824943/14731 daha iyi bir yanıt içeriyor (daha okunabilir olan ve set -o nounsetetkin durumdayken çalışan ). Kabaca şu şekilde çalışır:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi

4

Tanımlanmış bir değişkeni kontrol etmenin açık yolu:

[ -v mystr ]

1
Bunu man sayfalarının hiçbirinde bulamıyorum (bash veya test için) ama benim için işe yarıyor .. Bunun hangi sürümleri eklendiğini biliyor musunuz?
Ash Berlin-Taylor

1
Bourne Shell Builtins bölümünün arka ucundaki operatörden referans alınan Koşullu İfadeler'de belgelenmiştir . Ne zaman eklendiğini bilmiyorum, ancak ( 3.2.51'e dayalı ) Apple sürümünde değil , bu yüzden muhtemelen bir 4.x özelliğidir. testbashbash
Jonathan Leffler

1
Bu benim için işe yaramıyor GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu). Hata -bash: [: -v: unary operator expected. Buradaki bir yorum başına , çalışmak için en az bash 4.2 gerekir.
Acumenus

Ne zaman kullanılabilir olduğunu kontrol ettiğiniz için teşekkür ederiz. Daha önce çeşitli sistemlerde kullanmıştım, ama sonra birden işe yaramadı. Buraya meraktan geldim ve evet, elbette bu sistemdeki bash 3.x bash.
clacke

1

başka bir seçenek: "liste dizini dizinleri" genişletmesi :

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

bunun boş dizeye genişlediği tek zaman fooayarlanmadığı zamandır , böylece koşullu dizeyle kontrol edebilirsiniz:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

herhangi bir bash sürümünde kullanılabilir olmalıdır = 3.0


1

bu bisikleti daha da düşürmemek, ama eklemek istedim

shopt -s -o nounset

bir komut dosyasının üst kısmına ekleyebileceğiniz bir şeydir; bu, değişkenler komut dosyasının hiçbir yerinde bildirilmezse hata verir . Göreceğiniz mesaj şudur unbound variable, ancak diğerlerinin de belirttiği gibi boş bir dize veya boş değer yakalamaz. Herhangi bir değerin boş olmadığından emin olmak için, bir değişkeni genişletildiğinde ${mystr:?}, dolar işareti genişletmesi olarak da bilinir ve hata verir parameter null or not set.


1

Bash Bash hakkında bilgi sunan yetkili kaynağıdır.

Aşağıda, bir değişkenin var olup olmadığını görmek için test etme örneği verilmiştir:

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

( Bölüm 6.3.2'den .)

Açık sonra boşluk unutmayın [ve önce ]olduğu isteğe bağlı değil .


Vim kullanıcıları için ipuçları

Aşağıdaki gibi birkaç beyanları vardı bir senaryo vardı:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

Fakat mevcut değerlere ertelemelerini istedim. Bu yüzden onları şöyle görünmeleri için yeniden yazdım:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

Hızlı bir regex kullanarak vim bu otomatikleştirmek başardı:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

Bu, ilgili satırları görsel olarak seçip sonra yazarak uygulanabilir :. Komut çubuğu ile önceden doldurulur :'<,'>. Yukarıdaki komutu yapıştırın ve enter tuşuna basın.


Vim'in bu sürümünde test edilmiştir:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com

Windows kullanıcıları farklı satır sonları isteyebilir.


0

İşte bir değişken tanımlanmış olup olmadığını kontrol etmek için çok daha açık bir yol olduğunu düşünüyorum:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

Aşağıdaki gibi kullanın:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi

1
Bir işlev yazmak kötü bir fikir değildir; çağırmak grepkorkunç. Not bashdestekleri ${!var_name}adı belirtilen bir değişkenin değerini almanın bir yolu olarak $var_name, yani name=value; value=1; echo ${name} ${!name}verimleri value 1.
Jonathan Leffler

0

Tanımlanmamış değişkeni test etmek için daha kısa bir sürüm basitçe şöyle olabilir:

test -z ${mystr} && echo "mystr is not defined"

-3

herhangi bir argüman olmadan çağrı kümesi .. tanımlanan tüm vars çıktılar ..
Listede son olanlar senaryonuzda tanımlanan olanlar olacaktır ..
böylece çıktı ne şeyler tanımlanmış ve olmayan ne anlamaya bir şey boru olabilir


2
Bunu aşağı oylamadım, ama oldukça küçük bir somunu kırmak balyozla ilgili bir şey.
Jonathan Leffler

Aslında bu cevabı kabul edilen cevaptan daha iyi seviyorum. Bu cevapta ne yapıldığı açık. Kabul edilen cevap cehennem gibi sakat.
İsviçre

Bu cevap, istenen bir şeyden ziyade "set" in uygulanmasının bir yan etkisi gibi görünüyor.
Jonathan Morgan
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.