Çift alıntı ne zaman gereklidir?


120

Eski tavsiyeler $VARIABLE, en azından birinin kabuğun tek bir öğe olarak yorumlanmasını isterse, içerdiği herhangi bir ifadeyi ikiye katlamaktı , aksi halde içeriğindeki boşluklar $VARIABLEkabuğu fırlatırdı.

Bununla birlikte, kabukların daha yeni versiyonlarında artık çift alıntı yapılmasının gerekmediğini (en azından yukarıda tarif edilen amaç için) gerekli olduğunu biliyorum. Örneğin, içinde bash:

% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory

Öte zshyandan, aynı üç komut başarılı. Bu nedenle, bu deneye dayanarak bash, birinin içinde çift tırnakların içini dışlayabildiği [[ ... ]], ancak içinde [ ... ]ya da komut satırı argümanlarında bulunmadığı , oysa zshiçinde tüm bu durumlarda çift tırnakların göz ardı edilebileceği görülmektedir.

Ancak, yukarıdaki gibi fıkra örneklerinden genel kurallar çıkarılması şanssız bir öneridir. Çifte alıntı yapılması gerektiğinin bir özetini görmek güzel olurdu. Ben öncelikle ilgi zsh, bashve /bin/sh.


10
Zsh'deki gözlediğiniz davranışınız ayarlara bağlıdır ve SH_WORD_SPLITseçenek tarafından etkilenir .
Ulrich Dangel


3
Bir kenara - tümü büyük harf değişken adları, işletim sistemi ve kabuk için anlam taşıyan değişkenler tarafından kullanılır; POSIX spesifikasyonu, uygulama tanımlı değişkenler için küçük harf adlarının kullanılmasını açıkça önerir. (Belirtilen spesifikasyon özellikle ortam değişkenlerine odaklanırken, ortam değişkenleri ve kabuk değişkenleri bir ad alanını paylaşır: Zaten bir ortam değişkeni tarafından kullanılan bir adla bir kabuk değişkeni yaratmaya çalışmak ikincisinin üzerine yazar). Bkz pubs.opengroup.org/onlinepubs/009695399/basedefs/... , dördüncü paragraf.
Charles Duffy

Yanıtlar:


128

İlk önce, zsh'ı diğerlerinden ayırın. Eski ve modern kabukları ilgilendirmez: zsh farklı davranır. Zsh tasarımcıları geleneksel kabukları ile uyumsuz hale getirmeye karar verdiler (Bourne, ksh, bash), ancak kullanımı daha kolay.

İkincisi, her zaman çift tırnak kullanmak, gerektiğinde hatırlamaktan çok daha kolaydır. Çoğu zaman ihtiyaç duyulur, bu yüzden ihtiyaç duyulduğunda değil, ihtiyaç duyulmadığında öğrenmeniz gerekir.

Özet olarak, bir kelime veya kalıp listesinin beklendiği yerde çift tırnak işareti gerekir . Ayrıştırıcı tarafından işlenmemiş bir dizgenin beklendiği bağlamlarda isteğe bağlıdır.

Tırnaksız ne olur?

