source dosya.sh ve onun kısa eşi . dosya.sh, verilen dosyadaki komutları mevcut kabukta okuyup çalıştırır. Dosyayı bash dosya.sh veya ./dosya.sh ile çalıştırdığınızda yeni bir kabuk süreci açılır ve orada tanımlanan değişkenler, fonksiyonlar ya da yapılan cd işlemleri süreç bittiğinde kaybolur. source ile ise hepsi çağıran kabukta kalır. Ayar dosyası yüklemek, ortak fonksiyonları bir kütüphane dosyasından almak ve ~/.bashrc değişikliklerini kabuğu kapatmadan etkinleştirmek bu yüzden source ile yapılır.

Bu yazıda source ile . komutunun nasıl çalıştığını, dosyayı nerede aradığını, argüman ve return davranışını, bir dosyanın doğrudan mı çalıştırıldığını yoksa source mu edildiğini BASH_SOURCE ile anlamayı ve kütüphane dosyalarını betiğin konumuna göre yüklemeyi anlatıyoruz. Betik çalıştırmanın temelleri için Bash script yazma ve çalıştırma yazısına, serinin tamamı için Bash öğrenme rehberi sayfasına bakabilirsiniz.

source ile bash dosya.sh arasındaki fark

source ile bash dosya.sh arasındaki fark: bash dosya.sh, ./dosya.sh, source dosya.sh, . dosya.sh

Kılavuz . komutunu "dosyadaki komutları mevcut kabuk bağlamında okuyup çalıştırır" diye tanımlar ve source komutunun onun eş anlamlısı olduğunu belirtir. İkisi arasında Bash'te davranış farkı yoktur; . POSIX standardındaki addır, source ise Bash'in okunabilirlik için sunduğu addır. Taşınabilir bir sh betiği yazıyorsanız . kullanın; ayrıntı için Bash ve sh farkı yazısına bakın.

Aşağıdaki örnekte aynı ayar dosyası önce bash ile, sonra source ile çalıştırılıyor. İlk durumda değişken yeni bir süreçte tanımlanıp o süreçle birlikte yok oluyor; ikinci durumda hem değişken hem de fonksiyon çağıran betikte kullanılabiliyor:

bash
#!/usr/bin/env bash
cat > ./ayarlar.sh <<'EOF'
SUNUCU_ADI="web-01"
selamla() { echo "Merhaba, $1"; }
EOF
bash ./ayarlar.sh
echo "bash ile: [${SUNUCU_ADI-}]"
# çıktı: bash ile: []
source ./ayarlar.sh
echo "source ile: [$SUNUCU_ADI]"
# çıktı: source ile: [web-01]
selamla "Ayşe"
# çıktı: Merhaba, Ayşe

Aynı fark çalışma dizini için de geçerlidir. Bir betiğin içinde yapılan cd, betik ayrı bir süreç olarak çalıştığında sizin kabuğunuzun dizinini değiştirmez. Dizini gerçekten değiştiren bir "kısayol betiği" yazmak istiyorsanız onu source etmeniz gerekir:

Source işleminin bir yan etkisi de çakışmalardır. Dosyadaki her atama ve fonksiyon tanımı, çağıran kabukta aynı adla var olan değişkenin veya fonksiyonun üzerine yazar. Ayar dosyasında PATH, IFS ya da sizin betiğinizde de kullanılan dizin gibi genel bir adı değiştiren bir satır, betiğinizin geri kalanını sessizce etkiler. Kütüphane dosyalarında değişken ve fonksiyon adlarına ortak bir önek (örneğin log_) vermek bu riski azaltır.

bash
#!/usr/bin/env bash
mkdir -p ./hedef
baslangic=$PWD
cat > ./git.sh <<'EOF'
cd ./hedef || return
EOF
bash ./git.sh
[[ $PWD == "$baslangic" ]] && echo "bash sonrası: dizin değişmedi"
# çıktı: bash sonrası: dizin değişmedi
. ./git.sh
echo "nokta sonrası: ${PWD##*/}"
# çıktı: nokta sonrası: hedef

Bu örnekte cd başarısız olursa exit yerine return kullanıldığına dikkat edin. Source edilen bir dosyada exit yazmak, dosyayı çağıran kabuğun tamamını kapatır; bunu aşağıda ayrıca ele alıyoruz.

