Ne zaman bir kabuk değişkeni tırnak içine alınır?


184

Birisi bir kabuk betiğindeki değişkenlerin etrafına tırnak işaretleri sarmam gerekip gerekmediğini söyleyebilir mi?

Örneğin, aşağıdakiler doğrudur:

xdg-open $URL 
[ $? -eq 2 ]

veya

xdg-open "$URL"
[ "$?" -eq "2" ]

Ve eğer öyleyse, neden?



Bu soru birçoğu değişkenlerle ilgili olmayan bir çok kopya alır, bu yüzden "değişken" yerine "değer" e geri döndüm. Umarım bu daha fazla kişinin bu konuyu bulmasına yardımcı olur.
üçlü

1
@codeforester Geri döndürülen düzenlemenin nesi var?
tripleee


Yanıtlar:


131

Genel kural: boş veya boşluk (veya gerçekten herhangi bir boşluk) veya özel karakterler (joker karakterler) içeriyorsa alıntı yapın. Dizeleri boşluklarla tırnak içine almamak kabuğun tek bir argümanı birçok kişiye bölmesine neden olur.

$?sayısal bir değer olduğundan tırnak işareti gerektirmez. İhtiyaç $URLduyup duymayacağı, orada neye izin verdiğinize ve boşsa hala bir argüman isteyip istemediğinize bağlıdır.

Her zaman dizeleri alışkanlık dışında teklif etme eğilimindeyim çünkü bu şekilde daha güvenlidir.


2
"Boşluklar" ın gerçekten "herhangi bir boşluk" anlamına geldiğine dikkat edin.
William Pursell

4
@Cristian: Değişkende neler olabileceğinden emin değilseniz, alıntı yapmak daha güvenlidir. Paxdiablo ile aynı prensibi takip etme eğilimindeyim ve sadece her şeyi alıntılama alışkanlığı kazanıyorum (yapmamak için belirli bir neden yoksa).
Gordon Davisson

11
IFS'nin değerini bilmiyorsanız, ne olursa olsun teklif verin. Eğer IFS=0, o echo $?zaman çok şaşırtıcı olabilir.
Charles Duffy

3
Bağlama dayalı teklif, değerlerin ne olmasını beklediğinize göre değil, aksi takdirde hatalarınız daha kötü olacaktır. Örneğin, size yazabilir düşünüyorum böylece yolların hiçbiri, boşluklar olan eminiz cp $source1 $source2 $dest, ancak bazı beklenmedik sebeple eğer destset almaz, üçüncü argüman ortadan kaybolur ve sessizce kopyalar source1üzerinde source2bir verme yerine boş hedef için uygun hata (her bir argümanı alıntılamış olmanız gibi).
Derek Veit

3
quote it if...düşünce süreci geriye dönüktür - alıntılar ihtiyacınız olduğunda eklediğiniz bir şey değildir, ihtiyacınız olduğunda kaldırdığınız bir şeydir. Eğer sürece daima tek tırnak içinde dizeleri ve komut dosyalarını sarmak gerek çift tırnak kullanımı (örneğin bir değişken genişletmek izin için) veya ihtiyaç yok tırnak kullanmak (örn globbing ve dosya adı genişleme yapmak için).
Ed Morton

92

Kısacası, jeton bölme ve joker karakter genişletme gerçekleştirmek için kabuğa ihtiyaç duymadığınız her şeyi belirtin.

Tek tırnak işaretleri aralarındaki metni kelimesi kelimesine korur. Kabuğun dizeye hiç dokunmadığından emin olmanız gerektiğinde uygun araçtır. Tipik olarak, değişken enterpolasyona ihtiyacınız olmadığında tercih edilen alıntı mekanizmasıdır.

$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change

$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.

Değişken enterpolasyon gerektiğinde çift tırnak işaretleri uygundur. Uygun uyarlamalar ile, dizede tek tırnaklara ihtiyaç duyduğunuzda da iyi bir çözümdür. (Tek tırnak işaretleri arasında tek bir alıntıdan kaçmanın basit bir yolu yoktur, çünkü tek tırnak içinde kaçış mekanizması yoktur - eğer olsaydı, tam olarak kelimesi kelimesine alıntı yapmazlar.)

$ echo "There is no place like '$HOME'"
There is no place like '/home/me'

Kabuğun özellikle jeton bölme ve / veya joker karakter genişletmesi gerçekleştirmesi gerektiğinde tırnak içine alınmaz.

Jeton bölme;

 $ words="foo bar baz"
 $ for word in $words; do
 >   echo "$word"
 > done
 foo
 bar
 baz

Aksine:

 $ for word in "$words"; do echo "$word"; done
 foo bar baz