Çift tırnak olmadan, iki şey olduğunu unutmayın.

  1. İlk olarak, genişlemenin sonucu (gibi bir parametre değişiminin değişkeni değeri ${foo}veya benzeri bir komut değişiminin komutu çıktısı $(foo)), boşluk içerdiği her yerde kelimelere ayrılır.
    Daha doğrusu, genişlemenin sonucu, IFSdeğişkenin değerinde görünen her karakterde bölünür (ayırıcı karakter). Bir ayırıcı karakter dizisi boşluk içeriyorsa (boşluk, sekme veya yeni satır), boşluk tek bir karakter olarak sayılır; öndeki, takip eden veya tekrarlanan boşluk olmayan ayırıcılar boş alanlara neden olur. Örneğin, ile IFS=" :", :one::two : three: :four önce boş alanlar üretir onearasında, oneve twobir aralığa, ve (tek bir) threeve four.
  2. Bölünmeden kaynaklanan her alan karakterlerden birini içeriyorsa glob (joker desen) olarak yorumlanır \[*?. Bu model bir veya daha fazla dosya adıyla eşleşiyorsa, model eşleşen dosya adları listesi ile değiştirilir.

Bir tırnaksız değişken genişleme $foohalk dilinde aksine “bölünmüş + glob operatörü” olarak bilinen "$foo"sadece değişkenin değerini aldığı foo. Aynı şey komut değiştirme için de geçerli: "$(foo)"bir komut değiştirme işlemi, $(foo)ardından split + glob adlı bir komut değiştirme işlemi.

Çifte alıntıyı ihmal edebileceğiniz yer

İşte, Bourne tarzı bir kabukta düşünebildiğim tüm değişkenler, değişken veya komut yerine koyma işlemi olmadan çift tırnak işareti yazabilirsiniz ve değer tam anlamıyla yorumlanır.

  • Bir ödevin sağ tarafında.

    var=$stuff
    a_single_star=*

    exportBir anahtar kelime değil, sıradan bir yerleşik olduğundan, sonra çift tırnak işaretine ihtiyacınız olduğunu unutmayın . Bu sadece kısa çizgi, zsh (öykünme modunda), yash veya posh gibi bazı kabuklarda doğrudur; bash ve ksh, ikisine de exportözel muamele yapar .

    export VAR="$stuff"
  • Bir caseaçıklamada.

    case $var in 

    Bir vaka düzeninde çift tırnak kullanmanız gerektiğini unutmayın. Sözcük bölme, bir vaka düzeninde gerçekleşmez, ancak bir alıntı olmayan değişken değişkeni bir dize olarak yorumlanırken, alıntılanmamış bir değişken bir model olarak yorumlanır.

    a_star='a*'
    case $var in
      "$a_star") echo "'$var' is the two characters a, *";;
       $a_star) echo "'$var' begins with a";;
    esac
  • İkili dirsek içinde. Çift köşeli ayraçlar kabuk özel sözdizimidir.

    [[ -e $filename ]]

    Sağ tarafında: Bunun dışında bir desen veya normal ifade beklenen çift tırnak gerekiyor =ya ==ya !=ya =~.

    a_star='a*'
    if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
    elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
    fi

    Tek parantez içinde her zamanki gibi çift tırnak işareti kullanmanız gerekir, [ … ]çünkü bunlar sıradan kabuk sözdizimidir (çağrılan bir komuttur [). Bkz tek veya çift parantez

  • Etkileşimli olmayan POSIX kabuklarında yeniden yönlendirme (değil bash, ne de ksh88).

    echo "hello world" >$filename

    Bazı kabuklar, etkileşimli olduğunda, değişkenin değerini joker desen olarak görür. POSIX etkileşimli olmayan kabuklarda davranışı yasaklar, ama ((sözde) POSIX gibi bulunca dahil olmak üzere birkaç (POSIX modunda hariç) bash dahil kabukları ve ksh88 shSolaris gibi bazı ticari Unix'lerde arasında) hala orada (bunu bashda teşebbüs etmez bölme bu sürece ve yönlendirme başarısız bölünmüş globbing + tam olarak bir kelimeyle sonuçları makul bir sürede yönlendirmeler hedeflerini alıntı daha iyidir, o yüzden) shbir dönüştürmek istediğiniz durumda komut bashnerede bir sistem üzerinde bir gün komut dosyası veya koşmak sholduğunu o noktada uyumlu olmayan veya etkileşimli mermilerden kaynaklanmış olabilir .

  • Bir aritmetik ifadenin içinde. Aslında, bir değişkenin aritmetik bir ifade olarak ayrıştırılması için tırnak işaretleri bırakmanız gerekir.

    expr=2*2
    echo "$(($expr))"

    Bununla birlikte, POSIX'in gerektirdiği (!?) Çoğu kabukta kelime bölmeye tabi olduklarından, aritmetik genişlemenin etrafındaki tırnaklara ihtiyacınız var.

  • Bir ilişkisel dizi alt komutunda.

    typeset -A a
    i='foo bar*qux'
    a[foo\ bar\*qux]=hello
    echo "${a[$i]}"

Belirtilmeyen bir değişken ve komut değiştirme bazı nadir durumlarda faydalı olabilir:

  • Değişken değer veya komut çıktısı glob kalıplarının bir listesinden oluştuğunda ve bu kalıpları eşleşen dosyalar listesine genişletmek istediğinizde.
  • Değerin herhangi bir joker karakter içermediğini bildiğiniz zaman, bu $IFSdeğiştirilmemiş ve boşluk karakterlerine bölmek istiyorsunuz.
  • Belirli bir karakterde bir değer bölmek istediğinizde: ile globbing devre dışı set -fset, IFSardından genişleme yapmak, ayırıcı karakteri (ya da boşluktan bölmek yalnız bırakın).

zsh

Zsh'de, birkaç istisna dışında çoğu zaman çift tırnak işaretini çıkartabilirsiniz.

  • $varhiçbir zaman birden fazla kelimeye genişlemez, ancak değeri varboş dize ise boş listeye (tek, boş bir kelime içeren bir listenin aksine) genişler . Kontrast:

    var=
    print -l $var foo        # prints just foo
    print -l "$var" foo      # prints an empty line, then foo

    Benzer şekilde, "${array[@]}"dizinin tüm öğelerine $arraygenişlerken , yalnızca boş olmayan öğelere genişler.

  • @Parametre genişletme bayrak bazen bütün ikame sonuna çift tırnak gerektirir: "${(@)foo}".

  • Komut ikame edilmemişse alan bölünmesine uğrar: değiştirilmemiş iki satırlı dizgeyi echo $(echo 'a'; echo '*')yazdırırken a *(tek boşlukla) echo "$(echo 'a'; echo '*')"yazdırır. "$(somecommand)"Komutun çıktısını tek bir kelimeyle almak için kullanın , son satırları gönderin. "${$(somecommand; echo _)%?}"Son satırsonları dahil olmak üzere komutun tam çıktısını almak için kullanın . "${(@f)$(somecommand)}"Komutun çıktısından bir dizi satır almak için kullanın .


Aslında, bir değişkenin aritmetik bir ifade olarak ayrıştırılması için tırnak işaretleri bırakmanız gerekir. Neden örneğinizi alıntılarla çalışmasını sağlayabiliyorum:echo "$(("$expr"))"
Cyker

Şöyle man bashyazıyor: İfade çift tırnak içine alınmış gibi ele alınır, ancak parantez içindeki çift tırnak işareti özel olarak ele alınmaz.
Cyker,

4
Ayrıca, ilgi herkes için, biçimsel isimleri bölünmüş + topak olan sözcüklere ayırma ve yol adı genişleme .
Cyker,

3
Bilginize - StackOverflow üzerinden, bir argümandan alıntı yapmamak için bu cevabın içinde "ham dizgenin beklendiği durumlarda isteğe bağlı" dili seçti echo. Dili daha da açık hale getirmeye çalışmakta fayda var (“çözümleyici tarafından ham bir dize beklendiğinde", belki de?)
Charles Duffy

2
@CharlesDuffy Ugh, bu yanlış okumayı düşünmemiştim. “Nerede” yi “ne zaman” olarak değiştirdim ve cümlenizi önerdiğiniz gibi güçlendirdim.
Gilles,
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.