Bash'te tek tırnak ('...') içindeki her karakter olduğu gibi kalır; değişkenler, komut ikameleri ve ters eğik çizgi kaçışları çalışmaz. Çift tırnak ("...") ise metni tek bir kelime olarak korurken $, ters tırnak ve bazı durumlarda ters eğik çizgi gibi karakterlerin özel anlamını sürdürür; yani "$ad" değişkenin değerini verir, '$ad' ise dört karakterlik bir metindir.

Tırnaklama Bash'in en çok hataya yol açan konusudur, çünkü bir değişkeni tırnaksız kullanmak çoğu denemede sorunsuz çalışır ve hata ancak değer bir boşluk veya joker karakter içerdiğinde ortaya çıkar. Bu yazıda kılavuzun tanımladığı dört tırnaklama yöntemini, kelime bölme ile ilişkisini ve sık yapılan hataları çalıştırılmış örneklerle anlatıyoruz. Serinin geri kalanı için Bash öğrenme rehberi sayfasına bakabilirsiniz.

Tırnaklama neden gerekir?

Tırnaklama neden gerekir?

Kılavuza göre tırnaklamanın görevi, bazı karakterlerin veya kelimelerin kabuk için taşıdığı özel anlamı kaldırmaktır. Tırnaklama ile özel karakterlerin işlenmesini kapatabilir, ayrılmış kelimelerin (if, for gibi) tanınmasını engelleyebilir ve parametre genişletmesini durdurabilirsiniz. Kabuğun meta karakterleri (boşluk, sekme, yeni satır ve |, &, ;, (, ), <, >) tırnaksız kullanıldığında kelimeleri ayırır; kendilerini temsil etmeleri için tırnaklanmaları gerekir.

Kılavuz dört tırnaklama yöntemi tanımlar: kaçış karakteri (ters eğik çizgi), tek tırnak, çift tırnak ve dolar işaretli tek tırnak ($'...'). Bunlara ek olarak $"..." biçimi yerel ayara göre çeviri için kullanılır.

bash
ad="Ayşe"
echo 'Merhaba $ad'
# çıktı: Merhaba $ad
echo "Merhaba $ad"
# çıktı: Merhaba Ayşe
echo Merhaba\ \ \ $ad
# çıktı: Merhaba   Ayşe
echo Merhaba   $ad
# çıktı: Merhaba Ayşe

Son iki satır kaçış karakterinin etkisini gösterir. Ters eğik çizgiyle kaçırılmış boşluklar kelime ayırıcı olmaktan çıkar ve metnin parçası olur; tırnaksız ve kaçışsız boşluklar ise kelimeleri ayırır ve echo ayrı argümanları tek boşlukla birleştirir.

Kaçış karakteri: ters eğik çizgi

Tırnaksız bir ters eğik çizgi, kendisinden sonra gelen tek karakterin özel anlamını kaldırır. Tek istisna yeni satırdır: satırın sonundaki ters eğik çizgi ve ardından gelen yeni satır, kılavuza göre satır devamı olarak işlenir ve girdiden tamamen silinir. Uzun komutları birden fazla satıra bölmek için bu davranış kullanılır:

bash
echo bir \
  iki \
  üç
# çıktı: bir iki üç
echo \$ad \\ \"tırnak\"
# çıktı: $ad \ "tırnak"

Tek tırnak: her şey olduğu gibi

Tek tırnak içindeki her karakter gerçek değerini korur. Dolar işareti, ters tırnak, ters eğik çizgi, yıldız: hepsi düz metindir. Bu kuralın bir sonucu, tek tırnağın tek tırnak içinde hiçbir şekilde yer alamamasıdır; kılavuz bunun ters eğik çizgiyle bile mümkün olmadığını açıkça belirtir. Tek tırnak içeren bir metin yazmak için üç yol vardır:

bash
echo 'Ali'\''nin dosyası'
# çıktı: Ali'nin dosyası
echo "Ali'nin dosyası"
# çıktı: Ali'nin dosyası
echo $'Ali\'nin dosyası'
# çıktı: Ali'nin dosyası

Birinci yöntem tırnağı kapatır, kaçışlı bir tek tırnak ekler ve tırnağı yeniden açar. Kabuk yan yana duran tırnaklı ve tırnaksız parçaları tek kelime olarak birleştirdiği için sonuç tek bir argümandır. İkinci yöntem çift tırnak kullanır; metinde genişletilmesini istemediğiniz bir $ yoksa en okunaklı seçenektir. Üçüncü yöntem, aşağıda anlatılan $'...' biçimidir.

Tek tırnak; düzenli ifadeler, awk veya sed programları, başka bir kabuğa gönderilecek komutlar gibi içinde kabuğun dokunmasını istemediğiniz karakterler barındıran metinler için en güvenli seçimdir.