(Döngü yalnızca bir kez, tek tırnak içine alınmış dize üzerinde çalışır.)

 $ for word in '$words'; do echo "$word"; done
 $words

(Döngü, yalnızca tek tırnaklı dize üzerinden yalnızca bir kez çalışır.)

Joker karakter genişletmesi:

$ pattern='file*.txt'
$ ls $pattern
file1.txt      file_other.txt

Aksine:

$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory

(Kelimenin tam anlamıyla adlandırılmış bir dosya yok file*.txt.)

$ ls '$pattern'
ls: cannot access $pattern: No such file or directory

(Adında da dosya $patternyok!)

Daha somut ifadelerle, bir dosya adı içeren herhangi bir şey genellikle alıntılanmalıdır (çünkü dosya adları boşluk ve diğer kabuk metakarakterlerini içerebilir). Bir URL içeren her şey genellikle alıntılanmalıdır (çünkü birçok URL ?ve gibi kabuk metakarakterleri içerir &). Normal ifade içeren herhangi bir şey genellikle alıntılanmalıdır (ditto ditto). Boşluk olmayan karakterler arasındaki tek boşluklar dışında önemli boşluk içeren herhangi bir şeyin alıntılanması gerekir (aksi takdirde kabuk boşlukları etkili bir şekilde tek boşluklara dayatır ve önde gelen veya arkadaki boşlukları keser).

Bir değişkenin yalnızca kabuk metakarakterleri içermeyen bir değer içerebileceğini bildiğinizde, tırnak işareti isteğe bağlıdır. Bu nedenle, alıntılanmamış $?temel olarak iyidir, çünkü bu değişken sadece tek bir sayı içerebilir. Bununla birlikte, "$?"aynı zamanda doğrudur ve genel tutarlılık ve doğruluk için önerilir (bu benim kişisel tavsiyem olsa da, yaygın olarak tanınan bir politika değildir).

Değişken olmayan değerler temel olarak aynı kurallara uyar, ancak daha sonra tırnak işareti yerine meta karakterlerden de kaçabilirsiniz. Yaygın bir örnek olarak, içinde bulunan bir URL &, metakarakterden kaçınılmaz veya alıntı yapılmadıkça kabuk tarafından arka plan komutu olarak ayrıştırılır:

$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found

