İçeriğe geç
academia.sh

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:

  1. Kelime bölme (word splitting). Sonuç metni, IFS değişkenindeki karakterlere göre kelimelere ayrılır. IFS’in öntanımlı değeri boşluk, sekme ve satır sonudur.
  2. 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.
  • export değ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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat