Bir değişkenin boş veya sadece boşluk içerip içermediğini nasıl test edebilirim?


240

Aşağıdaki bash sözdizimi paramboş olmadığını doğrular :

 [[ !  -z  $param  ]]

Örneğin:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

Çıktı yok ve para cezası.

Ancak, parambir (veya daha fazla) boşluk karakteri dışında boş olduğunda , durum farklıdır:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

"Sıfır değilim" çıktı.

Testi yalnızca boşluk karakterleri içeren değişkenleri boş olarak değerlendirecek şekilde nasıl değiştirebilirim?


4
Gönderen man test: -z STRING - the length of STRING is zero. Tüm boşlukları kaldırmak istiyorsanız $param, kullanın${param// /}
dchirikov

6
WOWtrim() , herhangi bir * nix'te yerleşik olan basit bir fonksiyon değil mi? Çok basit bir şey elde etmek için çok farklı kesmek ...
ADTC

6
@ ADTC $ (sed 's / ^ \ s + | \ s + $ // g' <<< $ string) bana göre basit görünüyor. Neden tekerleği yeniden icat ettin?
jbowman

3
Çünkü daha parlak olabilirdi
Inversus,

1
Sadece bunun [[ ! -z $param ]]eşdeğer olduğuna dikkat edin test ! -z $param.
Noah Sussman

Yanıtlar:


292

Öncelikle, -ztestin açıkça şunlar için olduğunu unutmayın :

dizenin uzunluğu sıfır

Yani, sadece boşluk içeren bir dize olmalıdır edilir değil altında doğru olması -zbunun sıfır olmayan bir uzunluğa sahiptir çünkü.

İstediğiniz şey, kalıp değiştirme parametresi genişletmesini kullanarak değişkenden boşlukları kaldırmaktır :

[[ -z "${param// }" ]]

Bu, paramdeğişkeni genişletir ve desenin tüm eşleşmelerini (tek bir boşluk) hiçbir şeyle değiştirir, bu nedenle yalnızca içinde boşluk olan bir dize boş bir dizeye genişler.


Nasıl çalıştığını asıl mesele yani ${var/pattern/string}ilk en uzun maç değiştirir patternile string. Ne zaman patternbaşlar /(yukarıdaki gibi) o zaman değiştirir tüm maçları. Değişim boş olduğundan, finali /ve stringdeğeri atlayabiliriz :

$ {Parametre / kalıp / dizge}

Desen sadece Dosyaismi gibi bir kalıp üretmek için genişletilir. Parametre genişletilir ve değere karşı en uzun örüntü eşleşmesi dizeyle değiştirilir . Eğer desen '/' ile başlar, tüm maçları deseni ile değiştirilir dize . Normalde sadece ilk eşleşme değiştirilir. ... Dize boş ise, kalıp eşleşmeleri silinir ve / sonraki kalıp ihmal edilebilir.

Bütün bunlardan sonra, ${param// }tüm boşlukları silmek için son buluruz .

kshİçinde bulunduğu ( mevcut olduğu yerde) zshve bashsözdiziminin POSIX olmadığını ve shkomut dosyalarında kullanılmaması gerektiğini unutmayın .


kullandıkları seddediler ... sadece echo
söyledikleri

1
Hmm. Eğer durum bu ise ${var/pattern/string}o zaman olmamalıdır [[ -z ${param/ /} ]]dize olmak sonra desen ve hiçbir şey olmak eğik çizgiler arasında boşluk?
Jesse Chisholm

@JesseChisholm "Değiştirme boş olduğundan, son / ve dize değerini atlayabiliriz"; " Dize boşsa, kalıp eşleşmeleri silinir ve / sonraki kalıp ihmal edilebilir."
Michael Homer,

6
@MichaelHomer Cevap [[ -z "${param// }" ]]kesinlikle patternboş ve replacement stringtek bir boşluk gibi görünüyor . AH! Şimdi if pattern begins with a slashanlıyorum o kısmı özledim. bu nedenle desen / ve dizi bırakılır ve bu nedenle boştur. Teşekkürler.
Jesse Chisholm

bash notu: -o nounset (set -u) kümesinde çalışıyorsa ve param ayarlanmamışsa (boş veya boşluk dolu değil), o zaman bu sınırsız bir değişken hatası oluşturur. (set + u; .....) bu testi muaf tutar.
Brian Chrisman,

31

Bir dizginin yalnızca yetkili bir kümedeki karakterleri içerdiğini kontrol etmenin kolay yolu yetkisiz karakterlerin varlığını test etmektir. Bu nedenle, dizenin yalnızca boşluk içerip içermediğini test etmek yerine, dizenin boşluktan başka bir karakter içerip içermediğini test edin. Bash, ksh veya zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

“Yalnızca boşluklardan oluşur”, boş (veya ayarlanmamış) bir değişkenin durumunu içerir.

Herhangi bir boşluk karakteri için test yapmak isteyebilirsiniz. Kullanım [[ $param = *[^[:space:]]* ]]açık ne olursa olsun, örneğin test etmek istediğiniz boşluk karakterlerinin listesi yerel ayarları veya kullanmayı [[ $param = *[$' \t\n']* ]]uzay, sekme veya Yenisatır için test etmek.

İpin =içini bir desene göre eşleştirmek [[ … ]], bir ksh uzantısıdır (bash ve zsh da bulunur). Herhangi bir Bourne / POSIX tarzında, caseyapıyı dizeyle kalıpla eşleştirmek için kullanabilirsiniz . Standart kabuk desenlerinin , çoğu normal ifade sözdizimindeki gibi !değil, karakter kümesini olumsuzlamak için kullandığını unutmayın ^.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

Boşluk karakterlerini test etmek için, $'…'sözdizimi ksh / bash / zsh; Bu karakterleri tam anlamıyla betiğinize ekleyebilirsiniz (bir yeni satırın ters eğik çizgi + newline hiçbir şeye genişlemediğinden, tırnak içinde olması gerekeceğini unutmayın) veya

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac

Bu neredeyse mükemmel - ama yine de sorulan soruyu cevaplamıyor. Şu anda size açık olmayan veya boş bir kabuk değişkeni ile yalnızca boşluk içeren bir fark arasındaki farkı söyleyebilir . Yani - Benim öneriyi kabul edecek olursa henüz açık bir şekilde ayarlanabilir olduğu için bir kabuk değişkeni test başka bir cevap ile convered değil param genişleme formu katacak ${var?not set}- Değişken senin eşleşmesi sağlamak olacağını testi ve kurtulamamacasına geçen *) casekesin etkileyecektir Mesela cevap
mikeserv

Düzeltme - Bu, aslında, olur başlangıçta sorulan soruya cevap ama çünkü temelde sorunun anlamını değiştirir ve bu nedenle cevap geçersiz kıldığını şekilde düzenlendi.
mikeserv

İhtiyacın [[ $param = *[!\ ]* ]]var mksh.
Stéphane Chazelas

20

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

Ayırt etmek boş , dışı boş , boş , unset :

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]yatay boşluk karakterleri içindir (ASCII'de boşluk ve sekme, ancak yerel ayarlarınızda muhtemelen birkaç tane daha vardır; bazı sistemler ayrılmaz alanı içerecektir (mümkün olduğunda), bazıları olmaz). Siz de (yeni satır veya form-besleme gibi) dikey boşluk karakterleri istiyorsanız, yerini [:blank:]ile [:space:].


POSIXly, (muhtemelen daha iyi) bir çizgi ile çalışacağı için idealdir.
Ken Sharp,

zshsorun var gibi görünüyor [![:blank:]], zammı :bgeçersiz değiştiricidir. Gelen shve kshtaklit, bu olay yükseltmek için bulunamadı[
cuonglm

@cuonglm, ne denedin? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'benim için sorun yok. Bulunmayan olay yalnızca etkileşimli mermiler için olur.
Stéphane Chazelas

@ StéphaneChazelas: Ah, doğru, etkileşimli mermilerle denedim. :bGeçersiz değiştirici biraz garip.
cuonglm

1
@FranklinYu, OS / X shedilir bashile inşa --enable-xpg-echo-defaultve --enable-strict-posix-defaultböylece (içerir uyumlu Unix olması için echo '\t'bir sekme çıkışı).
Stéphane Chazelas

4

İyi bir betik dilinde bir betik yerine bir kabuk betiği yazmak için geriye kalan tek neden , aşırı taşınabilirliğin önemli bir endişe kaynağı olmasıdır. Miras /bin/sh, sahip olacağınızdan emin olabileceğiniz tek şey, ancak örneğin Perl, örneğin, platformlar arası Bash'ten daha olası. Bu nedenle, gerçekten evrensel olmayan özellikleri kullanan kabuk komut dosyalarını asla yazmayın - ve bazı özel Unix satıcılarının POSIX.1-2001'den önce kabuk ortamlarını donduğunu unutmayın .

Bu testi yapmanın taşınabilir bir yolu var, ancak kullanmanız gerekiyor tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(Varsayılan değer $IFS, uygun bir şekilde boşluk, sekme ve yeni satırdır.)

( printfYerleşik, kendiliğinden taşınabilirdir, ancak ona güvenmek, sizin hangi türünün bulunduğuna karar echovermekten çok daha az güçtür .)


1
Bu biraz sarstı. Bir dize belirli karakterler içerip içermediğini test etmek için her zamanki taşınabilir yoludur ilecase .
Gilles

@Gilles caseBoş olan veya yalnızca boşluk karakterleri içeren bir dize algılamak için bir küre yazmak mümkün olduğunu sanmıyorum . (Bir regexp ile kolay olurdu, ancak case
regexps yapmaz

1
Cevabımda örnek olarak boşluklar verdim çünkü sorulan soru buydu. Bu boşluk karakteri test etmek için aynı derecede kolaydır: case $param in *[![:space:]]*) …. IFSKarakterleri test etmek istiyorsanız , deseni kullanabilirsiniz *[$IFS]*.
Gilles

1
@mikeserv Gerçekten ciddi bir savunma senaryosunda, IFS'yi <space><tab><newline>başlangıçta açıkça ayarladınız ve bir daha asla dokunmuyorsunuz . Bunun bir dikkat dağıtıcı olacağını düşünmüştüm.
zwol

1
Desenlerdeki değişkenler ile ilgili olarak, tüm Bourne versiyonlarında ve tüm POSIX mermilerinde çalıştığını düşünüyorum; vaka modellerini tespit etmek ve değişken genişletmeyi kapatmak için ekstra kod gerekir ve bunu yapmak için bir neden düşünemiyorum. Ben benim de bunun örneklerinin bir çift var .profilebirçok Bourne kabukları ile gelmiştir. Elbette bazı varsayılan olmayan değerlere sahipse (boş (veya ayarlanmamış), içeren veya ile başlayan ) [$IFS]amaçlananı yapmaz . IFS]!^
Gilles

4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi

bu, sadece bir boşluktan değil, herhangi bir boşluk karakterinden kurtulur, değil mi? teşekkürler
Alexander Mills,

0

Değişkenin boş olup olmadığını veya boşluk içerdiğini test etmek için bu kodu da kullanabilirsiniz:

${name:?variable is empty}

2
Bence "ya da boşluk içeren" doğru olmadığı için cevabınız azaldı.
Bernhard

Dikkatli olun - bu mevcut kabuğu öldürür. Bir içine koymak daha iyi (: $ {subshell?}). Ayrıca - stderr'e yazacaktır - muhtemelen yeniden yönlendirilmek istersiniz. Ve en önemlisi , ayarlanmamış veya boş değilse değişkenin gerçek değerini değerlendirir. İçeride bir komut varsa, koştun. Bu testi yapmak en iyisidir :.
mikeserv

kabuğu nasıl öldürecek. ve u da tanımlamak öncelikle bu test edebilirsiniz name=aaaasonra bu komutu çalıştırmak echo ${name:?variable is empty}değişken adının değerini yazdırmak ve şimdi bu komutu çalışacaktır echo ${name1:?variable is empty}onu yazdırır değişken boş: isim1: -sh
pratik

Evet - echo ${name:?variable is empty}ya $nameboş olmayan değeri değerlendirir ya da kabuğu öldürür. Yani aynı sebepten dolayı siz prompt > $randomvarde yapmamalısınız ${var?}- eğer orada bir emir varsa, muhtemelen kaçacak - ve ne olduğunu bilmiyorsunuz. Etkileşimli bir kabuğun çıkması gerekmez - bu yüzden sizinki yoktur. Yine de, bazı testler görmek istiyorsanız, burada birçoğu var. . Bu çok uzun sürmedi ...
mikeserv

0

Bir değişkenin ayarlanmamış olup olmadığını test etmek için, boş (boş değere sahip) veya yalnızca boşluk içeriyor:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

Açıklama:

  • ${param%%[! ]*}Kaldırılan alanın ilkinden boşluk olmayana kadar genişler .
  • Bu sadece baştaki boşlukları bırakıyor.
  • Bir sonraki genişleme, öndeki boşlukları değişkenden kaldırır.
  • Sonuç boşsa, değişken başlamak için boştu.
  • Veya değişken boş değilse, öndeki boşlukları çıkarmak onu boş bıraktı.
  • Sonuç boşsa -z, eko empty.

Yukarıdaki POSIX uyumludur ve herhangi bir Bourne soyundan çalışır.

Bunu da sekmelere genişletmek istiyorsanız, açık bir sekme ekleyin:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

veya kaldırmanız gereken karakterleri içeren bir değişken kullanın:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

veya bazı POSIX "karakter sınıfı ifadeleri" kullanabilirsiniz (boşluk, boşluk ve sekme anlamına gelir):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

Fakat bu mksh, lksh ve posh'de başarısız olacaktır.


-1

Bunu dene:

$ [[ -z \`echo $n\` ]] && echo zero

zero
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.