Neden echo için değişken teklif etmem gerekiyor ama olmasın?


26

Değişkenleri genişletmek için çift tırnak kullanmanız gerektiğini okudum, örneğin

if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi

beklendiği gibi çalışacak

if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi

boş $test okolsa bile her zaman söyleyeceğim $test.

ama o zaman neden alıntılara ihtiyacımız yok echo $test?


2
Argüman olarak kullanılacak bir değişkeni alıntılamazsanız echo, fazladan boşluklar ve yeni satırlar kaldırılır.
Ürdün

Yanıtlar:


36

Tüm liste bağlamlarında değişkenlerin etrafında her zaman tırnaklara ihtiyacınız vardır ; bu, değişkeni tırnaksız bırakmaktan üç yan etki istemediğiniz sürece, değişkenin birden çok değere genişletilebileceği yerdir.

Liste bağlamları gibi basit komutlara argümanları şunlardır [ya echo, for i in <here>... değişkenler de alıntı gereken başka bağlam vardır ödevler diziler için. En iyisi, yapmamak için çok iyi bir nedeniniz olmadığı sürece değişkenleri alıntılamaktır.

Split + glob operatörü olarak tırnak (liste bağlamında) yokluğunu düşünün .

Sanki echo $testöyleydi echo glob(split("$test")).

Kabuk davranışı çoğu insan için kafa karıştırıcıdır, çünkü diğer birçok dilde, sabit dizgilere, puts("foo")değişkenler gibi (değişkenler gibi puts(var)) tırnaklar koyarsınız ; hantal olurdu, sen echo test, gerek yok "echo" "test". Kabukta, tırnaklar başka bir şey için kullanılır: bazı karakterlerin bazı özel anlamlarını önler ve / veya bazı genişlemelerin davranışını etkiler.

Gelen [ -n $test ]veya echo $testkabuk bölecek $test(varsayılan olarak boşlukları üzerine) ve ardından dosya adı üretimi yapan (tümünü genişlet *, '?' ... desenleri eşleştirme dosyalar listesine) ve daha sonra bağımsız değişkenlerin o listeyi geçmesi [veya echokomutlar .

Yine, bunu düşün "[" "-n" glob(split("$test")) "]". Eğer $testboşsa veya sadece kuru sıkı (SPC, sekme, nl) içerecek şekilde, daha sonra bölünmüş + glob operatörü, boş bir listesini döndürecektir [ -n $test ]olacak "[" "-n" "]""-n" boş dize olup olmadığını wheter kontrol etmek için bir test olan. Ama $test"*" ya da "= foo" olsaydı neler olacağını düşünün

Olarak [ -n "$test" ], [dört bağımsız değişken geçirilir "[", "-n", ""ve "]"ne istediğimizi olan (tırnaklar olmadan).

O olsun echoya [hiç fark etmez, sadece o var echoaynı şey çıkışları boş bir argüman ya da hiç argüman geçirilen olsun.

Komut ve yapı hakkında daha fazla ayrıntı için benzer bir soruya bu cevaba bakınız .[[[...]]


7

@ h3rrmiller'ın cevabı neden if(veya daha doğrusu, [/ test) için tırnaklara ihtiyaç duyduğunuzu açıklamakta iyidir , ancak aslında sorunuzun yanlış olduğunu kabul ediyorum.

Aşağıdaki komutları deneyin, ne demek istediğimi anlayacaksınız.

export testvar="123    456"
echo $testvar
echo "$testvar"

Tırnak işaretleri olmadan değişken değiştirme, ikinci komutun genişlemesine neden olur:

echo 123    456

ve çoklu alanlar tek bir birine daraltılır:

echo 123 456

Tırnaklarla, boşluklar korunur.

Bunun nedeni sizin (yani parametre geçirilen olup olmadığını bir parametre alıntı yaparken echo, testya da başka bir komut), bu parametrenin değeri olarak gönderilir tek komuta değeri. Alıntı yapmazsanız, kabuk, her parametrenin nerede başladığını ve bittiğini belirlemek için normal boşluk aramasını yapar.

Bu, aşağıdaki (çok çok basit) C programı ile de gösterilebilir. Komut satırında aşağıdakileri deneyin (boş bir dizinde bir şeyin üzerine yazma riskini almamak için yapmak isteyebilirsiniz).

cat <<EOF >paramtest.c
#include <stdio.h>
int main(int argc, char **argv) {
  int nparams = argc-1; /* because 1 parameter means only the executable's name */
  printf("%d parameters received\n", nparams);
  return nparams;
}
EOF
cc -o paramtest paramtest.c

ve sonra...

./paramtest 123 456
./paramtest "123 456"
./paramtest 123   456
./paramtest "123   456"

Koşu sonrasında paramtest, $?parametrelerin o geçirildi (ve bu sayı basılacak) sayısını tutacaktır.


2

Bütün bunlar bir program yürütülmeden önce kabuğun hattı nasıl yorumladığına bağlı .

Çizgi okursa echo I am $USER, kabuk bunu genişler echo I am blrflve echometnin kökeni bir sabit veya değişken genişleme olup olmadığı ipucu yok. Benzer şekilde, bir satır okursa echo I am $UNDEFINED, kabuk $UNDEFINEDhiçbir şeye genişlemez ve yankının argümanları olur I amve bu onun sonudur. Yana echoeserleri sadece hayır argümanlar ile ince, echo $UNDEFINEDtamamen geçerlidir.

İle Sorununuz ifgerçekten sahip olmadığı if, çünkü ifne olursa olsun programı ve argümanlar sadece çalışır onu takip ve yürütür thenprogram çıkar eğer kısmını 0(veya elseparça varsa ve program dışı çıkar 0):

if /bin/true ; then echo True dat. ; fi
if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi

Eğer kullandığınız zaman if [ ... ]bir karşılaştırma yapmak için, kabuk içinde bulunan temel öğeler kullanmıyoruz. Gerçekten de kabuğa son bir argüman gerektiren [çok küçük bir süperset olan bir programı çalıştırması talimatını veriyorsunuz . Her iki program da test koşulu gerçekleşirse veya çıkmazsa çıkar.test(1)]01

Bazı testlerin bir değişken tanımsızken kırılmasının nedeni, bir değişken testkullandığınızı görmemesidir. Ergo, [ $UNDEFINED -eq 2 ]kırılıyor çünkü kabuk onunla birlikte yapıldığında, tüm testargümanlar görüyor -eq 2 ], bu geçerli bir test değil. Tanımlanmış bir şeyle yaptıysanız [ $DEFINED -ne 0 ], bu işe yarar, çünkü kabuk onu geçerli bir teste genişletir (örn 0 -ne 0.).

foo $UNDEFINED barİki argümana genişleyen anlamsal bir fark var ( foove bar) çünkü $UNDEFINEDadına kadar yaşadı. Bunu üç argümana foo "$UNDEFINED" bargenişleyen ( boş bir dize ve `bar) ile karşılaştırın. Alıntılar, kabuğu aralarında bir şey olup olmadığına dair bir argüman olarak yorumlamaya zorlar.foo


0

Tırnaklar olmadan $testbirden fazla kelime olacak şekilde genişleyebilirler; bu nedenle, [komut içindeki her anahtar , tırnakların yaptığı şeyi (ne olursa olsun $testbir argümana ne yaparsa onu yapar) bir argüman beklediğinden sözdizimini kırmamak için alıntı yapılması gerekir.

Bir değişkeni genişletmek için alıntılara gerek duymamanın echonedeni, bir argüman beklememesidir. Sadece söylediklerini yazdıracak. Yani $test100 kelimeye genişlese bile yankı hala basacaktır.

Bash Pitfalls'a bir göz atın


evet ama neden buna ihtiyacımız yok echo?
CharlesB

@ CharlesB için tırnak gerekir echo. Aksini düşündüren ne?
Gilles 'SO- kötülük yapmayı bırak'

Onlara ihtiyacım yok, yapabilirim echo $testve işe yarıyor ($ testinin değerini
çıkarır

1
@CharlesB Herhangi bir yerde birden fazla boşluk içermiyorsa, yalnızca $ test değerini verir. Nedenini gösteren bir programı benim cevabımda deneyin.
CVn

0

Alıntı yapılmadığında boş parametreler kaldırılır:

start cmd:> strace -e trace=execve echo foo $bar baz
execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

start cmd:> strace -e trace=execve echo foo "$bar" baz
execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0

Çağrılan komut, kabuk komut satırında boş bir parametre olduğunu görmüyor. Görünüşe göre [ile hiçbir şey yapmadan -n için 0 döndürülür. Neden hiç.

Alıntı yapmak, bazı durumlarda yankı için de fark yaratır:

var='*'
echo $var
echo "$var"

var="foo        bar"
echo $var
echo "$var"

2
Değil echo, kabuk. Aynı davranışı da görürsünüz ls. touch '*'Maceracı hissediyorsanız biraz zaman deneyin . :)
bir CVn

Bu sadece 'if [...] `davasında bir fark olmadığı için ifadelerdir. [özel bir kabuk komutu değil. Bu, alıntı yapılmasının gerekli olmadığı [[(bash) 'den) farklıdır.
Hauke,
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.