Bash betiği (script), kabuk komutlarını içeren düz bir metin dosyasıdır. En basit haliyle bir dosyaya komutları yazar, ilk satıra #!/usr/bin/env bash gibi bir shebang satırı ekler ve dosyayı bash dosya.sh komutuyla çalıştırırsınız. Dosyaya çalıştırma izni verdiğinizde ./dosya.sh biçiminde doğrudan da çağırabilirsiniz; bu durumda hangi yorumlayıcının kullanılacağını shebang satırı belirler.
Bu yazıda ilk betiği adım adım yazıyor, iki çalıştırma yöntemi arasındaki farkı, betiğin neden yeni bir kabukta çalıştığını, çıkış durumunun nereden geldiğini ve betik içinde $0 değerinin ne gösterdiğini inceliyoruz. Seride sonraki adımlar için Bash öğrenme rehberi sayfasına dönebilirsiniz.
İlk betiğinizi yazın
Bir metin düzenleyiciyle merhaba.sh adında bir dosya oluşturun ve aşağıdaki satırları yazın. Kılavuz bir dosya uzantısı şartı koymaz; .sh uzantısı yalnızca dosyanın ne olduğunu insanlara anlatan bir alışkanlıktır.
#!/usr/bin/env bash
# merhaba.sh: ilk betiğimiz
ad="${1:-dünya}"
echo "Merhaba, $ad!"
echo "Betik adı: $0"İlk satır shebang satırıdır; birazdan ayrıntısına gireceğiz. # ile başlayan ikinci satır bir yorumdur ve kabuk tarafından yok sayılır. Kılavuza göre etkileşimsiz bir kabukta # ile başlayan bir kelime, satırın geri kalanıyla birlikte yorum sayılır; kelimenin başı satır başı, tırnaksız bir boşluğun hemen sonrası veya bir operatörün hemen sonrasıdır. Bu yüzden echo a#b komutunda # yorum başlatmaz, ama echo a #b komutunda başlatır.
Üçüncü satır, betiğe verilen ilk argümanı ad değişkenine atar; argüman verilmezse "dünya" değerini kullanır. Bu ${1:-dünya} biçimi bir parametre genişletmesidir ve varsayılan değer yazısında ayrıntılı anlatılır. Son iki satır ekrana iki satır basar.
$ bash merhaba.sh
Merhaba, dünya!
Betik adı: merhaba.sh
$ bash merhaba.sh Ayşe
Merhaba, Ayşe!
Betik adı: merhaba.sh
$ chmod +x merhaba.sh
$ ./merhaba.sh Ayşe
Merhaba, Ayşe!
Betik adı: ./merhaba.shÇıktıdaki son satıra dikkat edin: $0, betiği çağırırken kullandığınız adı gösterir. bash merhaba.sh ile çalıştırdığınızda değeri merhaba.sh, ./merhaba.sh ile çalıştırdığınızda ./merhaba.sh olur. Kılavuz bunu şöyle tarif eder: Bash bir dosyadan komut okuyarak başlatıldığında $0 o dosyanın adına, konumsal parametreler ise geri kalan argümanlara ayarlanır. Argüman verilmezse konumsal parametreler tanımsız kalır.
Shebang satırı ne işe yarar?

