Bash, POSIX kabuk standardının bir uygulamasıdır ama varsayılan davranışı standarttan bazı noktalarda ayrılır ve üstüne pek çok ek özellik koyar. sh ise tek bir program değil, POSIX kabuğu için kullanılan genel addır: bir sistemde /bin/sh Bash'in kendisi olabilir, başka bir sistemde Bash'e ait özellikleri hiç tanımayan daha küçük bir kabuk. Bash'i sh adıyla çağırdığınızda, bash --posix ile başlattığınızda veya set -o posix çalıştırdığınızda Bash POSIX moduna geçer ve standarttan ayrıldığı yerlerde standarda uyar. Bu mod Bash'in ek özelliklerini kapatmaz; yani bir betiğin POSIX modunda çalışması, onun başka bir sh uygulamasında da çalışacağının kanıtı değildir.
Bu yazıda POSIX'in ne olduğunu, Bash'in POSIX modunu nasıl açtığını ve bu modda nelerin değiştiğini, Bourne kabuğuna göre Bash'in getirdiği farkları ve taşınabilir betik yazarken kaçınmanız gereken Bash'e özgü yapıları (bashism) anlatıyoruz. Bash'in genel tanıtımı Bash öğrenme rehberi sayfasında, Linux'un kendisi Linux nedir yazısında yer alıyor.
POSIX nedir?

Kılavuz POSIX'i, Unix temelli bir standartlar ailesi olarak tanımlar. Kabuk ve yardımcı programlar kısmı ilk olarak IEEE 1003.2 (POSIX.2) çalışma grubunda geliştirildi ve ilk baskısı 1992'de yayımlandı. Bugün bu kısım, IEEE Std 1003.1-2024 belgeler setinin bir cildidir ve Austin Group tarafından sürdürülür. Standart; temel akış denetimi ve program çalıştırma yapılarını, giriş/çıkış yönlendirmelerini, boru hatlarını, argüman işlemeyi, değişken genişletmeyi ve tırnaklamayı tanımlar.
Kılavuz iki ayrıntıya da dikkat çeker. Birincisi, eval ve export gibi bazı yerleşik komutlar standartta özel (special builtin) olarak sınıflandırılır. İkincisi, standart etkileşimli kullanımın bazı yönlerini de kapsar; ancak satır düzenleme komutlarından yalnızca vi tarzı olanlar standartlaşmıştır, emacs tarzı komutlar itirazlar nedeniyle dışarıda kalmıştır.
POSIX modu nasıl açılır?
Kılavuz dört yol sayar: Bash'i --posix seçeneğiyle başlatmak, çalışırken set -o posix yazmak, Bash'i sh adıyla çağırmak ve ortamda POSIXLY_CORRECT değişkeni varken Bash'i başlatmak. sh adıyla çağrıldığında Bash önce başlangıç dosyalarını tarihsel sh kurallarına göre okur, POSIX moduna ondan sonra geçer. Mod açıldığında Bash, POSIXLY_CORRECT değişkenini (henüz yoksa) kendisi tanımlar. Modun açık olup olmadığını shopt -qo posix ile sorgulayabilirsiniz:
#!/usr/bin/env bash
shopt -qo posix && echo "posix açık" || echo "posix kapalı"
# çıktı: posix kapalı
echo "POSIXLY_CORRECT: [${POSIXLY_CORRECT-tanımsız}]"
# çıktı: POSIXLY_CORRECT: [tanımsız]
set -o posix
shopt -qo posix && echo "posix açık"
# çıktı: posix açık
echo "POSIXLY_CORRECT: [${POSIXLY_CORRECT-tanımsız}]"
# çıktı: POSIXLY_CORRECT: [y]
shopt inherit_errexit shift_verbose
# çıktı:
# inherit_errexit on
# shift_verbose onÇıktının son kısmı kılavuzdaki iki maddeyi doğruluyor: POSIX modu açıldığında inherit_errexit ve shift_verbose seçenekleri de açılır. Dört çağırma yolunu aynı küçük betikle deneyelim. sh adını taklit etmek için exec -a sh kullandık; bu, sıfırıncı argümanı sh yaparak Bash'i başlatır ve sistemdeki /bin/sh hangi programa bağlı olursa olsun aynı sonucu verir:
#!/usr/bin/env bash
cat > ./kip.sh <<'EOF'
shopt -qo posix && echo "$1: POSIX modu açık" || echo "$1: POSIX modu kapalı"
EOF
bash ./kip.sh "bash"
# çıktı: bash: POSIX modu kapalı
bash --posix ./kip.sh "bash --posix"
# çıktı: bash --posix: POSIX modu açık
POSIXLY_CORRECT=1 bash ./kip.sh "POSIXLY_CORRECT=1"
# çıktı: POSIXLY_CORRECT=1: POSIX modu açık
( exec -a sh bash ./kip.sh "sh adıyla" )
# çıktı: sh adıyla: POSIX modu açıkPOSIX modu Bash özelliklerini kapatmaz
Kılavuz, POSIX modunun amacını Bash'i standardın "katı bir üst kümesi" gibi davranmaya yaklaştırmak olarak açıklar. Üst küme ifadesi önemlidir: mod, Bash'in standarttan farklı davrandığı noktaları düzeltir, ama standartta hiç olmayan özellikleri kaldırmaz. Diziler, [[ ]], süslü parantez genişletmesi ve benzerleri POSIX modunda da çalışır:
#!/usr/bin/env bash
set -o posix
dizi=(bir iki üç)
[[ ${dizi[1]} == iki ]] && echo "[[ ]] ve diziler POSIX modunda da çalışıyor"
# çıktı: [[ ]] ve diziler POSIX modunda da çalışıyor
echo {1..3}
# çıktı: 1 2 3Bu nedenle bir betiğin taşınabilir olup olmadığını bash --posix ile test etmek yeterli değildir. Betiği hedef sistemdeki gerçek sh ile çalıştırmak ya da Bash'e özgü yapıları bilerek kullanmamak gerekir.
POSIX modunda neler değişir?

