declare, Bash'te bir değişkene değer atarken ona nitelik (attribute) de vermenizi sağlayan yerleşik komuttur. declare -i değişkeni tamsayı yapar, -r salt okunur, -x dışa aktarılmış, -l ve -u küçük ya da büyük harfe zorlanmış, -a ve -A dizi, -n ise isim referansı yapar. declare -p ad değişkenin niteliklerini ve değerini gösterir. typeset aynı komutun Korn kabuğu uyumluluğu için bulunan eş anlamlısıdır; readonly ise yalnızca salt okunur niteliğini veren kısa yoldur.

Bu yazıda her niteliği ne zaman kullanacağınızı, niteliklerin atama sırasında nasıl çalıştığını, + ile nasıl kaldırıldığını ve fonksiyon içinde declare kullanmanın kapsamı nasıl değiştirdiğini çalışan örneklerle anlatıyoruz. Değişkenlere yeni başlıyorsanız önce değişkenler ve atama yazısını okuyun; serinin tamamı Bash öğrenme rehberinde.

Değişken nitelikleri nedir?

Değişken nitelikleri nedir?

Kılavuza göre her Bash değişkeninin bir değeri ve sıfır ya da daha fazla niteliği vardır. Nitelik, değişkenin nasıl davranacağını belirleyen bir işarettir: atanan değer aritmetik olarak mı hesaplanacak, harfler dönüştürülecek mi, değer değiştirilebilecek mi, alt süreçlere aktarılacak mı? Nitelikleri declare verir; export ve readonly komutları ise birer niteliği vermenin kısa yollarıdır. declare, typeset, export, readonly, local ve alias kılavuzda "bildirim komutları" (declaration commands) olarak geçer; bu komutlara verilen ad=değer argümanları normal atama gibi işlenir.

Komutun genel biçimi declare [-aAfFgiIlnrtux] [-p] [ad[=değer] ...] şeklindedir. Bir harfin önüne - koymak niteliği açar, + koymak kapatır. Hiç ad vermeden çalıştırırsanız tanımlı bütün değişkenleri listeler; seçenekle birlikte ad vermeden çalıştırmak ise yalnızca o niteliğe sahip değişkenleri gösterir.

declare -p: bir değişkenin gerçek durumunu görmek

Hata ayıklarken en çok işinize yarayacak seçenek -p'dir. Değişkenin niteliklerini ve değerini, kabuğa tekrar verildiğinde aynı değişkeni yeniden kuracak biçimde yazdırır. Çıktıdaki -- işareti değişkenin hiçbir özel niteliği olmadığını gösterir. Var olmayan bir adı sorarsanız komut hata mesajı verir ve sıfırdan farklı bir durum koduyla döner.

bash
ad="Deniz"
declare -i yas=30
declare -p ad yas
# çıktı:
#   declare -- ad="Deniz"
#   declare -i yas="30"

declare -p olmayan
echo "durum: $?"
# çıktı:
#   ornek.sh: line 8: declare: olmayan: not found
#   durum: 1

# ${ad@a} yalnızca nitelik harflerini, ${ad@A} declare komutunu verir
echo "[${yas@a}] ${yas@A}"
# çıktı: [i] declare -i yas='30'

${ad@a} ve ${ad@A} dönüşümleri kılavuzun parametre genişletme bölümünde tanımlıdır. İlki niteliklerin harflerini, ikincisi değişkeni yeniden oluşturacak atama veya declare komutunu üretir. Bir betikte değişkenin dizi olup olmadığını sınamak gibi işler için @a kullanışlıdır.

declare -i: tamsayı niteliği

-i niteliği verilen değişkene yapılan her atama aritmetik ifade olarak değerlendirilir; $(( )) yazmanıza gerek kalmaz. Değer içinde boşluk olan bir ifadeyse tırnak içine almanız yeterlidir. Aritmetik bağlamda değişken adları $ olmadan da değerlerine çözülür, tanımsız veya boş değişkenler 0 sayılır. += ise metin eklemek yerine toplama yapar.

bash
declare -i n=3+4
echo "$n"
# çıktı: 7
n+=10              # toplama yapar
echo "$n"
# çıktı: 17
n="2 * n"          # n burada 17
echo "$n"
# çıktı: 34
n=abc              # abc tanımsız bir değişken: 0
echo "$n"
# çıktı: 0
declare -i onaltilik=16#ff
echo "$onaltilik"
# çıktı: 255

# baştaki 0 sekizlik taban demektir; 08 geçersizdir
n=08
echo "durum: $?"
# çıktı:
#   ornek.sh: line 18: 08: value too great for base (error token is "08")
#   durum: 1