Betiğin ilk satırı #! karakterleriyle başlıyorsa, satırın geri kalanı programı yorumlayacak olan yorumlayıcıyı belirtir. Kılavuz, GNU sisteminin ve çoğu Unix sürümünün bunu işletim sisteminin komut çalıştırma mekanizmasının parçası yaptığını belirtir; işletim sistemi bunu yapmıyorsa Bash kendisi yapar. Böylece aynı yöntemle bir Bash betiği, bir awk programı veya bir Perl betiği yazabilirsiniz.
Kılavuzda iki yaygın yazım geçer. #!/bin/bash, Bash'in /bin dizininde kurulu olduğunu varsayar. #!/usr/bin/env bash ise env programı aracılığıyla PATH içindeki ilk bash programını bulur; Bash başka bir dizine kurulmuş olsa bile çalışır. Kılavuz ayrıca bazı eski Unix sürümlerinin yorumlayıcı adını ve tek bir argümanı toplam 32 karakterle sınırladığını, bu yüzden shebang satırında birden fazla argüman kullanmanın taşınabilir olmadığını hatırlatır.
| Shebang | Ne olur | Not |
|---|---|---|
| #!/bin/bash | Doğrudan /bin/bash çalışır | Bash başka dizindeyse bulunamaz |
| #!/usr/bin/env bash | PATH içindeki ilk bash çalışır | Kılavuzda yaygın deyim olarak geçer |
| #!/bin/sh | Sistemdeki sh çalışır | Bash özellikleri bulunmayabilir |
| Shebang yok | Çalıştıran kabuk betiği yorumlar | Hangi kabuğun çalışacağı belirsizleşir |
bash dosya.sh ile ./dosya.sh arasındaki fark
İki yöntemin sonucu çoğu zaman aynıdır, ama çalışma biçimleri farklıdır. bash dosya.sh yazdığınızda yorumlayıcıyı siz seçersiniz: Bash çalışır ve dosyayı okur; shebang satırı yalnızca bir yorum olarak görülür. Kılavuza göre bu durumda dosya adında eğik çizgi yoksa Bash dosyayı önce bulunduğunuz dizinde, bulamazsa PATH dizinlerinde arar. Dosyanın çalıştırma izni olması gerekmez.
./dosya.sh yazdığınızda ise dosyayı bir program gibi çalıştırırsınız. Bunun için dosyanın çalıştırma biti açık olmalıdır; izin konusunun ayrıntılarını chmod rehberinde anlattık, burada chmod +x dosya.sh komutunun yeterli olduğunu söylemekle yetinelim. Kılavuz, çalıştırılabilir bir kabuk betiğini dosya argümanlar biçiminde çağırmanın bash dosya argümanlar ile eşdeğer olduğunu söyler. Yeni kabuk kendini sıfırdan başlatır; tek istisna, üst kabuğun hatırladığı komut konumlarının (hash tablosu) alt kabuğa aktarılmasıdır.
Komut adında eğik çizgi bulunmuyorsa (örneğin yalnızca dosya.sh yazarsanız) kabuk komutu fonksiyonlarda, yerleşik komutlarda ve ardından PATH dizinlerinde arar. Bulunduğunuz dizin genellikle PATH içinde olmadığından komut bulunamaz; bu yüzden ./ önekini yazarsınız. Dosya bulunur ama çalıştırılabilir biçimde değilse ve bir dizin de değilse, kılavuza göre kabuk onu bir kabuk betiği kabul eder ve kendisi yorumlar. Bash ayrıca dosyanın metin mi ikili (binary) dosya mı olduğunu anlamaya çalışır ve ikili saydığı dosyaları betik olarak çalıştırmaz.
Kabuk bir betiği nasıl okur?