Çalıştırma biçimiYeni süreçDeğişken ve fonksiyonlarcd etkisiÇalıştırma izni
bash dosya.shEvetÇağırana geçmezÇağırana geçmezGerekmez
./dosya.shEvetÇağırana geçmezÇağırana geçmezGerekir (chmod +x)
source dosya.shHayırÇağıranda kalırÇağıranda kalırGerekmez
. dosya.shHayırÇağıranda kalırÇağıranda kalırGerekmez

Tablodaki son sütun önemlidir: kılavuz, . ile okunacak dosyanın çalıştırılabilir olmasına gerek olmadığını açıkça söyler. Dosya yalnızca okunabilir olmalıdır. Çalıştırma izni ve chmod +x ayrıntıları için Linux dosya izinleri rehberine bakabilirsiniz.

Dosya nerede aranır?

Dosya adında eğik çizgi (/) varsa, örneğin ./ayar.sh veya /opt/uygulama/env.sh, Bash o yolu doğrudan kullanır. Eğik çizgi yoksa durum değişir: Bash dosyayı önce PATH değişkenindeki dizinlerde arar. POSIX modunda değilken orada bulamazsa geçerli dizine de bakar. Bu arama varsayılan olarak açık olan sourcepath seçeneğine bağlıdır; seçenek kapatılırsa PATH hiç taranmaz:

bash
#!/usr/bin/env bash
mkdir -p ./kutuphane
echo 'echo "kutuphane/yardimci.sh yüklendi"' > ./kutuphane/yardimci.sh
echo 'echo "geçerli dizindeki yerel.sh yüklendi"' > ./yerel.sh
PATH="$PWD/kutuphane:$PATH"
source yardimci.sh
# çıktı: kutuphane/yardimci.sh yüklendi
source yerel.sh
# çıktı: geçerli dizindeki yerel.sh yüklendi
shopt -u sourcepath
source yardimci.sh
# çıktı: ornek.sh: line 11: yardimci.sh: No such file or directory

Son satırda sourcepath kapatıldığı için yardimci.sh artık PATH içinde aranmadı ve geçerli dizinde de bulunmadığı için hata oluştu. Buradan çıkan pratik ders şu: source ayar.sh gibi eğik çizgisiz yazımlar, PATH içinde aynı adlı başka bir dosya varsa sürpriz yapabilir. Kendi dosyanızı yüklediğinizden emin olmak için her zaman ./ayar.sh veya tam yol yazın. POSIX modunda geçerli dizin hiç aranmaz; bu da aynı yazımın sh altında farklı sonuç vermesinin bir nedenidir.

Kılavuzun 5.3 sürümü . ve source için bir de -p yol seçeneği anlatıyor: verilen iki nokta üst üste ile ayrılmış dizin listesi, arama için PATH yerine kullanılır. Kılavuz (5.3) bunu anlatıyor; 5.2.37 ile denediğimizde çalışmadı, Bash source: -p: invalid option hatası verdi. Bu yüzden aşağıdaki satırı yalnızca 5.3 ve sonrası için bir bilgi olarak gösteriyoruz:

bash
# Yalnızca Bash 5.3 kılavuzunda anlatılıyor; 5.2.37'de çalışmadı (çalıştırılmadı)
source -p ./kutuphane yardimci.sh

Source edilen dosyaya argüman vermek

