Ders 07 / 24
Değişkenler ve Tırnak Kuralları
Atama sözdizimi, genişletme sırası, kelime bölme ve yol adı genişletmesi, üç alıntılama biçimi ve öntanımlı değer gösterimleri.
İçindekiler
Betikteki kütük adı şimdilik sabit yazılı. Onu bir değişkene almak ilk bakışta düzen meselesi görünür. Kabukta ise bu, dilin en çok hata üreten alanına girmek demektir: bir değişkenin genişletilmesi tek bir işlem değildir, birkaç aşamalı bir süreçtir ve aşamaların hangisinin uygulanacağı tırnak kullanımına bağlıdır.
Programlama Temelleri kursunda değişken, bir değere bağlanan addı; okunduğunda o değeri verirdi. Kabukta durum farklıdır: değişken okunduğunda ortaya çıkan metin, komut satırının geri kalanıyla birlikte yeniden çözümlenir. Bu dersin konusu tam olarak o yeniden çözümlemedir.
Atama
Atama, eşittir işaretinin iki yanında boşluk bırakılmadan yazılır.
ad = deger
bash: ad: command not found
Kabuk ad kelimesini komut adı, = ve deger kelimelerini argüman sandı. Kabuk
satırını önce boşluğa göre kelimelere ayırdığı için başka türlü davranamazdı. Doğru
yazımda boşluk yoktur:
ad=deger echo "[$ad] [${ad}] [${ad}suz]"
[deger] [deger] [degersuz]
Süslü ayraçlı biçim, ad ile onu izleyen metnin sınırını belirtir. $adsuz yazılsaydı
kabuk adsuz adlı bir değişken arardı.
Kabukta değişkenlerin tipi yoktur; her değer bir karakter dizisidir. sayi=5 ataması bir
tam sayı üretmez, iki karakterlik bir dizi üretir. Sayısal işlem gerektiğinde ayrı bir
gösterim kullanılır; bu, Aritmetik ve Komut Yerine Koyma dersinin konusudur.
Genişletmeden Sonra Ne Olur
Bir değişken genişletildiğinde iş bitmez. Kabuk, tırnaksız genişletmenin sonucuna iki işlem daha uygular:
- Kelime bölme (word splitting). Sonuç metni,
IFSdeğişkenindeki karakterlere göre kelimelere ayrılır.IFS’in öntanımlı değeri boşluk, sekme ve satır sonudur. - Yol adı genişletmesi (pathname expansion). Kelimelerde
*,?,[...]karakterleri varsa eşleşen dosya adlarıyla değiştirilir.
İki işlemin sonucu gözlemlenebilir. Aşağıdaki yardımcı, aldığı argüman sayısını ve her argümanı ayrı ayrı yazar:
say() { printf 'argüman sayisi: %d\n' "$#"; printf ' <%s>\n' "$@"; }
metin="iki bosluklu" say $metin say "$metin"
argüman sayisi: 2 <iki> <bosluklu> argüman sayisi: 1 <iki bosluklu>
Tırnaksız genişletmede iki kelime, tırnaklı genişletmede tek kelime oluştu. Aradaki üç boşluk da tırnaksız durumda kayboldu — kelime bölme ayırıcıları saklamaz.
Tırnaksız Genişletmenin Bedeli
Bu davranışın betiklerdeki karşılığı somuttur. Boşluk içeren bir dosya adı düşünün:
kutuk="erisim kutugu.log" say $kutuk say "$kutuk"
argüman sayisi: 2 <erisim> <kutugu.log> argüman sayisi: 1 <erisim kutugu.log>
Tırnaksız yazımda komut, var olmayan iki dosya adı aldı. rm $kutuk gibi bir satırda bu,
yanlış dosyanın silinmesi anlamına gelir.
Yol adı genişletmesi ikinci bir katman ekler. Değişkenin içinde kalıp karakteri varsa, tırnaksız genişletme dosya sistemine bakar:
cd "$(mktemp -d)" # yalnızca iki dosyanın bulunduğu boş bir dizin : > "erisim kutugu.log" : > yok.log kalip="*.log" say $kalip say "$kalip"
argüman sayisi: 2 <erisim kutugu.log> <yok.log> argüman sayisi: 1 <*.log>
Sıraya dikkat edin: değişkenin değeri *.log idi; genişletme, kelime bölme ve yol adı
genişletmesinden sonra iki dosya adı ortaya çıktı. Üstelik erisim kutugu.log tek
argüman olarak geldi — çünkü yol adı genişletmesinin sonucu kelime bölmeye
uğramaz. Bölme yalnızca değişkenin kendi değerine uygulanır.
Buradan kursun geri kalanında geçerli olacak kural çıkar:
Değişken genişletmeleri her zaman çift tırnak içine alınır. Tırnaksız bırakmak, kelime bölme ya da yol adı genişletmesi bilinçli olarak isteniyorsa yapılır.
Bu kural "$var", "$@", "$(komut)" ve "${dizi[@]}" için de geçerlidir.
Üç Alıntılama Biçimi
Çift tırnak genişletmeleri uygular ama kelime bölme ve yol adı genişletmesini engeller. Değişken, komut ve aritmetik genişletme içeride çalışır.
Tek tırnak hiçbir şeyi genişletmez; içerideki her karakter harfi harfine geçer. Tek tırnak içinde tek tırnak kullanılamaz — ters bölü bile onu kaçırmaz.
Ters bölü, kendisinden sonraki tek karakteri özel anlamından arındırır. Çift tırnak
içinde yalnızca $, `, ", \ ve satır sonu için etkilidir; diğer karakterlerin
önünde harfi harfine kalır.
metin="iki bosluklu" echo $metin echo "$metin" echo '$metin'
iki bosluklu iki bosluklu $metin
İlk satırda echo iki ayrı argüman aldı ve aralarına tek boşluk koyarak yazdı; ikinci
satırda boşluklar korundu; üçüncüde genişletme hiç olmadı.
Düzenli ifadeler ve sed kalıpları tek tırnak içinde yazılır: kalıplarda geçen $, *
ve \ karakterlerinin kabuk tarafından yorumlanması istenmez. Bu kural Metin İşleme
konusunun tamamında geçerli olacak.
IFS ve Bilinçli Bölme
IFS (internal field separator) değişkeni, kelime bölmenin hangi karakterlerde
yapılacağını belirler. Değiştirmek, ayrılmış bir satırı alanlarına bölmenin yollarından
biridir:
satir="10.0.0.12:404:512" IFS=: say $satir
argüman sayisi: 3 <10.0.0.12> <404> <512>
IFS genel bir ayardır; değiştirildikten sonra o kabuktaki tüm tırnaksız genişletmeleri
etkiler. Eski değerine döndürmeyi unutmak, betiğin ilerideki satırlarında açıklaması güç
hatalar üretir. Doğru desen, IFS’i yalnızca tek bir komut için ayarlamaktır:
IFS=: read -r adres kod bayt <<< "$satir"
Komuttan önce yazılan atama yalnızca o komutun ortamında geçerlidir; komut bitince eski değer geri gelir. Bu desen Döngüler dersinde kütüğü satır satır okurken kullanılacak.
Öntanımlı Değer Gösterimleri
Kabuk, tanımsız veya boş değişkenleri yerinde ele almak için bir gösterim ailesi sunar. İki nokta üst üste karakterinin varlığı, “boş” değerin de eksik sayılıp sayılmayacağını belirler.
| Gösterim | Değişken tanımsızsa | Değişken boşsa |
|---|---|---|
${v-yedek} |
yedek |
boş dizi |
${v:-yedek} |
yedek |
yedek |
${v=yedek} |
atar ve yedek verir |
boş dizi |
${v:=yedek} |
atar ve yedek verir |
atar ve yedek verir |
${v:?ileti} |
hata verip çıkar | hata verip çıkar |
${v:+deger} |
boş dizi | boş dizi |
${#v} |
0 | 0 |
unset KUTUK echo "1 tanimsiz, :- ile: [${KUTUK:-erisim.log}]" KUTUK="" echo "2 bos, :- ile: [${KUTUK:-erisim.log}]" echo "3 bos, - ile: [${KUTUK-erisim.log}]"
1 tanimsiz, :- ile: [erisim.log] 2 bos, :- ile: [erisim.log] 3 bos, - ile: []
Ayrım önemlidir: bir seçeneğin bilinçli olarak boş bırakılması ile hiç verilmemesi farklı
durumlardır. :- ikisini aynı sayar, - ayırır.
:? biçimi zorunlu değişkenleri bildirmenin en kısa yoludur:
unset ZORUNLU echo "[${ZORUNLU:?tanimlanmali}]"
bash: ZORUNLU: tanimlanmali
Betik bu noktada sıfırdan farklı kodla durur. Hata Yönetimi dersinde bu gösterim katı kiple birlikte kullanılacak.
Ortam Değişkenleri ve Kapsam
Kabuk değişkenleri öntanımlı olarak yalnızca o kabuğa aittir. export, değişkeni
ortama (environment) koyar; o noktadan sonra başlatılan alt süreçler onu görür.
export DIL=tr bash -c 'echo "alt surecte DIL=$DIL"' YEREL=x bash -c 'echo "alt surecte YEREL=[${YEREL-yok}]"'
alt surecte DIL=tr alt surecte YEREL=[yok]
Aktarım tek yönlüdür: alt süreç ortamı değiştirse bile üst süreç bundan etkilenmez. Boru Hattı dersindeki kural burada da geçerlidir.
readonly bir değişkeni değiştirilemez yapar:
readonly SABIT=5 SABIT=6
bash: SABIT: readonly variable
Adlandırma sözleşmesi yaygındır ve uyulması yararlıdır: ortama giren ve dış dünyayla
paylaşılan adlar büyük harfle, betiğe özgü adlar küçük harfle yazılır. Küçük harf
kullanmak ayrıca PATH, HOME, IFS gibi kabuk değişkenlerinin kazara ezilmesini
önler.
Betiğe Uygulama
rapor.sh betiği, sabit yazılı kütük adından kurtulur:
#!/usr/bin/env bash # rapor.sh — erisim kutugunden ozet rapor uretir. kutuk="${KUTUK:-erisim.log}" echo "== Kutuk: $kutuk ==" echo "== En cok istenen yollar ==" cut -d' ' -f7 "$kutuk" | sort | uniq -c | sort -rn | head -5
== Kutuk: erisim.log == == En cok istenen yollar == 6 /api/veri 4 /yok.html 4 /index.html 3 /urun/45 3 /urun/12
Üç karar birlikte alındı: ad bir değişkene çekildi, ortamdan gelen KUTUK değeri
öncelikli kılındı ve "$kutuk" genişletmesi tırnaklandı. Üçüncüsü olmadan, boşluk içeren
bir yol verildiğinde betik sessizce yanlış dosyayı okumaya çalışırdı.
Özet
- Atama, eşittir işaretinin iki yanında boşluk bırakmaz; kabuk satırı kelimelere ayırdığı için başka türlü ayrıştıramaz.
- Tırnaksız genişletmenin sonucuna kelime bölme ve yol adı genişletmesi uygulanır; yol adı genişletmesinin sonucu yeniden bölünmez.
- Değişken genişletmeleri her zaman çift tırnak içine alınır; tırnaksız bırakmak bilinçli bir karardır.
- Çift tırnak genişletir ve bölmeyi engeller, tek tırnak hiçbir şeyi genişletmez, ters bölü tek karakteri arındırır.
-ile:-arasındaki fark, boş değerin eksik sayılıp sayılmamasıdır;:?zorunlu değişkeni bildirir.exportdeğişkeni alt süreçlere aktarır; aktarım tek yönlüdür.
Sonraki Adım
Kütük adı artık ortam değişkeninden gelebiliyor, ama betiği çağıran kişi için doğal olan
yol bu değildir: ./rapor.sh erisim.log yazmak beklenir. Sonraki ders betiğe komut
satırından değer geçirmeyi ele alır — konumsal parametreler, argüman listesinin
tırnaklanması ve "$@" ile "$*" arasındaki, boşluklu argümanlarda ortaya çıkan fark.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.