Son örnek -i ile çalışırken en çok karşılaşılan sürprizdir. Bash aritmetiğinde 0 ile başlayan sayılar sekizlik tabanda okunur; tarih veya saatten gelen 08, 09 gibi değerler bu yüzden hata verir. Böyle bir değeri onluk tabanda yorumlatmak için 10#08 yazabilirsiniz. Taban gösterimleri, sıfıra bölme ve diğer aritmetik kuralları aritmetik işlemler yazısında ayrıntılı anlatılıyor.

-i niteliğinin bir dezavantajı da şudur: değişkenin tamsayı olduğunu atama satırına bakarak anlayamazsınız. n=abc gibi bir satır sessizce 0 üretir ve hatayı gizler. Bu yüzden birçok betik yazarı -i yerine hesaplamanın yapıldığı yerde açıkça (( )) veya $(( )) kullanmayı tercih eder. Hangisini seçerseniz seçin, betik boyunca tutarlı olun.

declare -l ve -u: harf dönüşümü

-l niteliği atanan değerdeki büyük harfleri küçüğe, -u ise küçük harfleri büyüğe çevirir. Dönüşüm atama anında yapılır; değişkenin değeri saklanırken zaten dönüştürülmüş haldedir. Kılavuz iki niteliğin birbirini dışladığını belirtir: -l verildiğinde büyük harf niteliği, -u verildiğinde küçük harf niteliği kapanır. Kullanıcıdan gelen bir "evet/EVET/Evet" cevabını tek biçime indirmek için kullanışlıdır.

bash
declare -l cevap
cevap="EvEt"
echo "$cevap"
# çıktı: evet

declare -u kod
kod=tr34
kod+=ab              # eklenen kısım da dönüştürülür
echo "$kod"
# çıktı: TR34AB

# Türkçe harfler: sonuç yerel ayara bağlıdır (burada C.UTF-8)
declare -l k="İSTANBUL ILGAZ"
declare -u b="istanbul ılgaz"
echo "$k / $b"
# çıktı: istanbul ilgaz / ISTANBUL ILGAZ

Son satıra dikkat edin. Örnekler C.UTF-8 yerel ayarıyla çalıştırıldı ve bu Türkçe bir yerel ayar değildir: büyük I harfi ı yerine i olur, İ harfi de i olur; küçük ı ise I olur. Türkçe kurallarına göre dönüşüm gerekiyorsa sisteminizde Türkçe bir yerel ayar kurulu olmalı ve betik o ayarla çalışmalıdır; sonucu mutlaka kendi sisteminizde deneyin. Aynı dönüşümü nitelik vermeden yapmak için ${ad,,} ve ${ad^^} genişletmeleri vardır; bunlar string işlemleri yazısında anlatılıyor.

declare -r ve readonly: salt okunur değişkenler

declare -r ve readonly: salt okunur değişkenler

-r niteliği verilen bir değişkene sonradan değer atanamaz ve unset ile silinemez. Aynı işi readonly AD=değer de yapar. Kılavuza göre bu nitelik +r ile geri alınamaz; değişken kabuk oturumu boyunca salt okunur kalır. Bu yüzden yapılandırma sabitlerini, örneğin bir sürüm numarasını veya bir kurulum klasörünü, betiğin başında salt okunur yapmak yanlışlıkla üzerine yazılmasını önler.

bash
readonly SURUM=2.4
SURUM=3.0
echo "atama durumu: $?"
# çıktı:
#   ornek.sh: line 2: SURUM: readonly variable
#   atama durumu: 1

unset SURUM
# çıktı: ornek.sh: line 8: unset: SURUM: cannot unset: readonly variable
declare +r SURUM
# çıktı: ornek.sh: line 10: declare: SURUM: readonly variable

echo "$SURUM"
# çıktı: 2.4

declare -ri ADET=5   # nitelikler birleştirilebilir
declare -p ADET
# çıktı: declare -ir ADET="5"

Bash'in varsayılan modunda betik hata mesajını yazıp sonraki satırdan devam etti. Kılavuzun POSIX modu listesine göre ise POSIX modunda, arkasından komut gelmeyen bir atamada oluşan bu tür bir hata (salt okunur değişkene atama gibi) etkileşimli olmayan kabuğu hata durumuyla sonlandırır. Ayrıntı için Bash ve sh farkı yazısına bakın. Hatanın betiği her durumda durdurmasını istiyorsanız set -e yazısındaki yöntemlere göz atın.

readonly komutunun da seçenekleri vardır: -a ve -A dizileri, -f fonksiyonları salt okunur yapar. readonly -p veya argümansız readonly tüm salt okunur adları listeler. Bu listede sizin tanımladıklarınızın yanında kabuğun kendi salt okunur değişkenleri de görünür; değerleri sistemden sisteme değiştiği için burada çıktısını göstermiyoruz.

