Bash'te bir fonksiyonun içinde tanımladığınız değişken, aksini söylemezseniz globaldir: fonksiyon bittikten sonra da yaşar ve dışarıdaki aynı adlı değişkenin üzerine yazar. Bir değişkeni yalnızca o fonksiyona ait yapmak için local ad=deger yazarsınız. Yerel değişken dışarıdaki aynı adlı değişkeni gölgeler; fonksiyon dönünce eski değer yeniden görünür. Bash dinamik kapsam kullandığı için yerel değişkeni, o fonksiyonun çağırdığı diğer fonksiyonlar da görür ve değiştirebilir.
Bu yazıda local komutunun nasıl çalıştığını, dinamik kapsamın pratikte ne anlama geldiğini, declare ile declare -g farkını, unset komutunun kapsamlar arasında nasıl davrandığını, local - ile kabuk seçeneklerini yerel yapmayı, local -n isim referanslarıyla sonuç döndürmeyi ve FUNCNAME çağrı yığınını GNU Bash kılavuzuna dayanarak anlatıyoruz. Fonksiyon tanımlamanın temellerini bilmiyorsanız önce Bash fonksiyon tanımlama yazısını okuyun; serinin tamamı için Bash öğrenme rehberi sayfasına bakabilirsiniz. Örnekler GNU Bash 5.2.37 ile çalıştırıldı.
Varsayılan durum: değişkenler paylaşılır

Kılavuz, değişkenlerin ve değerlerinin normalde fonksiyon ile onu çağıran arasında paylaşıldığını söyler. Fonksiyonun içinde yapılan bir atama, dışarıdaki değişkeni doğrudan değiştirir:
ayarla() { renk=mavi; }
renk=kırmızı
ayarla
echo "$renk" # çıktı: maviKüçük bir betikte bu bazen işinize yarar, ama fonksiyon sayısı arttıkça tehlikeli hale gelir. i, dosya, sonuc gibi sık kullanılan adlar farklı fonksiyonlarda tekrar tekrar geçer. Bir döngünün içinden çağırdığınız yardımcı fonksiyon kendi döngüsünde i değişkenini kullanıyorsa dıştaki döngünün sayacını bozar ve hatayı bulmak zor olur. Bu yüzden fonksiyon içinde yalnızca kendi işi için kullandığınız her değişkeni yerel tanımlamak iyi bir alışkanlıktır.
local ile gölgeleme
local yerleşik komutu her argüman için yerel bir değişken oluşturur ve isterseniz değer atar. Kılavuzun ifadesiyle yerel değişken, önceki kapsamlarda aynı adla tanımlanmış değişkenleri gölgeler: fonksiyon içindeki okuma ve yazmalar yerel değişkene gider, dışarıdaki değer olduğu gibi kalır.
ad=global
goster() {
local ad=yerel
echo "içeride: $ad"
}
goster # çıktı: içeride: yerel
echo "dışarıda: $ad" # çıktı: dışarıda: globallocal ile declare komutunun bütün seçeneklerini kullanabilirsiniz: local -i sayac=0 tamsayı niteliği verir, local -a liste=() yerel bir dizi oluşturur, local -r sabit=5 fonksiyon süresince salt okunur bir değişken tanımlar. Tek satırda birden fazla değişken de tanımlanabilir: local ad soyad yas=0. Niteliklerin anlamları Bash declare ve readonly yazısında ayrıntılı olarak var.
local yalnızca bir fonksiyonun içinde kullanılabilir. Betiğin en üst seviyesinde yazarsanız hata alırsınız ve komut 1 durumuyla döner:
local x=1
echo "durum: $?"
# çıktı:
# ornek.sh: line 1: local: can only be used in a function
# durum: 1Dinamik kapsam: çağrılan fonksiyon da görür
Bash, birçok programlama dilinden farklı olarak sözcüksel değil dinamik kapsam kullanır. Bu, bir fonksiyonun hangi değişkeni göreceğinin kodun nerede yazıldığına göre değil, çalışma sırasında hangi çağrı zinciriyle oraya gelindiğine göre belirlendiği anlamına gelir. Kılavuzdaki örneği Türkçeleştirerek çalıştıralım:
birinci() {
local mesaj='birincinin yereli'
ikinci
}
ikinci() {
echo "ikinci gördü: $mesaj"
}
mesaj=global
birinci # çıktı: ikinci gördü: birincinin yereli
ikinci # çıktı: ikinci gördü: global
dis() { local sayi=1; ic; echo "dis: $sayi"; }
ic() { sayi=99; } # local yok: çağıranın yerelini değiştirir
sayi=0
dis # çıktı: dis: 99
echo "global: $sayi" # çıktı: global: 0ikinci fonksiyonu kendi başına çağrıldığında global mesaj değerini görüyor. birinci üzerinden çağrıldığında ise birinci fonksiyonunun yerel değişkenini görüyor, çünkü o anda en yakın kapsamdaki mesaj odur. Örneğin ikinci kısmı daha da önemli bir sonucu gösteriyor: ic fonksiyonu local kullanmadan sayi=99 yazınca global değişkeni değil, kendisini çağıran dis fonksiyonunun yerel değişkenini değiştiriyor. Global sayi 0 olarak kalıyor.
Bu davranış, local ile tanımladığınız değişkenin tamamen gizli olmadığını gösterir. Kılavuz yerel değişkenlerin yalnızca fonksiyon ve onun çağırdığı komutlar tarafından görülebildiğini söyler. Dolayısıyla yerel değişken çağrı zincirinde aşağıya doğru görünür, yukarıya doğru görünmez. Çağırdığınız yardımcı fonksiyonların da kendi değişkenlerini local ile tanımlaması, bu tür sürprizlerin önüne geçer.
Değer verilmeyen local ve localvar_inherit