Çift tırnak: kelimeyi koru, genişletmeye izin ver

Çift tırnak: kelimeyi koru, genişletmeye izin ver

Çift tırnak içindeki karakterler de gerçek değerini korur; istisnalar $, ters tırnak, ters eğik çizgi ve geçmiş genişletmesi açıksa ! karakteridir. Dolar işareti ve ters tırnak çift tırnak içinde de çalışır; bu yüzden değişken genişletmesi, komut ikamesi ve aritmetik genişletme yapılır. Kılavuza göre ters eğik çizgi çift tırnak içinde yalnızca $, ters tırnak, ", \ veya yeni satırdan önce geldiğinde özeldir ve bu durumda silinir. Başka bir karakterden önce gelen ters eğik çizgi olduğu gibi kalır:

bash
x=5
echo "dolar: \$x, değer: $x, ters bölü: \\, tırnak: \", sekme: \t"
# çıktı: dolar: $x, değer: 5, ters bölü: \, tırnak: ", sekme: \t
echo "toplam: $((x * 2)), komut: $(echo iç)"
# çıktı: toplam: 10, komut: iç

Örnekteki \t çift tırnak içinde bir sekmeye dönüşmez; ters eğik çizgi t harfinden önce özel olmadığı için olduğu gibi kalır. Kaçış dizilerini yorumlatmak istiyorsanız $'...' biçimini veya printf komutunu kullanın; ikincisi echo ve printf yazısında anlatılıyor.

Etkileşimli kabuklarda geçmiş genişletmesi varsayılan olarak açıktır ve ! karakteri çift tırnak içinde de özel olabilir. Kılavuz, ! karakterini çift tırnak içinde ters eğik çizgiyle kaçırabileceğinizi, ama bu durumda ters eğik çizginin silinmediğini belirtir. Betiklerde geçmiş genişletmesi kapalı olduğundan bu sorun yaşanmaz; terminalde ünlem içeren bir metni tek tırnakla yazmak en kolay çözümdür. Geçmiş genişletmesi komut geçmişi yazısında anlatılıyor.

bash
echo "Dikkat\!"
# çıktı: Dikkat\!
echo 'Dikkat!'
# çıktı: Dikkat!

Tırnaksız değişken: kelime bölme ve joker karakterler