bash
selam() { echo "merhaba"; }
readonly -f selam
selam() { echo "yeni tanım"; }
echo "durum: $?"
# çıktı:
#   ornek.sh: line 3: selam: readonly function
#   durum: 1
selam
# çıktı: merhaba

declare -x ve export: alt süreçlere aktarmak

-x niteliği değişkeni dışa aktarılmış olarak işaretler; o andan sonra çalıştırılan her komut değişkeni ortamında görür. export AD=değer aynı işi yapar. declare +x ise dışa aktarmayı kaldırır ama değişkeni silmez; değişken mevcut kabukta yaşamaya devam eder.

bash
declare -x BOLGE=istanbul
bash -c 'echo "alt kabuk: $BOLGE"'
# çıktı: alt kabuk: istanbul

declare +x BOLGE
bash -c 'echo "alt kabuk: ${BOLGE-görünmüyor}"'
# çıktı: alt kabuk: görünmüyor
echo "mevcut kabuk: $BOLGE"
# çıktı: mevcut kabuk: istanbul

Burada bash -c ile yeni bir Bash süreci başlatıyoruz; tek tırnak sayesinde $BOLGE dış kabukta değil, alt süreçte genişletiliyor. Ortam ve alt kabuk farkları subshell ve çalışma ortamı yazısında ayrıntılı anlatılıyor.

declare -a, -A ve -n: diziler ve isim referansı

-a indeksli, -A ilişkisel dizi bildirir. İndeksli dizi bildirimsiz de oluşabilir, ilişkisel dizi ise mutlaka -A ister. Kılavuz bir ayrıntıya dikkat çeker: bileşik atamayla dizi oluştururken verdiğiniz ek nitelikler, örneğin -i, ilk atamaya değil sonraki atamalara uygulanır. Yine de denediğimiz sürümde declare -ai ile verilen ilk değerler hesaplandı; bu ayrıntıya güvenmek yerine sonucu declare -p ile kontrol edin.

bash
declare -ai puan=(1+1 2*3)
puan+=(4+4)
declare -p puan
# çıktı: declare -ai puan=([0]="2" [1]="6" [2]="8")

# dizi niteliği + ile kaldırılamaz
declare +a puan
# çıktı: ornek.sh: line 7: declare: puan: cannot destroy array variables in this way

Dizilerin kullanımı indeksli dizi ve ilişkisel dizi yazılarında anlatılıyor. -n ise değişkeni başka bir değişkenin adını tutan bir isim referansına (nameref) dönüştürür: referansa yapılan her okuma ve atama, adını taşıdığı değişkene yönlenir. Kılavuza göre dizilere nameref niteliği verilemez ama bir nameref bir diziyi gösterebilir. En yaygın kullanımı, bir fonksiyona sonuç yazacağı değişkenin adını vermektir.

bash
sayac=5
arttir() {
  declare -n hedef=$1     # hedef, adı $1 olan değişkeni gösterir
  hedef=$((hedef + 1))
}
arttir sayac
echo "$sayac"
# çıktı: 6

Nameref ile dolaylı genişletme (${!ad}) arasındaki farklar ve eval yerine neden genellikle nameref önerildiği eval ve dolaylı değişken yazısında, fonksiyonlarda kapsam ise local ve kapsam yazısında anlatılıyor.

Fonksiyon içinde declare: yerel kapsam ve -g

Bir fonksiyonun içinde kullanılan declare, local gibi davranır ve değişkeni o fonksiyona yerel yapar. Bu çoğu zaman istenen davranıştır, ama genel bir değişken oluşturmak isterken şaşırtabilir. Kılavuz bunun için -g seçeneğini tanımlar: fonksiyon içinde bile değişkeni genel kapsamda oluşturur veya değiştirir. Fonksiyon dışında -g yok sayılır. local komutu declare'in bütün seçeneklerini kabul eder, yani local -i veya local -A yazabilirsiniz.

bash
kur() {
  declare yerel=1
  declare -g genel=2
  local -i hesap=3+3
  echo "içeride: $yerel $hesap"
}
kur
# çıktı: içeride: 1 6
echo "dışarıda: ${yerel-tanımsız} $genel"
# çıktı: dışarıda: tanımsız 2

İlişkisel diziyi bir fonksiyon içinde doldurup betiğin geri kalanında kullanmak istiyorsanız declare -gA yazmanız gerekir; düz declare -A diziyi fonksiyon bitince yok eder. Fonksiyon tanımları için fonksiyon yazısına bakabilirsiniz.

declare -f ve -F: fonksiyonları listelemek

