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?
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?
Yanıtlar:
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ç $URL
duyup 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.
IFS=0
, o echo $?
zaman çok şaşırtıcı olabilir.
cp $source1 $source2 $dest
, ancak bazı beklenmedik sebeple eğer dest
set almaz, üçüncü argüman ortadan kaybolur ve sessizce kopyalar source1
üzerinde source2
bir verme yerine boş hedef için uygun hata (her bir argümanı alıntılamış olmanız gibi).
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).
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 $pattern
yok!)
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 ls
hiç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.
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ı
"StackOverflow rocks!"
, "Steve's Apple"
)"$var"
, "${arr[@]}"
)"$(ls)"
, "`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Tek tırnak
'Really costs $$!'
, 'just a backslash followed by a t: \t'
)'The "crux"'
) korumak için$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Alıntı yok
$$
, $?
, $#
vs.)((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)for word in $words
)for txtfile in *.txt; do ...
)~
olarak yorumlanmasını istediğimiz $HOME
( ~/"some dir"
ama değil "~/some dir"
)Ayrıca bakınız:
"ls" "/"
: "Tüm dize bağlamları" ifadesinin daha dikkatli bir şekilde nitelendirilmesi gerekir.
[[ ]]
, alıntı sağ taraftaki önemi var =
/ ==
ve =~
: Bir kalıp / regex veya anlamıyla bir dize yorumlama arasındaki fark yaratıyor.
$'...'
) kesinlikle kendi bölümlerine sahip olmalıdır.
"ls" "/"
daha yaygın yerine yazmak gerektiğini belirtir ls /
ve bunu yönergelerde büyük bir kusur olarak kabul ediyorum.
case
:)
Ben boşluk içermediğinden "$var"
emin $var
değilseniz , genellikle güvenli için alıntı gibi kullanın .
$var
Satı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
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)