set -euo pipefail, Bash betiğinin başına yazılan ve üç ayrı kabuk seçeneğini birlikte açan bir satırdır. -e (errexit) başarısız olan bir komutta betiği durdurur, -u (nounset) tanımlanmamış bir değişkenin genişletilmesini hata sayar, -o pipefail ise bir borunun (pipeline) çıkış durumunu yalnızca son komuta değil, hata veren en sağdaki komuta bağlar. Üçü birlikte, sessizce yanlış çalışmaya devam eden betik yerine sorunu ilk anda görünür kılan bir betik elde etmenizi sağlar.
Ancak bu satır sihirli bir güvenlik kalkanı değildir. GNU Bash kılavuzu, -e seçeneğinin bilerek devre dışı kaldığı bir dizi durumu tek tek sayar ve bu istisnalar bilinmediğinde betik ya beklenmedik yerde durur ya da durması gereken yerde devam eder. Bu yazıda her seçeneğin kılavuzdaki tanımını, istisnalarını, en sık düşülen tuzakları ve önerilen bir başlangıç şablonunu, GNU Bash 5.2.37 ile çalıştırılmış örneklerle anlatıyoruz. Bash'e yeni başlıyorsanız konuların sırası için Bash öğrenme rehberi sayfasına bakabilirsiniz.
set ile seçenek açmak ve kapatmak
set yerleşik komutu iki iş yapar: kabuk seçeneklerini açıp kapatır ve konumsal parametreleri yeniden ayarlar. Bir seçeneği tek harfiyle (set -e) ya da uzun adıyla (set -o errexit) açabilirsiniz; ikisi aynı şeydir. Kapatmak için tire yerine artı işareti kullanılır: set +e veya set +o errexit. pipefail seçeneğinin tek harfli bir kısaltması yoktur, bu yüzden her zaman -o pipefail diye yazılır.
O anda açık olan tek harfli seçenekler $- özel parametresinde durur. Uzun adlı bir seçeneğin durumunu öğrenmek için [[ -o seçenek ]] koşulunu, tüm listeyi görmek için argümansız set -o komutunu kullanabilirsiniz. Kılavuza göre set ile verilebilen tek harfli seçeneklerin hepsi Bash çağrılırken de verilebilir; yani bash -eu betik.sh yazmak, betiğin ilk satırında set -eu çalıştırmakla aynı etkiyi yaratır.
set -euo pipefail
echo "$-"
# çıktı: ehuB
[[ -o pipefail ]] && echo "pipefail açık"
# çıktı: pipefail açık
set +e
echo "$-"
# çıktı: huBÇıktıdaki h ve B harfleri sizin açtığınız seçenekler değildir: h komut yollarını hatırlayan hashall, B ise süslü parantez genişletmesini açan braceexpand seçeneğidir ve ikisi de varsayılan olarak açıktır. set +e sonrasında e harfinin listeden düştüğünü görüyorsunuz; u ise hâlâ açık.
set -e (errexit): ilk hatada durmak
Bash varsayılan olarak bir komut başarısız olduğunda hiçbir şey yapmaz; hatayı ekrana yazar ve bir sonraki satıra geçer. Bu, etkileşimli kullanımda mantıklıdır ama bir betikte zincirleme hasara yol açabilir. Aşağıdaki betikte cd başarısız olduğu hâlde sonraki satır çalışır; bu satır bir silme ya da kopyalama komutu olsaydı yanlış klasörde çalışmış olurdu.
#!/usr/bin/env bash
cd ./olmayan-klasor
echo "cd başarısız oldu ama betik devam etti"
# çıktı: ornek.sh: line 2: cd: ./olmayan-klasor: No such file or directory
# çıktı: cd başarısız oldu ama betik devam ettiAynı betiğe set -e eklendiğinde kabuk, sıfırdan farklı bir durumla biten ilk komutta çıkar ve kendi çıkış kodu olarak o komutun durumunu kullanır. Kılavuzdaki tanım geniştir: tek bir basit komut, bir boru, bir komut listesi veya bir bileşik komut sıfır dışı bir değer döndürdüğünde kabuk hemen sonlanır. ERR için bir trap tanımlıysa çıkmadan önce o çalıştırılır.
#!/usr/bin/env bash
set -e
cd ./olmayan-klasor
echo "bu satır çalışmaz"
# çıktı: ornek.sh: line 3: cd: ./olmayan-klasor: No such file or directory
# $? = 1Çıkış kodlarının anlamı ve $? ile nasıl okunduğu Bash exit kodları yazısında ayrıntılı olarak anlatılıyor. Burada bilmeniz gereken tek şey, Bash için 0 değerinin başarı, diğer her değerin başarısızlık sayılmasıdır.
set -e hangi durumlarda devreye girmez