-f seçeneği değişkenler yerine fonksiyonlarla çalışır: declare -f ad fonksiyonun tanımını yazdırır, declare -F ise yalnızca fonksiyon adlarını listeler. Kılavuza göre extdebug seçeneği açıkken -F fonksiyonun tanımlandığı dosya adını ve satır numarasını da gösterir. Bir betiğe hangi fonksiyonların yüklendiğini görmek veya bir fonksiyonun tanımını başka bir kabuğa aktarmak için kullanılır.

bash
yedekle() { echo "yedek alınıyor"; }
declare -F
# çıktı: declare -f yedekle
declare -f yedekle
# çıktı:
#   yedekle () 
#   { 
#       echo "yedek alınıyor"
#   }

Nitelikleri kaldırmak: + kullanımı

Bir niteliği kaldırmak için harfin önüne + koyarsınız: declare +i n tamsayı niteliğini, declare +x AD dışa aktarmayı kaldırır. Kılavuz iki istisna belirtir: +a ve +A dizi değişkenini yok etmek için kullanılamaz, +r de salt okunur niteliğini kaldırmaz. Nitelik kaldırmak değeri değiştirmez; sadece sonraki atamaların nasıl işleneceğini değiştirir.

bash
declare -i n=2+2
declare +i n
n=2+2              # artık düz metin ataması
echo "$n"
# çıktı: 2+2

Seçenekler tablosu

Seçenekler tablosu: indeksli dizi, ilişkisel dizi, tamsayı, atama aritmetik değerlendirilir, atamada küçük harfe çevirir
SeçenekAnlamıNot
-aindeksli dizibildirimsiz de oluşabilir
-Ailişkisel dizitek oluşturma yolu
-itamsayı, atama aritmetik değerlendirilir+= toplama yapar
-latamada küçük harfe çevirir-u niteliğini kapatır
-uatamada büyük harfe çevirir-l niteliğini kapatır
-rsalt okunur+r ile kaldırılamaz
-xdışa aktarılırexport ile aynı
-nisim referansıdizilere verilemez
-ttrace niteliğifonksiyonlar DEBUG ve RETURN tuzaklarını devralır
-gfonksiyon içinde genel kapsamfonksiyon dışında yok sayılır
-Iyerel değişken dış kapsamdaki aynı adlı değişkenin nitelik ve değerini devralırnameref devralınmaz
-f / -Ffonksiyon tanımı / yalnızca adı-F, -f anlamını içerir
-pnitelik ve değeri yeniden kullanılabilir biçimde gösteriradla verilince diğer seçenekler yok sayılır

Sık yapılan hatalar

  • Fonksiyon içinde declare ile genel değişken beklemek: değişken yerel olur; genel için -g ekleyin.
  • -i ile baştaki sıfır: 08 sekizlik tabanda geçersizdir; 10#08 kullanın.
  • -i ile metin atamak: hata vermeden 0 veya beklenmedik bir sayı üretir; girdiyi önce doğrulayın.
  • +r ile salt okunurluğu kaldırmaya çalışmak: mümkün değildir; değişken oturum boyunca salt okunur kalır.
  • Türkçe harf dönüşümüne güvenmek: -l ve -u sonucu yerel ayara bağlıdır.
  • Diziyi sonradan ilişkisel yapmak: indeksli bir değişken declare -A ile dönüştürülemez; önce silin.

Sık Sorulan Sorular

declare ile typeset arasında fark var mı?

Hayır. Kılavuz typeset komutunu Korn kabuğu uyumluluğu için sağlanan, declare ile eş anlamlı bir komut olarak tanımlar. Bash betiklerinde declare daha yaygın kullanılır.

readonly ile declare -r aynı şey mi?

Değişkenler için etkisi aynıdır: ikisi de salt okunur niteliğini verir. readonly fonksiyon içinde çağrıldığında değişkeni yerel yapmaz, declare -r ise fonksiyon içinde yerel bir salt okunur değişken oluşturur. Bu farkı betiğinizde declare -p ile kontrol edebilirsiniz.

Salt okunur bir değişkeni nasıl değiştiririm?

Aynı kabuk oturumunda değiştiremezsiniz. Değerin değişmesi gerekiyorsa değişkeni salt okunur yapmayın ya da değeri hesaplayan kodu değişkeni salt okunur yapmadan önce çalıştırın. Yeni bir kabuk süreci başlatırsanız salt okunur nitelik oraya aktarılmaz.

Bir değişkenin tamsayı olup olmadığını nasıl anlarım?

${ad@a} genişletmesinin çıktısında i harfi olup olmadığına bakabilirsiniz. Ancak bu yalnızca niteliği gösterir; değerin bir sayı olup olmadığını sınamak için bir desen veya =~ eşleşmesi kullanmanız gerekir.

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 · local ve kapsam · İndeksli diziler · İlişkisel diziler · Aritmetik işlemler