İçeriğe geç
academia.sh

Ders 20 / 24

Hata Yönetimi

Katı kip seçenekleri, erken çıkışın kapsamı ve istisnaları, tanımsız değişken denetimi, boru hattı kodu ve hata yakalama kancası.

İçindekiler

Betik doğru çıktı üretiyor, ama yalnızca her şey yolunda gittiğinde. Bir komut başarısız olduğunda rapor yarım kalıyor ve betiğin çıkış kodu bunu bildirmiyor. Tanımsız bir değişken sessizce boş dizgiye genişliyor. Boru hattının ortasındaki hata görünmüyor.

Kabuğun öntanımlı davranışı hataya karşı hoşgörülüdür: bir komut başarısız olsa da sonraki komut çalışır. Bu, etkileşimli kullanımda doğru bir seçimdir — yanlış yazılmış bir komut oturumu kapatmamalıdır. Betikte ise ters etki yapar.

Katı kip (strict mode), kabuğun bu hoşgörüsünü kısan seçenekler kümesidir. Bu ders seçenekleri tek tek ele alır; her birinin ne yakaladığını ve — daha önemlisi — ne yakalamadığını gösterir.

Erken Çıkış

set -e seçeneği, çıkış kodu sıfırdan farklı olan bir komuttan sonra kabuğu sonlandırır.

#!/usr/bin/env bash
set -e
echo "1: baslangic"
false
echo "2: BU SATIR YAZILMAZ"
1: baslangic
echo $?
1

Betik ikinci satıra ulaşmadan durdu ve başarısızlık kodunu dışarı taşıdı. Kazanç yalnızca kolaylık değildir: bir betiğin yarım çıktı üretip başarı bildirmesi, onu çağıran her sistemi yanıltır.

Erken Çıkışın İstisnaları

set -e bir komutun başarısızlığını her yerde ölümcül saymaz. İstisnalar mantıklıdır ama sayıları çoktur ve bilinmezse yanlış güven verir.

#!/usr/bin/env bash
set -e
if false; then echo "a"; fi
echo "1: kosulda basarisizlik betigi durdurmaz"
false || echo "2: || solunda basarisizlik durdurmaz"
false && echo "buraya gelinmez"
echo "3: && solunda basarisizlik da durdurmaz"
false | true
echo "4: boru hattinin son komutu basarili ise durdurmaz"
! false
echo "5: degillenmis komut durdurmaz"
f() { false; echo "f devam etti"; }
if f; then :; fi
echo "6: kosulda cagrilan fonksiyon icinde set -e askiya alinir"
1: kosulda basarisizlik betigi durdurmaz
2: || solunda basarisizlik durdurmaz
3: && solunda basarisizlik da durdurmaz
4: boru hattinin son komutu basarili ise durdurmaz
5: degillenmis komut durdurmaz
f devam etti
6: kosulda cagrilan fonksiyon icinde set -e askiya alinir

Altı istisnanın ilk beşi tasarım gereğidir: koşul olarak kullanılan bir komutun başarısızlığı bilgi taşır, hata değildir. Altıncısı en tehlikelisidir — bir koşulda çağrılan fonksiyonun tüm gövdesi boyunca erken çıkış askıya alınır ve gövdedeki gerçek hatalar görünmez olur.

Bu yüzden set -e tek başına bir güvence değildir. Kritik komutların kodları ayrıca sınanmalı ya da bir hata kancası kurulmalıdır.

Tanımsız Değişken Denetimi

set -u seçeneği, tanımsız bir değişkenin genişletilmesini hata sayar.

#!/usr/bin/env bash
set -u
echo "kutuk: ${KUTUK}"
./u.sh: line 3: KUTUK: unbound variable

Bu seçenek, kabuk betiklerinde en yıkıcı hata sınıfını yakalar. rm -rf "$DIZIN/" satırı, DIZIN tanımsızken rm -rf / hâline gelir — seçenek etkinken betik o satıra hiç ulaşmaz.