Betik yazarken kabuğun girdiyi hangi sırayla işlediğini bilmek, hata mesajlarını anlamayı kolaylaştırır. Kılavuz bu işlemi kabaca yedi adımda özetler. Kabuk önce girdiyi bir dosyadan, -c ile verilen bir dizgeden veya terminalden okur. Ardından girdiyi tırnaklama kurallarına uyarak kelimelere ve operatörlere ayırır; takma ad genişletmesi bu adımda yapılır. Üçüncü adımda bu parçaları basit ve bileşik komutlara dönüştürür. Dördüncü adımda genişletmeleri uygular, beşinci adımda yönlendirmeleri yapar ve yönlendirme işaretlerini argüman listesinden çıkarır. Son olarak komutu çalıştırır ve gerekiyorsa bitmesini bekleyip çıkış durumunu toplar.
Bu sıranın pratik bir sonucu vardır: sözdizimi hataları ile genişletme hataları farklı zamanlarda ortaya çıkar. Kapanmamış bir if veya tırnak, kabuğun komutu tamamlayabilmek için okumayı sürdürmesine yol açar ve hata çoğu zaman dosyanın sonunda raporlanır; aşağıdaki bash -n örneğinde bunu göreceksiniz. Genişletme hataları ise ancak o komut çalıştırılırken ortaya çıkar; bu yüzden betiğin ilk yarısı sorunsuz çalışıp ikinci yarısında "bad substitution" gibi bir hatayla durabilir. Yönlendirmelerin genişletmelerden sonra yapılması da bir sonuçtur: > "$dosya" yazdığınızda önce değişken genişletilir, sonra dosya açılır; değişken boşsa hata yönlendirme adımında ortaya çıkar.
Genişletmelerin hangi sırayla yapıldığı brace expansion ve tilde yazısında, komut listeleri ve &&, || operatörleri ise komut listeleri yazısında anlatılıyor.
Betik ayrı bir kabukta çalışır
Yeni başlayanların en sık şaşırdığı nokta, betik içinde yapılan değişikliklerin betiği çağıran kabuğa yansımamasıdır. Betikte bir değişken atarsanız veya cd ile dizin değiştirirseniz, betik bittiğinde terminalinizdeki kabuk eskisi gibi kalır. Kılavuz bunu çalışma ortamı kavramıyla açıklar: ayrı bir ortamda çalıştırılan bir komut, kabuğun kendi ortamını etkileyemez. Aynı dosyayı source (ya da .) ile çalıştırırsanız komutlar mevcut kabukta yürütülür ve değişiklikler kalıcı olur.
printf 'sayac=10\ncd /\n' > ayar.sh
sayac=1
bash ayar.sh
echo "bash ile: sayac=$sayac, dizin kökte mi: $([[ $PWD == / ]] && echo evet || echo hayır)"
# çıktı: bash ile: sayac=1, dizin kökte mi: hayır
. ./ayar.sh
echo "source ile: sayac=$sayac, dizin: $PWD"
# çıktı: source ile: sayac=10, dizin: /Bu ayrım bilinçli bir tasarımdır: bir betik, sizin kabuk oturumunuzu habersizce değiştirmemelidir. Ortam değişkenlerini veya fonksiyonları mevcut kabuğa yüklemek istediğiniz durumlar için source ve nokta komutu yazısına, alt kabuk ve çalışma ortamının ayrıntıları için subshell yazısına bakın.
Betiğin çıkış durumu
Kılavuza göre Bash bir betiği çalıştırdıktan sonra, betikte çalışan son komutun çıkış durumuyla sonlanır; hiç komut çalışmadıysa çıkış durumu 0 olur. Betiği exit n ile bitirirseniz çıkış durumu n olur. Betiği çağıran kabukta bu değeri $? özel parametresiyle okursunuz. 0 başarı, sıfırdan farklı her değer başarısızlık anlamına gelir.
printf 'echo çalıştı\nfalse\n' > son.sh
bash son.sh
# çıktı: çalıştı
# $? = 1
printf 'echo çalıştı\nexit 3\n' > cik.sh
bash cik.sh
# çıktı: çalıştı
# $? = 3Birinci betik başarılı bir echo komutundan sonra false çalıştırdığı için 1 ile biter; ikinci betik exit 3 ile açıkça 3 döndürür. Bir betik başka bir betik veya bir zamanlanmış görev tarafından çağrılıyorsa, bu değer işin başarılı olup olmadığını anlamanın tek güvenilir yoludur. Çıkış kodlarının anlamları ve 126, 127 gibi özel değerler exit kodları yazısında anlatılıyor.
Çalıştırmadan sözdizimi denetimi: bash -n
set yerleşik komutunun tüm tek harfli seçenekleri Bash başlatılırken de kullanılabilir. Bunlardan -n, komutları okur ama çalıştırmaz; böylece bir betiği hiçbir şey yapmadan sözdizimi hatalarına karşı denetleyebilirsiniz. Aşağıdaki dosyada if yapısını kapatan fi eksik:
printf 'if true\nthen echo tamam\n' > bozuk.sh
bash -n bozuk.sh
# çıktı: bozuk.sh: line 3: syntax error: unexpected end of file
# $? = 2Mesaj, dosyanın beklenmedik biçimde bittiğini ve hatanın üçüncü satırda (dosya sonunda) fark edildiğini söyler. -n yalnızca sözdizimini denetler; tanımsız değişkenleri veya yanlış komut adlarını yakalamaz. Çalışma sırasında satır satır izleme için bash -x dosya.sh kullanılır; bu konu hata ayıklama yazısında anlatılıyor.
bash -c ile tek satırlık komut
Bash'i bir dosya yerine bir metin dizgesiyle de çalıştırabilirsiniz. -c seçeneği, ilk seçenek olmayan argümanı komut dizgesi olarak okur. Kılavuza göre dizgeden sonra argüman varsa ilki $0 değerine, geri kalanlar konumsal parametrelere atanır. $0 değeri hata mesajlarında kabuğun adı olarak görünür.
bash -c 'echo "ad=$0 ilk=$1 sayı=$#"' benim-betik elma armut
# çıktı: ad=benim-betik ilk=elma sayı=2Bu biçim kısa denemeler için pratiktir; ancak tırnaklama hızla karmaşıklaşır. Birden fazla satırlık veya tırnak içeren işler için bir dosya yazmak hem okunabilirlik hem hata ayıklama açısından daha iyidir.
Düzenli bir betik iskeleti
Küçük betikler büyüdükçe yapı kazandırmak işinizi kolaylaştırır. Aşağıdaki iskelet, Bash'i açıkça isteyen bir shebang satırı, hata durumlarında erken duran seçenekler, bir kullanım mesajı ve tüm işi yapan bir main fonksiyonundan oluşur. set -euo pipefail satırının neyi değiştirdiğini ve hangi durumlarda beklediğiniz gibi çalışmadığını güvenli betik yazısında ayrıntılı ele aldık.
#!/usr/bin/env bash
set -euo pipefail
kullanim() {
echo "Kullanım: $0 <dosya>" >&2
exit 2
}
main() {
[[ $# -eq 1 ]] || kullanim
local dosya=$1
echo "İşlenecek dosya: $dosya"
}
main "$@"$ bash isle.sh
Kullanım: isle.sh <dosya>
$ echo $?
2
$ bash isle.sh rapor.txt
İşlenecek dosya: rapor.txtmain "$@" satırı, betiğe gelen tüm argümanları olduğu gibi fonksiyona aktarır; çift tırnak, boşluk içeren argümanların bölünmesini engeller. Bu ayrıntı argümanlar yazısında anlatılıyor. Kullanım mesajını >&2 ile standart hataya yazmak, betiğin normal çıktısının bir dosyaya yönlendirildiği durumlarda mesajın yine ekranda görünmesini sağlar; yönlendirmeler için stdout ve stderr yazısına bakın.
Sık yapılan hatalar

- Windows satır sonları: Dosya Windows'ta düzenlendiyse satırlar CRLF ile bitebilir. Shebang satırının sonundaki görünmez karakter yorumlayıcı adının parçası sayılır ve "bad interpreter" hatası alırsınız. Çözüm ve diğer hata mesajları için sık hata mesajları yazısına bakın.
- Betiği sh ile çalıştırmak:
sh dosya.shkomutu shebang satırını yok sayar ve dosyayıshile çalıştırır. Bash özellikleri kullanıyorsanızbash dosya.shyazın. - ./ önekini unutmak: Bulunduğunuz dizin
PATHiçinde değilsedosya.shyazmak "command not found" hatası verir. - Betikten kabuğu değiştirmeyi beklemek: Betikteki
cdveyaexportkomutları sizin terminalinizi etkilemez; bunun için dosyayısourceile çalıştırmanız gerekir. - Çıkış durumunu yok saymak: Betik ortadaki bir hatadan sonra çalışmaya devam edebilir ve son komut başarılıysa 0 ile biter. Kritik adımlardan sonra durumu denetleyin.
Betiğinizi belirli saatlerde otomatik çalıştırmak istiyorsanız cron ile çalıştırmak için cron job rehberine, bir servis olarak sürekli çalıştırmak istiyorsanız systemd servis dosyası rehberine bakabilirsiniz.
Sık Sorulan Sorular
Shebang satırı yazmak zorunlu mu?
bash dosya.sh biçiminde çalıştıracaksanız zorunlu değildir; yorumlayıcıyı zaten siz seçersiniz. Dosyayı ./dosya.sh ile doğrudan çalıştıracaksanız shebang satırı hangi yorumlayıcının kullanılacağını belirler. Satır yoksa kılavuza göre dosya, onu çalıştırmaya çalışan kabuk tarafından yorumlanır; bu da betiğin hangi kabukta çalışacağını belirsiz bırakır.
#!/bin/bash mı, #!/usr/bin/env bash mı?
Kılavuz ikisini de anlatır. #!/bin/bash Bash'in /bin içinde olduğunu varsayar; #!/usr/bin/env bash ise PATH içindeki ilk bash programını kullanır ve Bash'in başka bir dizine kurulduğu sistemlerde de çalışır.
Betik dosyasının uzantısı .sh olmak zorunda mı?
Hayır. Bash için uzantının bir anlamı yoktur; dosyanın içeriği ve nasıl çağrıldığı önemlidir. Uzantı, dosyanın bir kabuk betiği olduğunu okuyanlara göstermek için kullanılan bir adlandırma alışkanlığıdır.
Betiğim neden terminalimdeki dizini değiştirmiyor?
Çünkü betik ayrı bir kabuk sürecinde çalışır ve o sürecin çalışma dizini betik bittiğinde ortadan kalkar. Dizini mevcut kabukta değiştirmek için komutları source dosya.sh ile çalıştırmanız veya bir fonksiyon olarak tanımlamanı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 · Script argümanları · source komutu · Exit kodları · chmod rehberi