source dosya argüman1 argüman2 yazarsanız argümanlar, dosya çalışırken konumsal parametreler ($1, $2, $#) olur. Argüman vermezseniz dosya, çağıranın konumsal parametrelerini olduğu gibi görür. Denediğimiz sürümde argümanlı çağrıdan sonra çağıranın parametreleri eski hâline döndü:

bash
#!/usr/bin/env bash
cat > ./argli.sh <<'EOF'
echo "içeride: $# argüman, ilki: $1"
EOF
set -- dış1 dış2 dış3
source ./argli.sh iç1 iç2
# çıktı: içeride: 2 argüman, ilki: iç1
echo "dışarıda: $# argüman, ilki: $1"
# çıktı: dışarıda: 3 argüman, ilki: dış1
source ./argli.sh
# çıktı: içeride: 3 argüman, ilki: dış1

Üçüncü çağrıda argüman verilmediği için dosya, betiğin kendi dış1 dış2 dış3 parametrelerini gördü. Bu, kütüphane dosyalarının istemeden çağıranın argümanlarına bakmasına yol açabilir; kütüphanelerin konumsal parametrelere dayanmamasını, ihtiyaç duyduğu değerleri fonksiyon argümanı olarak almasını öneririz. Konumsal parametrelerin ayrıntıları için betik argümanları ve özel değişkenler yazısına bakın.

return ile erken çıkmak, exit ile kabuğu kapatmak

return ile erken çıkmak, exit ile kabuğu kapatmak

Kılavuz, return komutunun yalnızca fonksiyonlarda değil, . veya source ile çalıştırılan dosyalarda da kullanılabileceğini söyler. Source edilen dosyada return n, dosyanın geri kalanını atlar ve source komutunun çıkış kodu n olur. n verilmezse son çalışan komutun kodu döner:

bash
#!/usr/bin/env bash
cat > ./kontrol.sh <<'EOF'
if [[ -z ${API_ANAHTARI-} ]]; then
  echo "API_ANAHTARI tanımlı değil, dosyanın geri kalanı atlanıyor"
  return 3
fi
echo "ayarlar yüklendi"
EOF
source ./kontrol.sh
echo "\$? = $?"
# çıktı:
#   API_ANAHTARI tanımlı değil, dosyanın geri kalanı atlanıyor
#   $? = 3
API_ANAHTARI=abc source ./kontrol.sh
# çıktı: ayarlar yüklendi

Son satırdaki API_ANAHTARI=abc source ./kontrol.sh yazımı, değişkeni yalnızca o komut için tanımlar. exit ise bambaşka çalışır: source edilen dosya çağıranla aynı süreçte olduğundan exit çağıranın tamamını sonlandırır. Betik içinde bu, betiğin aniden bitmesi demektir; etkileşimli bir terminalde source ettiğiniz dosyadaki exit ise terminal oturumunuzu kapatır:

bash
#!/usr/bin/env bash
cat > ./kotu.sh <<'EOF'
exit 5
EOF
source ./kotu.sh
echo "bu satıra hiç gelinmez"
# (çıktı yok; betiğin çıkış kodu 5 olur)

Dosya bulunamazsa ne olur?

Kılavuza göre dosya bulunamaz veya okunamazsa source sıfırdan farklı bir kodla döner. Varsayılan modda betik bir sonraki satırdan devam eder; bu, ayar dosyası eksik olduğu hâlde betiğin boş değişkenlerle çalışmayı sürdürmesi anlamına gelebilir:

bash
#!/usr/bin/env bash
source ./yok.sh
echo "\$? = $?, betik devam ediyor"
# çıktı:
#   ornek.sh: line 2: ./yok.sh: No such file or directory
#   $? = 1, betik devam ediyor

POSIX modunda ise etkileşimli olmayan kabuk, . ile verilen dosya bulunamadığında doğrudan çıkar. Kılavuzun POSIX modu listesinde bu açıkça yer alıyor ve denememiz de bunu doğruladı:

bash
#!/usr/bin/env bash
set -o posix
. ./yok.sh
echo "POSIX modunda bu satır çalışmaz"
# çıktı: ornek.sh: line 3: ./yok.sh: No such file or directory
# $? = 1 (betik burada sonlandı)

Varsayılan modda da aynı sağlamlığı istiyorsanız dönüş kodunu kendiniz kontrol edin: source ./ayar.sh || exit 1. set -e açık bir betikte başarısız source komutu zaten betiği durdurur; bu seçeneğin istisnaları için set -euo pipefail yazısına bakın.

Doğrudan mı çalıştırıldı, source mu edildi?

Bir dosyayı hem bağımsız bir betik hem de kütüphane olarak kullanmak isteyebilirsiniz: doğrudan çalıştırıldığında bir ana fonksiyonu işletsin, source edildiğinde yalnızca fonksiyonlarını tanımlasın. BASH_SOURCE dizisi, o anda çalışan kodun hangi dosyadan geldiğini tutar. ${BASH_SOURCE[0]} geçerli dosyanın adıdır, $0 ise kabuğun ya da ana betiğin adıdır. Dosya doğrudan çalıştırıldığında ikisi aynıdır; source edildiğinde farklıdır:

bash
#!/usr/bin/env bash
cat > ./arac.sh <<'EOF'
topla() { echo $(( $1 + $2 )); }
ana() {
  echo "doğrudan çalıştırıldı, sonuç: $(topla 2 3)"
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  ana "$@"
fi
EOF
bash ./arac.sh
# çıktı: doğrudan çalıştırıldı, sonuç: 5
source ./arac.sh
echo "source edildi, ana çalışmadı; topla: $(topla 4 6)"
# çıktı: source edildi, ana çalışmadı; topla: 10

Bu kalıp sayesinde arac.sh dosyasındaki topla fonksiyonunu başka betiklerde yeniden kullanabilir, aynı dosyayı komut satırından çalıştırarak da test edebilirsiniz. Karşılaştırmada her iki tarafı da çift tırnak içine almak, boşluklu dosya adlarında yanlış sonuç almanızı önler.

Kütüphaneyi betiğin bulunduğu dizine göre yüklemek

source ./lib/log.sh yazımı geçerli çalışma dizinine göredir. Betik başka bir dizinden çağrılırsa (örneğin cron veya systemd ile) bu yol tutmaz. Çözüm, kütüphane yolunu betiğin kendi konumundan türetmektir. ${BASH_SOURCE[0]%/*} genişletmesi dosya yolundaki son eğik çizgiden sonrasını atar ve dizini verir. Yolda hiç eğik çizgi yoksa (betik bash calistir.sh ile çağrıldıysa) genişletme adı değiştirmeden döndürür; örnekteki ikinci satır bu durumu yakalayıp dizini . yapar:

bash
#!/usr/bin/env bash
mkdir -p ./proje/lib
cat > ./proje/lib/log.sh <<'EOF'
log() { echo "[log] $*"; }
EOF
cat > ./proje/calistir.sh <<'EOF'
betik_dizini=${BASH_SOURCE[0]%/*}
[[ $betik_dizini == "${BASH_SOURCE[0]}" ]] && betik_dizini=.
source "$betik_dizini/lib/log.sh"
log "kütüphane $betik_dizini/lib altından yüklendi"
EOF
bash ./proje/calistir.sh
# çıktı: [log] kütüphane ./proje/lib altından yüklendi
cd ./proje && bash calistir.sh
# çıktı: [log] kütüphane ./lib altından yüklendi

Bu yöntem sembolik bağlantıları çözmez; betik bir bağlantı üzerinden çağrılırsa bağlantının bulunduğu dizin kullanılır. Kılavuz 5.3, BASH_SOURCE içindeki adların tam yola çevrilmesini sağlayan bash_source_fullpath adlı bir shopt seçeneği anlatıyor; ancak bu seçenek denediğimiz 5.2.37'de yok. Dizin genişletmelerinin ayrıntısı için string işlemleri yazısına bakabilirsiniz. Betiklerinizi zamanlanmış görev olarak çalıştıracaksanız cron job rehberi de işinize yarar.

Aynı dosyanın iki kez yüklenmesini önlemek

Birden fazla kütüphane aynı ortak dosyayı source ettiğinde, o dosyadaki kodlar iki kez çalışır. Değişkenlere başlangıç değeri atayan ya da readonly tanımlayan bir dosya için bu sorun yaratır, çünkü salt okunur değişkene ikinci atama hata verir. Basit bir "yüklendi" bayrağı ve return ile dosyanın ikinci kez çalışmasını engelleyebilirsiniz:

bash
#!/usr/bin/env bash
cat > ./ortak.sh <<'EOF'
[[ -n ${_ORTAK_YUKLENDI-} ]] && return 0
_ORTAK_YUKLENDI=1
echo "ortak.sh ilk kez yükleniyor"
EOF
source ./ortak.sh
# çıktı: ortak.sh ilk kez yükleniyor
source ./ortak.sh
echo "ikinci source sessiz geçti"
# çıktı: ikinci source sessiz geçti

Bayrak değişkeni için ${_ORTAK_YUKLENDI-} yazımı bilinçli bir seçim: tire ile verilen boş varsayılan, betik set -u ile çalışsa bile tanımsız değişken hatası almadan kontrol yapmanızı sağlar. Bu genişletme ailesi için parametre genişletme ve varsayılan değer yazısına bakabilirsiniz.

Bash başlangıç dosyaları da source mantığıyla çalışır

source yalnızca sizin yazdığınız betiklerde değil, Bash'in kendi başlangıç sürecinde de kullanılır. Kılavuz, tipik bir ~/.bash_profile dosyasının if [ -f ~/.bashrc ]; then . ~/.bashrc; fi satırını içerdiğini söyler; böylece giriş kabuğu (login shell) da ~/.bashrc içindeki ayarları alır. Etkileşimli olmayan bir kabuk, yani bir betik başlarken ise Bash ortamda BASH_ENV değişkeni olup olmadığına bakar ve varsa, sanki . "$BASH_ENV" çalıştırılmış gibi o dosyayı okur. Tek fark, bu dosya adı için PATH araması yapılmamasıdır. Yani bir sunucuda BASH_ENV tanımlıysa, her betiğiniz kendi ilk satırından önce o dosyayı mevcut kabukta çalıştırır. Beklenmeyen bir fonksiyon veya değişken görüyorsanız bu değişkeni kontrol etmeye değer.

Source edilen dosyalar hata ayıklama araçlarında da ayrı bir çağrı katmanı olarak görünür. Kılavuz, caller komutunun ve FUNCNAME, BASH_SOURCE, BASH_LINENO dizilerinin hem fonksiyonları hem de . veya source ile çalıştırılan betikleri "alt rutin" olarak saydığını belirtir. RETURN tuzağı (trap) da source edilen dosya bittiğinde çalışır. Bu araçların kullanımı için hata ayıklama ve set -x yazısına ve trap rehberine bakabilirsiniz.

Sık yapılan hatalar

Sık yapılan hatalar: Ayar dosyasını bash ile çalıştırıp değişkenleri beklemek, Eğik çizgisiz dosya adı, Source edilen dosyada exit, Eksik dosyayı fark etmemek
  • Ayar dosyasını bash ile çalıştırıp değişkenleri beklemek: Yeni süreçteki değişkenler geri gelmez; source kullanın.
  • Eğik çizgisiz dosya adı: source ayar.sh, önce PATH içinde arar. ./ayar.sh yazın.
  • Source edilen dosyada exit: Çağıran betiği veya terminal oturumunu kapatır; return kullanın.
  • Eksik dosyayı fark etmemek: Varsayılan modda betik devam eder; source ./ayar.sh || exit 1 yazın.
  • Güvenilmeyen dosyayı source etmek: Dosyadaki her satır sizin kabuğunuzda, sizin yetkilerinizle çalışır. Başkalarının yazabildiği bir dosyayı source etmek, o kişiye kabuğunuzda komut çalıştırma imkânı verir.

Sık Sorulan Sorular

source ile . arasında fark var mı?

Bash'te yoktur; kılavuz source için yalnızca ". ile eş anlamlı" der. sh veya POSIX uyumlu betiklerde . yazmak daha taşınabilirdir.

.bashrc değişikliğini nasıl hemen etkinleştiririm?

source ~/.bashrc veya . ~/.bashrc komutu dosyayı mevcut kabukta yeniden okur. Hangi dosyanın ne zaman okunduğu için bashrc ve bash_profile farkı yazısına bakın.

Source edilen dosyanın çalıştırma izni olmalı mı?

Hayır. Kılavuza göre dosyanın çalıştırılabilir olması gerekmez, okunabilir olması yeterlidir.

source komutunun çıkış kodu nedir?

Dosyada çalışan son komutun kodudur; hiçbir komut çalışmadıysa 0'dır. Dosya bulunamaz veya okunamazsa sıfırdan farklıdır. return n kullanılırsa n değerinin en düşük 8 biti döner.

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 · Bash script yazma ve çalıştırma · Bash fonksiyon tanımlama · subshell ve çalışma ortamı · bashrc ve bash_profile farkı · exit kodları