Kılavuz, bir komut başarısız olsa bile kabuğun çıkmadığı durumları açıkça listeler. Bu liste ezberlenmeye değer, çünkü errexit ile ilgili şaşırtıcı davranışların neredeyse tamamı buradan çıkar:
whileveyauntilsözcüğünden hemen sonra gelen koşul listesindeki komutlar.ifveelifsözcüğünden sonra gelen test bölümündeki komutlar.&&veya||ile kurulan bir listedeki komutlar; yalnızca son&&ya da||işaretinden sonra gelen komut bunun dışındadır.- Bir borudaki son komut dışındaki komutlar (bu kural
pipefailseçeneğinin durumuna bağlıdır). - Çıkış durumu
!ile tersine çevrilen komutlar.
Kılavuz iki önemli ek kural daha koyar. Birincisi: -e göz ardı edilirken içinde bir komut başarısız olduğu için sıfır dışı değer döndüren bir bileşik komut (alt kabuk hariç) kabuğu sonlandırmaz. İkincisi ve daha şaşırtıcı olanı: bir bileşik komut ya da fonksiyon, -e seçeneğinin yok sayıldığı bir bağlamda çalışıyorsa, gövdesindeki hiçbir komut -e ayarından etkilenmez. Yani bir fonksiyonu if koşulunda çağırdığınızda, fonksiyonun içindeki tüm komutlar için errexit fiilen kapanır.
set -e
kontrol() {
false
echo "false'tan sonra devam edildi"
}
if kontrol; then
echo "if içinde: kontrol başarılı sayıldı"
fi
kontrol
echo "buraya gelinmez"
# çıktı: false'tan sonra devam edildi
# çıktı: if içinde: kontrol başarılı sayıldı
# $? = 1Örnekte aynı fonksiyon iki kez çağrılıyor. if içinde çağrıldığında false fonksiyonu durdurmaz; fonksiyonun son komutu olan echo başarılı olduğu için fonksiyon 0 döndürür ve if dalı çalışır. Fonksiyon doğrudan çağrıldığında ise false satırında betik sonlanır. Aynı kodun bağlama göre farklı davranması, errexit'e tek başına güvenmemeniz gerektiğinin en açık göstergesidir. Önemli bir kontrol yapan fonksiyonlarda hatayı açıkça return ile bildirmek daha güvenlidir; fonksiyon yazımı için Bash fonksiyon tanımlama yazısına bakabilirsiniz.
Sık görülen errexit tuzakları