local x yazıp değer vermezseniz yeni yerel değişken başlangıçta ayarsızdır (unset). Dışarıdaki x değerini kopyalamaz, onu gölgeleyip yok gibi gösterir. shopt ile açılan localvar_inherit seçeneği bu davranışı değiştirir: kılavuza göre seçenek açıkken yerel değişken, yeni değer atanmadan önce önceki kapsamdaki aynı adlı değişkenin değerini ve niteliklerini devralır (isim referansı niteliği hariç).
x=dışarıdaki
goster() {
local x
echo "[${x-ayarsız}]"
}
goster # çıktı: [ayarsız]
shopt -s localvar_inherit
goster # çıktı: [dışarıdaki]${x-ayarsız} kalıbı değişken ayarsızsa yedek metni basar; bu genişletmeyi Bash parametre genişletme ve varsayılan değer yazısında anlatıyoruz. declare komutunun -I seçeneği de aynı devralmayı tek bir değişken için yapar. Seçeneğin varsayılan olarak kapalı olduğunu unutmayın; çoğu betik, yerel değişkenin boş başlamasına güvenir.
declare fonksiyon içinde yereldir, -g globaldir
Kılavuz, fonksiyon içinde kullanılan declare komutunun her adı local gibi yerel yaptığını, -g seçeneği verilirse değişkenin global kapsamda oluşturulduğunu söyler. Bu ayrıntı, declare -A ile ilişkisel dizi tanımlayan bir başlatma fonksiyonu yazarken önemlidir: -g unutulursa dizi fonksiyon bitince kaybolur.
f() {
declare a=1 # fonksiyon içinde declare = local
declare -g b=2 # -g: global kapsamda oluştur
}
f
echo "a=[${a-yok}] b=[$b]" # çıktı: a=[yok] b=[2]typeset komutu declare ile eş anlamlıdır ve aynı kural ona da uygulanır. İlişkisel dizilerin kullanımını Bash ilişkisel dizi yazısında bulabilirsiniz.
Tuzak: local ve komut ikamesi aynı satırda
local da bir komuttur ve kendi çıkış durumunu üretir. Kılavuza göre local, fonksiyon dışında kullanılmadıkça, geçersiz bir ad verilmedikçe veya salt okunur bir değişkene dokunulmadıkça 0 döndürür. Bu yüzden local degisken=$(komut) yazdığınızda $? komutun değil, local komutunun durumunu gösterir ve hata gizlenir:
oku() {
local icerik=$(false)
echo "aynı satırda local: $?" # çıktı: aynı satırda local: 0
local icerik2
icerik2=$(false)
echo "ayrı satırda: $?" # çıktı: ayrı satırda: 1
}
okuÇözüm, tanımı ve atamayı iki ayrı satıra bölmektir. Yalnız başına bir atama satırında, kılavuzun basit komut genişletmesi bölümüne göre çıkış durumu son komut ikamesinin durumudur; böylece $? ya da set -e hatayı yakalar. Aynı durum export, readonly ve declare için de geçerlidir. set -e ile birlikte bu tuzak daha da önemlidir, çünkü betik hatayı fark etmeden devam eder; ayrıntılar set -euo pipefail yazısında.
unset ve kapsamlar
Kılavuza göre unset da aynı dinamik kapsam kurallarıyla çalışır. Değişken geçerli fonksiyonda yerelse onu kaldırır; değilse çağrı zincirinde bulduğu ilk değişkeni kaldırır. Burada ince bir nokta var: unset önceki bir kapsamdaki yerel değişkene etki ederse, onun gölgelediği daha dıştaki değişken yeniden görünür hale gelir. Aşağıdaki örnekte g fonksiyonu, f fonksiyonunun yerel x değişkenini kaldırıyor:
x=global
f() {
local x=yerel
g
echo "f sonra: [${x-ayarsız}]"
}
g() {
unset x
echo "g içinde: [${x-ayarsız}]"
}
f
echo "global: [$x]"
# çıktı:
# g içinde: [global]
# f sonra: [global]
# global: [global]
shopt -s localvar_unset
f
# çıktı:
# g içinde: [ayarsız]
# f sonra: [ayarsız]İlk çağrıda g içinde ve f dönüşünde artık global değer görünüyor; yerel kopya silinince altındaki global açığa çıktı. localvar_unset seçeneği açıldığında ise kılavuzun anlattığı gibi önceki kapsamdaki yerel değişken o fonksiyon dönene kadar ayarsız olarak işaretleniyor ve iki satırda da ayarsız yazıyor. Bu farklar nadiren karşınıza çıkar, ama bir fonksiyonun kendisini çağıranın değişkenlerini unset ile temizlemesi gibi kalıplarda beklenmedik sonuçların nedenini açıklar.
local - ile kabuk seçeneklerini yerel yapmak
local komutuna ad yerine tek bir - verirseniz kabuk seçenekleri fonksiyona yerel olur. Kılavuza göre bu çağrıdan sonra fonksiyon içinde set ile değiştirilen seçenekler, fonksiyon dönünce eski değerlerine geri getirilir. Böylece bir fonksiyonun içinde set -u ya da set -x açıp kapatmayı unutma riski ortadan kalkar:
siki() {
local -
set -u
[[ $- == *u* ]] && echo "içeride nounset açık"
}
siki # çıktı: içeride nounset açık
[[ $- == *u* ]] || echo "dışarıda nounset kapalı" # çıktı: dışarıda nounset kapalı$- özel parametresi o an açık olan seçenek harflerini içerir; u harfi nounset seçeneğini gösterir. Kılavuz bu geri yüklemeyi yalnızca set ile değiştirilen seçenekler için anlatır. Denediğimizde local - sonrasında fonksiyon içinde shopt -s nullglob ile açılan seçenek, fonksiyon bittikten sonra da açık kaldı; shopt seçeneklerini kendiniz geri almanız gerekir. Seçeneklerin anlamları için set -euo pipefail ve shopt seçenekleri yazılarına bakın.
local -n ile isim referansı ve sonuç döndürme
Bir fonksiyonun sonucunu çağıranın seçtiği bir değişkene yazmasının en temiz yolu isim referansıdır. Kılavuz, -n niteliği verilen değişkene yapılan her okuma, atama ve nitelik değişikliğinin aslında değeri olan addaki değişkene uygulandığını anlatır. Fonksiyona değişkenin adını verirsiniz, fonksiyon local -n ref=$1 ile o ada bağlanır:
buyut() {
local -n _ref=$1
_ref=${_ref^^}
}
sehir=ankara
buyut sehir
echo "$sehir" # çıktı: ANKARA
ekle() {
local -n _dizi=$1
_dizi+=("$2")
}
liste=(elma armut)
ekle liste kiraz
echo "${liste[@]}" # çıktı: elma armut kirazİkinci örnek isim referansının dizilerle de çalıştığını gösteriyor. Kılavuz bir dizinin kendisine -n niteliği verilemeyeceğini, ama bir isim referansının bir diziyi ya da dizinin tek bir elemanını gösterebileceğini söyler. Böylece komut ikamesine ve alt kabuğa gerek kalmadan diziye eleman ekleyebiliyoruz. Dizilerin temelleri Bash dizi kullanımı yazısında.
İsim referansının en büyük tuzağı ad çakışmasıdır. Referansın kendi adı, göstermesini istediğiniz değişkenin adıyla aynıysa referans kendini gösterir. Bash 5.2.37 bu durumda uyarı veriyor ve referansı yok sayıp global değişkene yazıyor:
artir() {
local -n sayi=$1
sayi=$(( sayi + 1 ))
}
sayi=5
artir sayi
echo "sayi=$sayi"
# çıktı:
# ornek.sh: line 2: local: warning: sayi: circular name reference
# ornek.sh: line 2: warning: sayi: circular name reference
# ornek.sh: line 3: warning: sayi: circular name reference
# ornek.sh: line 3: warning: sayi: circular name reference
# sayi=6Sonuç bu örnekte tesadüfen doğru çıktı, ama dört uyarı satırı ve kapsamın karışması bu kodun güvenilmez olduğunu gösteriyor. Daha kötüsü, çağıranın kendi yerel değişkeni referansın adıyla aynıysa referans yanlış değişkeni gösterebilir. Yaygın önlem, referans adlarına _ref, _hedef gibi çağıranın kullanmayacağı bir önek ya da sonek vermektir. Daha önceki örneklerde bu yüzden _ref ve _dizi adlarını seçtik. İsim referanslarına alternatif olan ${!ad} dolaylı genişletmesini ve eval risklerini Bash eval ve dolaylı değişken yazısında anlatıyoruz.
FUNCNAME ile çağrı yığını
FUNCNAME o an çalışmakta olan fonksiyonların adlarını tutan bir dizidir. Kılavuza göre 0 numaralı eleman şu anki fonksiyondur, en yüksek indeksteki eleman main olur ve değişken yalnızca bir fonksiyon çalışırken vardır. Fonksiyon dışında genişletildiğinde boş gelir:
uc() { echo "yığın: ${FUNCNAME[*]}"; }
iki() { uc; }
bir() { iki; }
bir # çıktı: yığın: uc iki bir main
echo "dışarıda: [${FUNCNAME[*]}]" # çıktı: dışarıda: []Bu dizi hata mesajlarına bağlam eklemek için kullanışlıdır: echo "${FUNCNAME[1]} beni yanlış argümanla çağırdı" >&2 gibi bir satır, hangi fonksiyonun hatalı çağrı yaptığını söyler. Kılavuz FUNCNAME elemanlarının BASH_SOURCE ve BASH_LINENO dizilerindeki elemanlarla eşleştiğini de belirtir; bu üçüyle tam bir çağrı izi basılabilir. FUNCNAME değişkenine atama yapmak etkisizdir, unset ile silinirse özel davranışını kaybeder. Çağrı izi ve caller komutu Bash script hata ayıklama yazısında ayrıntılı olarak var.
Uygulamalı örnek: iki sonuç döndüren fonksiyon
Şimdi öğrendiklerimizi birleştirelim. ayir fonksiyonu ad:soyad biçimindeki bir metni ikiye bölüyor ve parçaları çağıranın verdiği iki değişkene yazıyor. Metni tutan metin değişkeni yerel olduğu için fonksiyon bitince dışarıda iz bırakmıyor:
ayir() {
local -n _bas=$2 _son=$3
local metin=$1
_bas=${metin%%:*}
_son=${metin#*:}
}
ayir "Ada:Lovelace" ad soyad
echo "$ad / $soyad" # çıktı: Ada / Lovelace
declare -p metin 2>/dev/null || echo "metin dışarıya sızmadı" # çıktı: metin dışarıya sızmadılocal -n _bas=$2 _son=$3 satırı tek komutta iki referans tanımlıyor. ${metin%%:*} ilk iki noktadan sonrasını, ${metin#*:} ilk iki noktaya kadar olan kısmı siliyor; bu kalıpları Bash string işlemleri yazısında ayrıntılı anlatıyoruz. Son satırdaki declare -p metin değişken yoksa hata verdiği için || sonrasındaki mesaj basılıyor. Aynı işi komut ikamesiyle yapmak için iki ayrı çağrı ya da çıktıyı yeniden bölmek gerekirdi; isim referansı bu yükü ortadan kaldırıyor.
Hızlı başvuru tablosu

| Yazım | Etkisi |
|---|---|
| local x=1 | Fonksiyona ve çağırdığı fonksiyonlara görünen yerel değişken |
| local x | Yerel ve başlangıçta ayarsız (localvar_inherit kapalıyken) |
| declare x=1 (fonksiyonda) | local ile aynı |
| declare -g x=1 | Fonksiyon içinden global değişken |
| local - | set seçenekleri fonksiyon dönünce geri yüklenir |
| local -n r=$1 | Adı $1 olan değişkene isim referansı |
| unset -n r | Referansın gösterdiği değişkeni değil, referansın kendisini siler |
| ${FUNCNAME[0]} | Şu an çalışan fonksiyonun adı |
Sık yapılan hatalar
- Yardımcı fonksiyonlarda döngü sayacını
localyapmamak: dıştaki döngünün sayacı bozulur. local x=$(komut)yazıp$?kontrol etmek: durum her zamanlocalkomutuna aittir.localkomutunu betiğin en üstünde kullanmak: yalnızca fonksiyon içinde geçerlidir.- Başlatma fonksiyonunda
declare -Ayazıp-geklememek: dizi fonksiyonla birlikte kaybolur. - İsim referansına hedef değişkenle aynı adı vermek:
circular name referenceuyarısı ve yanlış değişkene yazma. - Yerel değişkenin tamamen gizli olduğunu sanmak: çağrılan fonksiyonlar dinamik kapsam yüzünden onu görür ve değiştirebilir.
Sık Sorulan Sorular
local ile declare arasında fark var mı?
Fonksiyon içinde declare ad=deger ve local ad=deger aynı işi yapar. Fark, declare komutunun fonksiyon dışında da kullanılabilmesi ve -g seçeneğiyle fonksiyon içinden global değişken oluşturabilmesidir. local fonksiyon dışında hata verir; bu da niyetinizi kodda açıkça gösterir.
Alt kabukta değiştirilen değişken neden dışarıya yansımıyor?
Bu kapsamla değil, süreçle ilgilidir. Komut ikamesi, ( ) grubu ve pipe içindeki komutlar kabuğun bir kopyasında çalışır; kopyadaki değişiklikler ana kabuğa dönmez. Ayrıntılar Bash subshell yazısında.
Global değişkene fonksiyon içinden nasıl atama yaparım?
O adla yerel bir değişken yoksa düz bir atama zaten globale gider. Çağrı zincirinde aynı adla bir yerel değişken varsa atama ona gider; bu durumda declare -g ad=deger yazmak gerekir.
İsim referansı hangi Bash sürümünde var?
Bu yazıdaki örnekler Bash 5.2.37 ile çalıştı ve kılavuzun 5.3 sürümü de -n niteliğini anlatıyor. Daha eski bir sistemde çalışacaksanız sürümü Bash sürümünü öğrenme yazısındaki yöntemlerle kontrol edin.
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 · Fonksiyon tanımlama · declare ve readonly · eval ve dolaylı değişken · Subshell · Hata ayıklama