(Tabii ki, bu URL URL'siz bir değişken içindeyse de olur.) Statik bir dize için, tek tırnak işaretleri en mantıklıdır, ancak herhangi bir alıntı veya kaçış şekli burada çalışır.

wget 'http://example.com/q&uack'  # Single quotes preferred for a static string
wget "http://example.com/q&uack"  # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack   # Backslash escape
wget http://example.com/q'&'uack  # Only the metacharacter really needs quoting

Son örnekte ayrıca "tahterevalli alıntı" olarak adlandırmak istediğim başka bir yararlı kavram da öneriliyor. Tek ve çift tırnakları karıştırmanız gerekiyorsa, bunları birbirine bitişik olarak kullanabilirsiniz. Örneğin, aşağıdaki alıntılanan dizeler

'$HOME '
"isn't"
' where `<3'
"' is."

tokenizasyon ve alıntı kaldırıldıktan sonra tek bir uzun dize oluşturarak arka arkaya yapıştırılabilir.

$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.

Bu çok okunaklı değil, ancak yaygın bir teknik ve bu nedenle bilmek iyi.

Bir yana, senaryolar genellikle lshiçbir şey için kullanılmamalıdır . Bir joker karakteri genişletmek için, sadece ... kullanın.

$ printf '%s\n' $pattern   # not ``ls -1 $pattern''
file1.txt
file_other.txt

$ for file in $pattern; do  # definitely, definitely not ``for file in $(ls $pattern)''
>  printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt

(İkinci örnekte döngü tamamen gereksizdir; printfözellikle birden fazla argümanla iyi çalışır stat. Ancak joker karakter eşleşmesi üzerinde döngü yapmak yaygın bir sorundur ve sıklıkla yanlış yapılır.)

Üzerinde döngü için bir jeton listesi veya genişletmek için bir joker karakter içeren bir değişken daha az görülür, bu nedenle bazen "ne yaptığınızı tam olarak bilmediğiniz sürece her şeyi alıntılamak" anlamına gelir.


1
Bu, ilgili bir soruya gönderdiğim yanıtın (bir kısmının) bir varyantıdır . Buraya yapıştırıyorum, çünkü bu özlü ve bu özel sorun için kanonik bir soru haline gelecek kadar iyi tanımlanmış.
üçlü

4
Bunun madde # 0 olduğunu ve mywiki.wooledge.org/BashPitfalls genel Bash hataları koleksiyonunda tekrar eden bir tema olduğunu not edeceğim . Bu listedeki münferit öğelerin çoğu, temel olarak bu konuyla ilgilidir.
üçlü

27

Genel olarak alıntılar için üç noktalı bir formül:

İkili alıntı

Kelime bölme ve globbing'i bastırmak istediğimiz bağlamlarda. Ayrıca, değişmez değerin normal ifade yerine dize olarak ele alınmasını istediğimiz bağlamlarda.

Tek tırnak

İnterpolasyonu ve ters eğik çizgilerin özel muamelesini bastırmak istediğimiz string değişmezlerinde. Başka bir deyişle, çift tırnak kullanmanın uygun olmadığı durumlar.

Alıntı yok

Kesin kelime bölme veya zonklama sorunları olmadığından emin olduğumuz bağlamlarda veya kelime bölme ve zonklama istiyoruz .


Örnekler

İkili alıntı

  • boşluklu değişmez dizeler ( "StackOverflow rocks!", "Steve's Apple")
  • değişken açılımlar ( "$var", "${arr[@]}")
  • komut ikameleri ( "$(ls)", "`ls`")
  • dizin yolu veya dosya adı bölümünün boşluk içerdiği globs ( "/my dir/"*)
  • tek tırnakları korumak ( "single'quote'delimited'string")
  • Bash parametresi genişletme ( "${filename##*/}")

Tek tırnak

  • içinde boşluk olan komut adları ve bağımsız değişkenler
  • Bastırılması gereken enterpolasyon gerektiren değişmez dizeler ( 'Really costs $$!', 'just a backslash followed by a t: \t')
  • çift ​​tırnak ( 'The "crux"') korumak için
  • bastırılması için enterpolasyon gerektiren regex değişmez değerleri
  • Kullanım (özel karakterleri içeren sayılların alıntı shell $'\n\t')
  • birkaç tek ve çift tırnak ( $'{"table": "users", "where": "first_name"=\'Steve\'}')

Alıntı yok

  • yaklaşık standart sayısal değişkenler ( $$, $?, $#vs.)
  • aritmetik bağlamları gibi içinde ((count++)), "${arr[idx]}","${string:start:length}"
  • [[ ]]kelime bölme ve globbing konulardan arınmış ifadesi (bu tarz meselesidir ve görüşler ölçüde değişebilir)
  • nerede kelime bölme ( for word in $words)
  • nerede globbing istiyoruz ( for txtfile in *.txt; do ...)
  • nerede ~olarak yorumlanmasını istediğimiz $HOME( ~/"some dir"ama değil "~/some dir")

Ayrıca bakınız:


3
Bu yönergelere göre, kök dizindeki dosyaların bir listesini şu şekilde yazarak elde edilir "ls" "/" : "Tüm dize bağlamları" ifadesinin daha dikkatli bir şekilde nitelendirilmesi gerekir.
William Pursell

5
İse [[ ]], alıntı sağ taraftaki önemi var =/ ==ve =~: Bir kalıp / regex veya anlamıyla bir dize yorumlama arasındaki fark yaratıyor.
Benjamin W.

6
İyi bir genel bakış, ancak @ BenjaminW.'nin yorumları entegre edilmeye değer ve ANSI C alıntı dizeleri ( $'...') kesinlikle kendi bölümlerine sahip olmalıdır.
mklement0

3
@ mklement0, aslında eşdeğerler. Bu yönergeler, her zaman "ls" "/"daha yaygın yerine yazmak gerektiğini belirtir ls /ve bunu yönergelerde büyük bir kusur olarak kabul ediyorum.
William Pursell

4
İçin hiçbir tırnak değişken atama veya ekleyebiliriz case:)
PesaThe

4

Ben boşluk içermediğinden "$var"emin $vardeğilseniz , genellikle güvenli için alıntı gibi kullanın .

$varSatırları birleştirmek için basit bir yol olarak kullanıyorum :

lines="`cat multi-lines-text-file.txt`"
echo "$lines"                             ## multiple lines
echo $lines                               ## all spaces (including newlines) are zapped

Son yorum biraz yanıltıcı; yeni satırlar etkili bir şekilde boşluklarla değiştirilir, yalnızca kaldırılmaz.
Üçlü

-1

Kabuk kodundaki değişkenleri kullanmak için, alıntılanan değişken olarak "" alıntılanan değişkenleri kullanın, değişkenin kabuk komut dosyanızın yürütülmesini etkilemeyecek boşluklar veya özel karakterler içerebileceği anlamına gelir. Değişken adınızda boşluk veya özel karakter bulunmadığından emin değilseniz, bunları "" olmadan kullanabilirsiniz.

Misal:

echo "$ url name" - (Her zaman kullanılabilir)

echo "$ url name" - (Bu gibi durumlarda kullanılamaz, kullanmadan önce önlem alın)

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.