Tırnaklamanın pratikteki en önemli görevi kelime bölmeyi ve dosya adı genişletmesini durdurmaktır. Kılavuza göre kabuk, çift tırnak içinde olmayan parametre genişletmesi, komut ikamesi ve aritmetik genişletme sonuçlarını IFS değişkenindeki karakterlere göre kelimelere böler. Ardından her kelimede tırnaksız *, ? veya [ varsa onu bir desen sayar ve eşleşen dosya adlarıyla değiştirir. Aşağıdaki küçük fonksiyon kendisine gelen argüman sayısını yazar; böylece bölmenin etkisini doğrudan görebilirsiniz:

bash
say() { echo "$# argüman"; }
dosya="yıllık rapor.txt"
say $dosya
# çıktı: 2 argüman
say "$dosya"
# çıktı: 1 argüman
bos=""
say $bos
# çıktı: 0 argüman
say "$bos"
# çıktı: 1 argüman

Boş değişken örneği gözden kaçan bir ayrıntıyı gösterir. Kılavuza göre değeri olmayan bir parametrenin tırnaksız genişletmesinden doğan örtük boş argümanlar silinir; çift tırnak içinde genişletilen boş bir parametre ise boş bir argüman olarak korunur. [ $bos = x ] gibi bir testin "unary operator expected" hatası vermesinin nedeni budur: tırnaksız boş değişken ortadan kaybolur ve test eksik argümanla çalışır. Bu hata sık hata mesajları yazısında, test yazımı ise test ve [[ ]] yazısında anlatılıyor.

bash
touch not1.txt not2.txt
desen="*.txt"
echo $desen
# çıktı: not1.txt not2.txt
echo "$desen"
# çıktı: *.txt
echo '*.txt'
# çıktı: *.txt

Tırnaksız $desen klasördeki dosyalarla eşleşti; tırnaklı biçimler yıldızı düz bir karakter olarak bıraktı. Bir değişkende kullanıcıdan gelen bir metin, bir dosya adı veya bir URL tutuyorsanız tırnaksız kullanım, değer içinde yıldız veya soru işareti olduğu anda beklenmedik dosya adlarına dönüşebilir. Joker karakterlerin kuralları glob yazısında anlatılıyor.

Değerin içindeki tırnaklar veridir

Kılavuzun tanımladığı son genişletme adımı tırnak kaldırmadır: önceki genişletmelerden geçen kelimelerdeki tırnaksız \, ' ve " karakterleri silinir. Ancak bu yalnızca sizin yazdığınız tırnaklar için geçerlidir; bir genişletmenin sonucunda ortaya çıkan tırnaklar silinmez. Yani bir değişkenin değerinde tırnak işareti varsa, o tırnaklar kabuk için sözdizimi değil, sıradan karakterlerdir:

bash
deger='"iki kelime"'
say() { echo "$# argüman: $*"; }
say $deger
# çıktı: 2 argüman: "iki kelime"
say "$deger"
# çıktı: 1 argüman: "iki kelime"

Tırnaksız kullanımda değer yine boşluktan bölündü ve tırnak işaretleri iki parçanın içinde düz karakter olarak kaldı. Bir komutu argümanlarıyla birlikte bir değişkende tutmaya çalışırken sık karşılaşılan bu sorunun doğru çözümü, argümanları bir dizide saklamaktır; bu yöntem dizi yazısında anlatılıyor. Değeri kabuğa yeniden verilebilir biçimde tırnaklamak için printf %q veya ${degisken@Q} kullanılabilir.

Tırnak, yorum ve boş argümanlar

Tırnaklamanın etkilediği bir başka kural yorum karakteridir. Kılavuza göre #, yalnızca bir kelimenin başında geldiğinde yorum başlatır. Tırnak içindeki veya kelimenin ortasındaki # sıradan bir karakterdir. Aynı bölüm, açıkça yazılmış boş argümanların ("" veya '') korunduğunu, ama boş olmayan bir kelimenin parçası olan tırnaklı boş kısmın silindiğini söyler; -d'' kelimesi kelime bölmeden sonra -d olur:

bash
echo merhaba # bu bir yorum
# çıktı: merhaba
echo "merhaba # yorum değil"
# çıktı: merhaba # yorum değil
echo merhaba#bitişik
# çıktı: merhaba#bitişik
printf '[%s]\n' -d'' '' x
# çıktı:
#   [-d]
#   []
#   [x]

İkinci argüman olan '' tek başına durduğu için boş bir argüman olarak korundu ve printf onu [] biçiminde yazdı. Bir komuta bilerek boş argüman vermek istediğinizde, örneğin bir seçeneğin değerini boş bırakmak istediğinizde, bu davranıştan yararlanırsınız.

ANSI-C tırnaklama: $'...'

ANSI-C tırnaklama: $'...': yeni satır, yatay sekme, satır başı (carriage return), \e veya \E

$'metin' biçimi özel bir tek tırnak türüdür. Kılavuza göre metin içindeki ters eğik çizgili kaçış dizileri ANSI C standardındaki gibi çözülür ve sonuç, dolar işareti hiç yokmuş gibi tek tırnaklı sayılır. Bu sayede sekme, yeni satır veya bir Unicode karakteri değişkene doğrudan yazabilirsiniz:

bash
printf '%s\n' $'satır1\nsatır2'
# çıktı:
#   satır1
#   satır2
printf '[%s]\n' $'tırnak: \' çift: " ters bölü: \\'
# çıktı: [tırnak: ' çift: " ters bölü: \]
echo $'\u00e7ay'
# çıktı: çay
echo $'\x41\102'
# çıktı: AB
KaçışAnlamı
\nyeni satır
\tyatay sekme
\rsatır başı (carriage return)
\e veya \Eescape karakteri (ANSI C dışında)
\\ters eğik çizgi
\'tek tırnak
\nnnsekizlik değeri nnn olan bayt (1-3 rakam)
\xHHonaltılık değeri HH olan bayt (1-2 rakam)
\uHHHHonaltılık değerli Unicode karakteri (1-4 rakam)
\cxCtrl-x kontrol karakteri

\u ile üretilen karakterin doğru görünmesi için kabuğun UTF-8 bir yerel ayarla çalışması gerekir. $'\t' biçimi, özellikle IFS=$'\t' gibi ayırıcı tanımlarında ve sekmeyle ayrılmış verileri işlerken çok kullanılır.

Yerel ayara göre çeviri: $"..."

Çift tırnaklı bir metnin önüne dolar işareti koyduğunuzda ($"Merhaba") Bash metni o anki yerel ayara göre çevirmeye çalışır; çeviri gettext altyapısıyla ve TEXTDOMAIN, TEXTDOMAINDIR değişkenleriyle yapılır. Kılavuza göre yerel ayar C veya POSIX ise ya da çeviri bulunamazsa dolar işareti yok sayılır ve metin sıradan çift tırnaklı bir metin gibi işlenir. Bu yüzden çeviri dosyası hazırlamadığınız sürece sonuç çift tırnakla aynıdır:

bash
ad="Ayşe"
echo $"Merhaba $ad"
# çıktı: Merhaba Ayşe

Çok dilli betik hazırlıyorsanız bash --dump-po-strings ile çevrilecek metinlerin listesini çıkarabilirsiniz; bu seçenek kılavuzun Bash'i başlatma bölümünde anlatılır. Günlük betiklerde bu biçime ihtiyaç duymazsınız ve yanlışlıkla $" yazmak çoğu zaman fark edilmez.

Tırnakları birleştirmek

Kabuk, aralarında boşluk olmayan tırnaklı ve tırnaksız parçaları tek bir kelime olarak birleştirir. Bu sayede aynı argümanın bir kısmını genişletmeye açık, bir kısmını kapalı tutabilirsiniz. Çift tırnak içindeki tek tırnaklar ve tek tırnak içindeki çift tırnaklar düz karakterdir:

bash
ad="Ayşe"
echo "Kullanıcı '$ad' giriş yaptı"
# çıktı: Kullanıcı 'Ayşe' giriş yaptı
echo 'Değer: "'"$ad"'" ($ad genişlemedi)'
# çıktı: Değer: "Ayşe" ($ad genişlemedi)
echo "Bugün $(echo "iç içe tırnak") çalışıyor"
# çıktı: Bugün iç içe tırnak çalışıyor

Son satır önemli bir kolaylığı gösterir: $( ) içinde yeni bir tırnaklama bağlamı başlar, bu yüzden dış çift tırnağın içinde iç çift tırnakları kaçırmadan kullanabilirsiniz. Eski ters tırnak biçiminde bu kadar rahat değildir; ayrıntılar komut ikamesi yazısında.

Özet tablo

YazımDeğişken genişler miKaçış dizileriKelime bölme
'metin'HayırHayırOlmaz
"metin"EvetYalnızca \$ \` \" \\ ve satır sonuOlmaz
$'metin'HayırEvet, ANSI C kaçışlarıOlmaz
$"metin"EvetÇift tırnakla aynıOlmaz
tırnaksızEvetTers eğik çizgi tek karakteri kaçırırOlur, joker de genişler

Sık yapılan hatalar

  • Değişkeni tek tırnakla yazmak: echo 'Merhaba $ad' değişkeni genişletmez. Genişleme istiyorsanız çift tırnak kullanın.
  • Tek tırnak içinde tek tırnak kaçırmaya çalışmak: 'Ali\'nin' çalışmaz; 'Ali'\''nin', "Ali'nin" veya $'Ali\'nin' yazın.
  • Tırnaksız değişkenle test yapmak: [ $ad = x ] değişken boşsa veya boşluk içeriyorsa hata verir; [ "$ad" = x ] yazın.
  • Çift tırnakta \n beklemek: echo "a\nb" yeni satır üretmez; printf 'a\nb\n' veya $'a\nb' kullanın.
  • Komutu tırnaklı bir dizgede saklamak: komut="ls -l 'iki kelime'" değişkenindeki tırnaklar çalıştırırken sözdizimi olarak işlenmez; argümanları bir dizide tutun.

Sık Sorulan Sorular

Bash'te tek tırnak ile çift tırnak arasındaki fark nedir?

Tek tırnak içinde hiçbir genişletme yapılmaz; metin karakteri karakterine korunur. Çift tırnak içinde değişken, komut ikamesi ve aritmetik genişletme yapılır ama kelime bölme ve joker genişletmesi yapılmaz. İkisi de içeriği tek bir kelime olarak tutar.

Değişkenleri her zaman tırnak içine almalı mıyım?

Değerin birden fazla argümana bölünmesini bilerek istemediğiniz her durumda evet. Tırnaksız kullanım, değer boşluk, sekme, yeni satır veya joker karakter içerdiğinde farklı davranır ve bu hatalar çoğu zaman yalnızca belirli girdilerde ortaya çıkar.

Tek tırnaklı bir metnin içine değişken nasıl eklenir?

Tek tırnağı kapatıp değişkeni çift tırnakla ekleyin ve tek tırnağı yeniden açın: 'Önce '"$ad"' sonra'. Parçalar arasında boşluk bırakmadığınız sürece kabuk sonucu tek bir argüman olarak birleştirir.

$'...' ile printf arasında hangisini kullanmalıyım?

Bir değişkene sekme veya yeni satır gibi sabit bir karakter koymak için $'...' kısa ve açıktır. Biçimlendirilmiş çıktı üretmek, değişkenleri belirli genişlikte yazmak veya kaçış dizilerini bir değişkenin değeri üzerinde yorumlatmak için printf daha uygundur.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Bash öğrenme rehberi · Değişkenler ve atama · echo ve printf · Argümanlar ve $@ · Joker karakterler