Fonksiyonun son satırındaki && listesi
[[ koşul ]] && komut biçimi kısa bir "eğer" gibi kullanılır. Üst düzeyde, koşul yanlış olduğunda bu satır betiği durdurmaz, çünkü başarısız olan komut && listesinin son elemanı değildir. Fakat aynı satır bir fonksiyonun son komutu olduğunda listenin durumu, yani 1, fonksiyonun dönüş değeri olur. Fonksiyonu çağıran satır artık errexit istisnası değildir ve betik orada sonlanır.
set -e
ayar_yukle() {
[[ -f ./ayar.conf ]] && echo "ayar yüklendi"
}
[[ -f ./ayar.conf ]] && echo "ayar bulundu"
echo "üst düzeydeki && betiği durdurmadı"
ayar_yukle
echo "buraya gelinmez"
# çıktı: üst düzeydeki && betiği durdurmadı
# $? = 1Çözüm, kısa yazım yerine if [[ -f ./ayar.conf ]]; then ...; fi kullanmaktır. Kılavuza göre hiçbir koşul doğru çıkmadığında if komutunun durumu 0'dır, bu yüzden dosya yoksa fonksiyon başarıyla döner. Bu ayrım, && ve || listelerinin genel davranışıyla birlikte Bash ve/veya operatörleri yazısında da işleniyor.
Sıfırdan başlayan sayaçta ((sayac++))
(( ifade )) aritmetik komutu, ifadenin değeri sıfır olduğunda 1, sıfır dışı olduğunda 0 durumuyla biter. Son ek artırma sayac++ önce eski değeri, yani 0'ı döndürdüğü için ilk artırmada komut başarısız sayılır ve set -e betiği durdurur.
set -e
sayac=0
(( sayac++ ))
echo "buraya gelinmez"
# $? = 1set -e
sayac=0
(( ++sayac ))
sayac=$(( sayac + 1 ))
echo "sayac = $sayac"
# çıktı: sayac = 2Ön ek artırma ++sayac yeni değeri (1) döndürdüğü için sorun çıkarmaz; sayac=$(( sayac + 1 )) ise bir atama olduğundan her zaman 0 durumuyla biter. Aritmetik işlemlerin ayrıntısı Bash aritmetik işlemler yazısında.
local ve export komut ikamesinin hatasını gizler
local degisken=$(komut) satırında çıkış durumunu belirleyen komut değil, local yerleşik komutunun kendisidir. local geçerli bir adla çağrıldığında 0 döndürdüğü için içerideki hata kaybolur. Bildirimi ve atamayı iki satıra ayırdığınızda atamanın durumu komut ikamesinin durumuna eşit olur ve errexit yeniden çalışır.
set -e
yukle() {
local icerik=$(false)
echo "local, false'un hatasını gizledi"
local veri
veri=$(false)
echo "buraya gelinmez"
}
yukle
# çıktı: local, false'un hatasını gizledi
# $? = 1Komut ikamesinin içinde errexit kapalıdır
Kılavuza göre POSIX modunda değilken Bash, komut ikamesi için açtığı alt kabukta -e seçeneğini temizler. Yani $( ... ) içindeki başarısız bir komut, ikamenin geri kalanını durdurmaz. Bu davranışı değiştirmek için shopt -s inherit_errexit kullanılır; bu seçenek açıkken alt kabuk errexit değerini üst kabuktan devralır. POSIX modu açıldığında bu seçenek kendiliğinden açılır.
set -e
x=$(false; echo "alt kabuk devam etti")
echo "x = $x"
shopt -s inherit_errexit
y=$(false; echo "bu satır yazılmaz")
echo "buraya gelinmez"
# çıktı: x = alt kabuk devam etti
# $? = 1İkinci atamada alt kabuk false satırında 1 ile çıkar; atamanın durumu da bu olur ve üst kabuk errexit nedeniyle sonlanır. Komut ikamesinin diğer özellikleri için Bash komut ikamesi, shopt seçeneklerinin tamamı için Bash shopt seçenekleri yazısına bakabilirsiniz.
set -u (nounset): tanımsız değişkeni yakalamak
Bash, hiç atanmamış bir değişkeni sessizce boş metin olarak genişletir. Değişken adında yapılan tek harflik bir yazım hatası bu yüzden fark edilmez ve "$hedef/" gibi bir yol beklenmedik biçimde kök dizine dönüşebilir. set -u açıkken tanımsız bir parametrenin genişletilmesi hata sayılır: kabuk standart hataya bir mesaj yazar ve etkileşimli olmayan kabuk (yani betik) sonlanır.
set -u
hedef="./yedek"
echo "Hedef: $hedf"
echo "buraya gelinmez"
# çıktı: ornek.sh: line 3: hedf: unbound variable
# $? = 1Kılavuz bu kuralın bazı istisnalarını belirtir: @ ve * özel parametreleri ile @ ya da * alt simgesiyle kullanılan diziler hata vermez. Bu sayede argümansız çalışan bir betikte "$@" veya boş bir dizide "${dizi[@]}" kullanmak güvenlidir. İsteğe bağlı bir değer beklediğiniz yerde ise varsayılan değer genişletmesiyle değişkenin tanımsız olabileceğini kabuğa açıkça bildirirsiniz.
set -u
bos=()
echo "argüman sayısı: $#, hepsi: [$*]"
echo "dizi: [${bos[@]}]"
echo "ilk argüman: ${1:-verilmedi}"
echo "ortam: ${YEDEK_DIZIN-tanımsız}"
# çıktı: argüman sayısı: 0, hepsi: []
# çıktı: dizi: []
# çıktı: ilk argüman: verilmedi
# çıktı: ortam: tanımsız${1:-verilmedi} ile ${YEDEK_DIZIN-tanımsız} arasındaki iki nokta farkı (boş değerin de varsayılana düşüp düşmediği) ve ${v:?mesaj} ile zorunlu değişken tanımlama Bash parametre genişletme ve varsayılan değer yazısında anlatılıyor. set -u kullanan bir betikte bu genişletmeler, "bu değişken bilerek boş olabilir" demenin standart yoludur.
set -o pipefail: borudaki hatayı kaybetmemek
Kılavuza göre bir borunun çıkış durumu, normalde borudaki son komutun durumudur. komut1 | komut2 yazdığınızda komut1 başarısız olsa bile komut2 başarılıysa tüm boru başarılı sayılır; bu yüzden set -e tek başına borunun başındaki hatayı yakalayamaz. pipefail açıkken borunun durumu, sıfır dışı değerle biten en sağdaki komutun durumudur; tüm komutlar başarılıysa sonuç 0'dır. Seçenek varsayılan olarak kapalıdır.
( exit 3 ) | true
echo "pipefail kapalı: $?"
set -o pipefail
( exit 3 ) | true
echo "pipefail açık: $?"
( exit 2 ) | ( exit 5 ) | true
echo "en sağdaki hatalı komut: $?"
# çıktı: pipefail kapalı: 0
# çıktı: pipefail açık: 3
# çıktı: en sağdaki hatalı komut: 5Son satırda iki komut başarısız olduğu hâlde sonuç 5'tir, çünkü kural "ilk hata" değil "en sağdaki hata"dır. Borudaki her komutun durumunu ayrı ayrı görmek isterseniz PIPESTATUS dizisine bakmanız gerekir; bu dizi ve lastpipe seçeneği Bash pipe, PIPESTATUS ve pipefail yazısında ele alınıyor.
Üç seçeneğin karşılaştırması