Kılavuz, POSIX modunda değişen davranışları 75 maddelik bir listede sayar. Hepsini tekrar etmek yerine betik yazarken en sık karşınıza çıkacak olanları gruplayalım:
- Başlangıç dosyaları: Etkileşimli kabuk
~/.bashrcyerineENVdeğişkeninin gösterdiği dosyayı okur. Ayrıntı için bashrc yazısına bakın. - Alias'lar: Etkileşimli olmayan kabuklarda da her zaman genişletilir.
- Özel yerleşik komutlar: Komut aramasında fonksiyonlardan önce bulunurlar, aynı ada sahip fonksiyon tanımlanamaz ve hata döndürdüklerinde etkileşimli olmayan kabuk çıkar.
- Öndeki atamalar: Özel bir yerleşik komutun önüne yazılan değişken atamaları, komut bittikten sonra da kabukta kalır.
- Hatalarda çıkış: Aritmetik genişletmedeki sözdizimi hatası, parametre genişletme hatası,
.ile verilen dosyanın bulunamaması veevalmetnindeki sözdizimi hatası etkileşimli olmayan kabuğu sonlandırır. - Komut ikamesi ve set -e:
inherit_errexitaçıldığı için komut ikamesi alt kabukları-eseçeneğini devralır. - source araması:
.vesource, dosyaPATHiçinde bulunamazsa geçerli dizine bakmaz. - Küçük biçim farkları:
kill -l,trap,exportvealiasçıktıları standardın istediği biçimde yazılır; varsayılan geçmiş dosyası~/.sh_historyolur.
Özel yerleşik komut kuralının etkisini görmek için export adında bir fonksiyon tanımlamayı deneyelim. Varsayılan modda bu mümkündür ve fonksiyon yerleşik komutu gölgeler; POSIX modunda ise Bash tanımı reddeder ve betik sonlanır:
#!/usr/bin/env bash
export() { echo "kendi export fonksiyonum"; }
export
# çıktı: kendi export fonksiyonum#!/usr/bin/env bash
set -o posix
export() { echo "kendi export fonksiyonum"; }
echo "bu satıra gelinmez"
# çıktı: ornek.sh: line 3: `export': is a special builtinKılavuzun listelediği özel yerleşik komutlar şunlardır: break, :, ., source, continue, eval, exec, exit, export, readonly, return, set, shift, times, trap ve unset. POSIX modunda değilken bu komutlar diğer yerleşik komutlardan farklı davranmaz.
Öndeki atama kuralı da kolayca gözlemlenebilir. Normalde degisken=deger komut yazımı değişkeni yalnızca o komutun ortamına koyar ve kılavuza göre bu, yerleşik komutlar ve fonksiyonlar için de geçerlidir. POSIX modunda ise komut özel bir yerleşikse, örneğin :, atama kabukta kalır:
#!/usr/bin/env bash
renk=mavi :
echo "varsayılan kip: [${renk-}]"
# çıktı: varsayılan kip: []
set -o posix
renk=mavi :
echo "POSIX kipi: [${renk-}]"
# çıktı: POSIX kipi: [mavi]set -e ile komut ikamesi arasındaki fark ise hata yönetimini doğrudan etkiler. Varsayılan modda Bash, komut ikamesinin alt kabuğunda -e seçeneğini temizler; bu yüzden $( ) içindeki başarısız bir komut, ikamenin geri kalanını durdurmaz. POSIX modunda seçenek devralınır:
#!/usr/bin/env bash
set -e
deger=$(false; echo "komut ikamesi devam etti")
echo "varsayılan: $deger"
# çıktı: varsayılan: komut ikamesi devam etti
set -o posix
deger=$(false; echo "komut ikamesi devam etti")
echo "POSIX: [$deger]"
# (çıktı yok: false ile alt kabuk, ardından set -e yüzünden betik sonlanır)POSIX modundaki ikinci atamada false komut ikamesinin alt kabuğunu sonlandırdı, atamanın çıkış kodu 1 oldu ve set -e betiği durdurdu. Varsayılan modda aynı davranışı istiyorsanız shopt -s inherit_errexit yazabilirsiniz. set -e kurallarının tamamı için set -euo pipefail yazısına bakın.
Başlangıç dosyası farkı da pratikte sık karşılaşılan bir sürprizdir. Kılavuza göre Bash sh adıyla etkileşimli bir giriş kabuğu olarak başladığında önce /etc/profile, sonra ~/.profile dosyasını okur; etkileşimli kabukta ise yalnızca ENV değişkeninin gösterdiği dosyaya bakar. ~/.bashrc ve ~/.bash_profile hiç okunmaz, --rcfile seçeneği de etkisizdir. Etkileşimli olmayan bir sh kabuğu hiçbir başlangıç dosyası okumaz. Bu yüzden alias'larınızın veya ortam ayarlarınızın sh altında görünmemesi bir hata değil, belgelenmiş bir davranıştır.
POSIX modunda bile uygulanmayanlar
Kılavuz, Bash'in POSIX modunda dahi varsayılan olarak uygulamadığı birkaç standart davranışı da açıkça belirtir. Örneğin standart, kelime bölmesinin bayt temelli olmasını ister; Bash ise IFS içindeki geçerli çok baytlı bir karakteri tek bir ayırıcı olarak görür. echo komutunun tam uyumlu olması için de ayrıca xpg_echo seçeneği gerekir. Bash, derlenirken --enable-strict-posix-default seçeneği verilerek varsayılan olarak POSIX uyumlu yapılabilir.
#!/usr/bin/env bash
echo 'a\tb'
# çıktı: a\tb
shopt -s xpg_echo
echo 'a\tb'
# çıktı: a b
printf '%s\t%s\n' a b
# çıktı: a becho komutunun ters eğik çizgi kaçışlarını yorumlayıp yorumlamaması kabuktan kabuğa değiştiği için, taşınabilir betiklerde biçimli çıktı gerektiğinde printf tercih edilir. Ayrıntılar echo ve printf yazısında.
Bash, Bourne kabuğundan neleri farklı yapar?
![Bash, Bourne kabuğundan neleri farklı yapar? [[ ifade ]], [ "$a" == "$b" ], dizi=(a b c), {1..10}](/assets/blog/bash-ve-sh-farki-posix-modu-b5.webp)
Kılavuzun B ekinde, tarihsel Bourne kabuğunun son sürümü olan SVR4.2 sh temel alınarak Bash'in önemli farkları listelenir. Bu listedeki yapıların çoğu bugün "bashism" olarak bilinir: Bash'te çalışırlar, ama yalnızca POSIX'in tanımladığı özellikleri destekleyen bir kabukta çalışmayabilirler. En sık karşılaşılanlar ve taşınabilir karşılıkları şöyle:
| Bash yapısı | Ne işe yarar | Taşınabilir karşılık |
|---|---|---|
| [[ ifade ]] | Kabuk sözdiziminin parçası olan koşul | [ ifade ] veya test |
| [ "$a" == "$b" ] | Metin eşitliği (kılavuz test için = önerir) | [ "$a" = "$b" ] |
| dizi=(a b c) | Tek boyutlu diziler | Konumsal parametreler: set -- a b c |
| {1..10} | Süslü parantez genişletmesi | while döngüsü ve sayaç |
| $'\t' | ANSI-C tırnaklama | printf '\t' |
| local degisken | Fonksiyona yerel değişken (B ekine göre Bourne kabuğunda yok) | Çakışmayan değişken adları |
| <<< metin | Here string | printf '%s\n' metin | komut |
| &> dosya | stdout ve stderr aynı dosyaya | > dosya 2>&1 |
| <( komut ) | Process substitution | Geçici dosya veya boru hattı |
| (( i++ )) | Aritmetik komut | i=$((i + 1)) |
| ${v/a/b}, ${v:0:3} | Desen değiştirme ve alt dize | ${v#desen}, ${v%desen} veya harici araçlar |
Tablodaki ${v#desen} ve ${v%desen} desen silme genişletmeleri POSIX'te de vardır; kılavuz bunları Bash'in "POSIX desen silme" genişletmeleri olarak anar. $(( )) aritmetik genişletmesi ve $( ) komut ikamesi de taşınabilirdir; ters tırnak yerine $( ) kullanmanız kılavuzca da önerilir. Aşağıdaki küçük betik yalnızca taşınabilir yapılar kullanıyor; hem varsayılan modda hem bash --posix ile aynı çıktıyı verdi:
#!/bin/sh
# Bashism yerine POSIX karşılıkları
ad="dünya"
if [ "$ad" = "dünya" ]; then
echo "merhaba $ad"
fi
i=1
while [ "$i" -le 3 ]; do
echo "sayaç: $i"
i=$((i + 1))
done
case "dosya.tar.gz" in
*.tar.gz) echo "arşiv" ;;
esac
selam() {
echo "fonksiyon: $1"
}
selam test
# çıktı:
# merhaba dünya
# sayaç: 1
# sayaç: 2
# sayaç: 3
# arşiv
# fonksiyon: testB eki bunların dışında Bash'in davranış farklarını da sayar. Örneğin Bash, IFS değişkenini yalnızca genişletme sonuçlarını bölmek için kullanır, tüm kelimeleri değil; kılavuz bunun eski bir güvenlik açığını kapattığını belirtir. Bash, kabuğun başlangıç ortamındaki değişkenleri otomatik olarak alt süreçlere aktarır; Bourne kabuğu bunu açıkça export edilmedikçe yapmaz. Ayrıca Bash'te aynı ada sahip bir değişken ve bir fonksiyon birlikte bulunabilir, sh ise bu iki ad alanını ayırmaz.
Ne zaman sh, ne zaman bash?
Betiğinizin çok farklı sistemlerde, Bash'in kurulu olmayabileceği küçük ortamlarda veya yalnızca /bin/sh sunan bir yürütme ortamında çalışması gerekiyorsa POSIX sözdizimiyle yazıp #!/bin/sh kullanın ve betiği o ortamdaki gerçek sh ile test edin. Betik yalnızca Bash'in kesin olarak bulunduğu sunucularda çalışacaksa Bash'in dizileri, [[ ]] ve set -o pipefail gibi özellikleri betiği daha okunaklı ve güvenli yapar; bu durumda ilk satıra açıkça bash yazın. En kötü seçenek, Bash özellikleri kullanıp ilk satıra #!/bin/sh yazmaktır: betik bir sistemde çalışır, diğerinde bozulur. Betiğin hangi Bash sürümünde çalıştığını sınamak için Bash sürümü ve uyumluluk modu yazısına bakın.
Sık yapılan hatalar
- Bash yapıları + #!/bin/sh:
/bin/shbaşka bir kabuksa[[, diziler veya{1..3}hata verir. - bash --posix testini yeterli saymak: POSIX modu Bash eklerini kapatmaz; hedef kabukla test edin.
- sh betik.sh ile Bash betiği çalıştırmak: İlk satırdaki yorumlayıcı atlanır; betik o sistemdeki
shile çalışır. - Özel yerleşik adıyla fonksiyon:
export,setveyaevaladında fonksiyon POSIX modunda tanımlanamaz. - echo ile kaçış dizileri: Davranış kabuğa ve
xpg_echoseçeneğine bağlıdır;printfkullanın.
Sık Sorulan Sorular
Bash ile sh aynı şey mi?
Hayır. sh, POSIX kabuğu için kullanılan genel addır; Bash ise bu standardı uygulayan ve üstüne birçok özellik ekleyen bir kabuktur. Bazı sistemlerde /bin/sh Bash'i çağırır ve Bash bu durumda POSIX moduna geçer, bazılarında ise farklı bir kabuktur.
Betiğimin POSIX modunda çalışıp çalışmadığını nasıl anlarım?
shopt -qo posix komutu POSIX modu açıksa 0 döndürür. Mod açıldığında POSIXLY_CORRECT değişkeni de tanımlanır.
set -o posix sonradan kapatılabilir mi?
Evet, set +o posix modu kapatır. Denediğimiz 5.2.37'de mod kapatıldıktan sonra shift_verbose kapandı, ama inherit_errexit açık kaldı. Bu yüzden modu geçici açıp kapattıysanız ilgili shopt seçeneklerinin durumunu ayrıca kontrol edin.
POSIX standardını nerede okuyabilirim?
Kılavuz, kabuk ve yardımcı programlar cildinin The Open Group sitesinde ücretsiz olarak yayımlandığını belirtir.
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 · Linux nedir · Bash script yazma · Bash sürümü ve uyumluluk · test ve [[ ]] farkı · shopt seçenekleri