Seçenek, öntanımlı değer gösterimlerini kullanmayı zorunlu kılar:

#!/usr/bin/env bash
set -u
echo "kutuk: ${KUTUK:-erisim.log}"
echo "ilk arguman: ${1-yok}"
echo "arguman sayisi: $#"
kutuk: erisim.log
ilk arguman: yok
arguman sayisi: 0

Değişkenler ve Tırnak Kuralları dersinde tanıtılan ${v-yedek} ve ${v:-yedek} gösterimleri burada zorunluluğa dönüşür: verilmemiş bir konumsal parametre okunacaksa öntanımlı değer yazılmalıdır. Bu, betiğin arayüzünü belgeleyen bir yan etki de üretir.

Boru Hattı Kodu

set -o pipefail, boru hattının kodunu sıfırdan farklı kod döndüren en sağdaki komutun koduna eşitler. Boru Hattı dersinde tanıtılmıştı; katı kipin ayrılmaz parçasıdır.

#!/usr/bin/env bash
set -eo pipefail
grep '999' erisim.log | wc -l
echo "BU SATIR YAZILMAZ"
       0
echo $?
1

pipefail olmadan bu betik 0 yazıp başarıyla biterdi: wc sorunsuz çalıştı ve hattın kodu onunkiydi.

pipefail ve set -e ile birlikte, SIGPIPE ile kesilen hatların da başarısız sayılacağı unutulmamalıdır. komut | head -5 biçimindeki hatlar bilinçli olarak bastırılmalıdır.

Alan Ayırıcısını Daraltmak

IFS=$'\n\t' ataması, kelime bölmeden boşluğu çıkarır: bölme yalnızca satır sonu ve sekmede yapılır.

IFS=$'\n\t'
d="a b:c"
printf '<%s>\n' $d
<a b:c>

Tırnaksız genişletme bile bölünmedi. Bu, boşluk içeren dosya adlarının yol açtığı hata sınıfına karşı ek bir savunmadır.

Uyarı gerekiyor: bu ayar geneldir ve boşluğa göre bölmeye dayanan her kodu etkiler. Bir değişkeni sözcüklerine ayırmak isteyen bir döngü, ayardan sonra tek tur döner. Katı kipin diğer üç seçeneği koşulsuz önerilirken bu seçenek, betiğin ne yaptığına bakılarak kararlaştırılmalıdır.

Aritmetik Tuzağı

Aritmetik ve Komut Yerine Koyma dersinde (( )) komutunun çıkış kodunun değerin tersi olduğu gösterilmişti. Katı kip altında bu, betiği durduran bir hataya dönüşür:

#!/usr/bin/env bash
set -e
sayac=0
(( sayac++ ))
echo "BU SATIR YAZILMAZ (sayac=$sayac)"

echo $?
1

sayac++ son ek biçimi eski değeri — yani sıfırı — döndürdü ve (( )) bunu başarısızlık saydı. Güvenli yazım aritmetik genişletmedir:

#!/usr/bin/env bash
set -e
sayac=0
sayac=$(( sayac + 1 ))
echo "sayac=$sayac"
sayac=1

Bu tuzak, katı kipin neden dikkatle uygulandığını gösterir: seçenekler betiğin anlamını değiştirmez, ama hangi durumların “hata” sayılacağını değiştirir ve bazı deyimler bu yeni tanımla uyumsuzdur.

Beklenen Başarısızlıkları Bastırmak

Bazı komutların sıfırdan farklı kod döndürmesi normaldir; süzgeç aracının “eşleşme yok” bildirimi bunun tipik örneğidir. Katı kip altında bu durumlar açıkça bastırılır.

#!/usr/bin/env bash
set -euo pipefail
adet=$(grep -c 'YOKBOYLE' erisim.log || true)
echo "eslesme sayisi: $adet"
if grep -q 'YOKBOYLE' erisim.log; then echo "var"; else echo "yok"; fi
eslesme sayisi: 0
yok