| Seçenek | Uzun adı | Ne yapar | Dikkat edilecek nokta |
|---|---|---|---|
| -e | errexit | Başarısız komutta kabuğu sonlandırır | if, while, until, && ve || listeleri ve ! ile tersine çevrilen komutlarda devre dışı |
| -u | nounset | Tanımsız değişken genişletmesini hata sayar | $@, $* ve [@] ile kullanılan diziler istisna; isteğe bağlı değerler için ${v:-} kullanın |
| -o pipefail | pipefail | Borunun durumunu hata veren en sağdaki komuta bağlar | Kısaltması yok; erken kapanan okuyucularda beklenmedik hata verebilir |
| -E | errtrace | ERR trap'ini fonksiyonlara ve alt kabuklara aktarır | Açılmazsa fonksiyon içindeki hatalarda ERR trap'i çalışmaz |
Önerilen başlangıç şablonu
Aşağıdaki betik, üç seçeneği bir ERR trap'i ile birleştirir ve başarısız olması normal olan komutların nasıl işaretleneceğini gösterir. Kılavuz, ERR trap'inin errexit ile tamamen aynı istisna koşullarına uyduğunu belirtir; yani trap tam olarak kabuğun çıkacağı durumlarda tetiklenir ve çıkmadan önce hangi satırda hata olduğunu yazabilir.
#!/usr/bin/env bash
set -euo pipefail
trap 'echo "HATA: satır $LINENO, çıkış kodu $?" >&2' ERR
hedef="${1:-./cikti}"
mkdir -p -- "$hedef"
echo "rapor" > "$hedef/rapor.txt"
# Başarısız olabileceğini bildiğiniz komutlar:
rm -f -- "$hedef/eski.txt"
if ! cat -- "$hedef/istege-bagli.txt" 2>/dev/null; then
echo "isteğe bağlı dosya yok, devam"
fi
grep -q "bulunmayan" "$hedef/rapor.txt" || true
echo "bitti"
cat -- "$hedef/kayip.txt"
echo "buraya gelinmez"
# çıktı: isteğe bağlı dosya yok, devam
# çıktı: bitti
# çıktı: cat: ./cikti/kayip.txt: No such file or directory
# çıktı: HATA: satır 17, çıkış kodu 1
# $? = 1Şablondaki üç kalıp, errexit ile yaşamanın temel araçlarıdır. if ! komut başarısızlığı bir koşul olarak ele alır ve kendi dalınızda işlem yapmanıza izin verir. komut || true "bu komutun durumu önemsiz" demenin en kısa yoludur. rm -f gibi bazı araçlar ise bu durumu kendi seçenekleriyle zaten ele alır. Trap kullanımı, geçici dosya temizliği ve sinyaller için Bash trap ile sinyal ve temizlik yazısını okuyabilirsiniz.
Sık yapılan hatalar
- set -e'yi hata yönetiminin tamamı sanmak. Kılavuzdaki istisnalar nedeniyle kritik adımlarda durumu yine
ifveya||ile açıkça kontrol etmeniz gerekir. - Kontrol fonksiyonlarını if içinde çağırıp errexit beklemek. Fonksiyon if, while veya && bağlamında çağrıldığında gövdesindeki tüm komutlar için
-eetkisizdir. - local x=$(komut) yazmak. Bildirimi ve atamayı ayırmazsanız komutun hatası kaybolur.
- Sayaçta (( i++ )) kullanmak. Değer 0 iken komut başarısız sayılır;
(( ++i ))veyai=$(( i + 1 ))yazın. - set -u ile birlikte $1 kullanmak. Argüman zorunlu değilse
${1:-}yazın; zorunluysa açık bir kullanım mesajı verin. - pipefail'i unutup yalnızca set -e kullanmak. Boru başındaki hata, son komut başarılı olduğu sürece görünmez kalır.
Sık Sorulan Sorular
set -euo pipefail betiğin neresine yazılmalı?
Genellikle shebang satırının hemen altına yazılır, böylece betiğin tamamı bu seçeneklerle çalışır. Seçenekleri betiğin bir bölümünde kapatıp sonra yeniden açmak da mümkündür (set +e ... set -e), ama bu bölümlerde hata kontrolünü elle yapmanız gerekir. Betik dosyasının yapısı için Bash script yazma ve çalıştırma yazısına bakın.
Seçenekler alt kabuklara geçer mi?
Kılavuza göre -e seçeneği kabuk ortamına ve her alt kabuk ortamına ayrı ayrı uygulanır; bir alt kabuk, içindeki komut başarısız olduğunda kendi işini tamamlamadan çıkabilir. Komut ikamesi bunun istisnasıdır: POSIX modu dışında errexit orada temizlenir, inherit_errexit bunu değiştirir. Alt kabukların genel davranışı Bash subshell ve çalışma ortamı yazısında.
Etkileşimli terminalde set -e açmak sorun çıkarır mı?
Evet. Kılavuzdaki tanım etkileşimli kabuk için de geçerlidir; terminalde set -e açtıktan sonra başarısız olan ilk komut kabuğu kapatır ve oturumunuz sona erer. Bu seçenekleri betiklerde kullanın, .bashrc gibi başlangıç dosyalarına eklemeyin.
set -u ile boş dizi kullanmak güvenli mi?
"${dizi[@]}" ve "${dizi[*]}" biçimleri kılavuzda nounset istisnası olarak geçer ve Bash 5.2.37 ile boş dizide hata vermediğini yukarıdaki örnekte gördünüz. Ancak ${dizi[0]} gibi belirli bir elemana erişim, eleman yoksa hata verir. Diziler için Bash dizi kullanımı yazısına bakabilirsiniz.
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 trap · Bash exit kodları · PIPESTATUS ve pipefail · Bash hata ayıklama · Varsayılan değer genişletmesi