İki yöntem kullanıldı. || true, kodu koşulsuz sıfırlar; sayının kendisi anlamlıysa uygundur. if içinde çağırmak ise başarısızlığı zaten koşul olarak yorumlar; varlık sorusunda doğru biçim budur.

|| true ölçülü kullanılmalıdır. Her satıra eklendiğinde katı kip anlamını yitirir ve betik, hataları yutan eski hâline döner. Kural: bastırma, o komutun başarısızlığının neden beklenen olduğunu bilerek yapılır ve gerekiyorsa bir yorumla belgelenir.

Hata Kancası

trap ... ERR, erken çıkışı tetikleyen her komut için bir işleyici çalıştırır. Hatanın nerede oluştuğunu bildirmek, katı kipin ürettiği sessiz çıkışları izlenebilir kılar.

set -E seçeneği bu kancanın fonksiyonlar ve alt kabuklar içinde de geçerli olmasını sağlar; onsuz kanca yalnızca üst düzeyde çalışır.

#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'

hata_yakala() {
  local kod=$? satir=$1
  printf 'rapor.sh: %d. satirda hata, cikis kodu %d\n' "$satir" "$kod" >&2
  exit "$kod"
}
trap 'hata_yakala "$LINENO"' ERR

echo "1: baslangic"
grep -q 'YOKBOYLE' erisim.log
echo "2: BU SATIR YAZILMAZ"
1: baslangic
rapor.sh: 13. satirda hata, cikis kodu 1

Kanca gövdesindeki ilk satır local kod=$? biçiminde yazılmıştır; $? başka bir komut çalıştırılmadan okunmalıdır, yoksa değeri değişir. $LINENO, kancanın kurulduğu yerde değil tetiklendiği yerde genişlemesi için tek tırnak içinde bırakılmıştır.

trap komutunun kendisi ve sinyal işleme, sonraki dersin konusudur.

Katı Kip Başlığı

Bu dersin sonucu, betiklerin başına konan sabit bir başlıktır:

#!/usr/bin/env bash
set -Eeuo pipefail

Dört seçenek dört ayrı hata sınıfını kapatır: başarısız komut, tanımsız değişken, boru hattında kaybolan kod ve fonksiyon içinde kaybolan hata kancası. Beşincisi — IFS daraltması — betiğe göre kararlaştırılır.

Başlık bir güvence değil, bir taban çizgisidir. set -e istisnaları göz önünde tutulduğunda, kritik komutların kodları hâlâ elle sınanmalıdır. Katı kip, gözden kaçanları yakalar; gözden kaçmaması gerekenlerin yerini tutmaz.

Özet

  • set -e sıfırdan farklı kod döndüren komuttan sonra kabuğu sonlandırır; koşul bağlamlarında, zincir işleçlerinin solunda ve koşulda çağrılan fonksiyonların gövdesinde askıya alınır.
  • set -u tanımsız değişken genişletmesini hata sayar ve öntanımlı değer gösterimlerini zorunlu kılar.
  • set -o pipefail boru hattının kodunu ilk başarısız halkanın koduna bağlar.
  • (( sayac++ )) sıfır değerinde başarısızlık bildirir; katı kip altında sayac=$(( sayac + 1 )) yazılır.
  • Beklenen başarısızlıklar || true ya da koşul bağlamıyla bilinçli olarak bastırılır.
  • set -E ile birlikte trap ... ERR, hatanın satır numarasını bildiren bir kanca kurar.

Sonraki Adım

Katı kip betiği hata anında durduruyor — ama durduğu yerde ne bırakıyor? Yarım yazılmış bir çıktı dosyası, temizlenmemiş geçici dosyalar, başka bir sürecin beklediği bir kilit. Sonraki ders sinyalleri ve çıkışta çalışan temizlik kancalarını ele alır; geçici dosyanın güvenli biçimde yaratılması ve her çıkış yolunda silinmesi de orada kurulacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat