İçeriğe geç
academia.sh

Ders 05 / 24

Çıkış Kodları

Başarı/başarısızlık sözleşmesi, ayrılmış kod aralıkları, koşullu zincirleme işleçleri ve bu işleçlerin koşullu dallanmayla karıştırılmaması.

İçindekiler

Önceki derslerde komutların sonuçları hep beklendiği gibi çıktı. Gerçekte grep eşleşme bulamayabilir, cut dosyayı açamayabilir, sort yeterli geçici alan bulamayabilir. Kabuğun bu durumları öğrenme yolu tek bir sayıdır.

Her süreç sonlanırken çekirdeğe bir tam sayı bırakır. Kabuk bu sayıyı çıkış kodu (exit status) olarak alır ve $? özel değişkeninde saklar. Kabuk programlamanın denetim akışı — koşullar, döngü koşulları, hata yönetimi — tümüyle bu sayının üzerine kuruludur.

Sıfır Neden Başarıdır

Sözleşme şudur: 0 başarı, sıfırdan farklı her değer başarısızlıktır.

Programlama Temelleri kursunda mantıksal doğruluk için true değeri kullanılmıştı; kabuktaki sözleşme bunun tersi görünür. Gerekçe basittir: bir işin başarılı olmasının tek bir anlamı vardır, başarısız olmasının ise pek çok nedeni. Sıfırdan farklı 255 değer, farklı başarısızlık türlerini ayırt etmek için kullanılabilir; başarı için tek bir değer yeterlidir.

grep bu ayrımın açık bir örneğidir ve üç kodu birbirinden ayırır:

grep -q '404' erisim.log;      echo "bulundu    -> $?"
grep -q '999' erisim.log;      echo "bulunamadi -> $?"
grep -q '404' yok-dosya.log 2>/dev/null; echo "dosya yok  -> $?"
bulundu    -> 0
bulunamadi -> 1
dosya yok  -> 2

Üçü de birbirinden farklıdır ve bu fark iş görür. “Eşleşme yok” beklenen bir sonuçtur; “dosya yok” ise betiğin durması gereken bir durumdur. Yalnızca “sıfır mı değil mi” diye bakan bir betik bu ikisini karıştırır.

Kendi betiklerinizde de aynı titizlik beklenir: farklı hata sınıflarına farklı kodlar verin, 1 kodunu her şey için kullanmayın.

Sözleşmeyi Somutlaştıran Komutlar

true ve false yerleşik komutları hiçbir iş yapmaz; yalnızca kod döndürür.

true;  echo "true  -> $?"
false; echo "false -> $?"
true  -> 0
false -> 1

: komutu true ile eşdeğerdir ve önceki derste dosya boşaltmak için kullanılmıştı.

test komutu ve onun köşeli ayraç yazımı [ ... ] bir koşulu sınar ve sonucu kod olarak döndürür. Bu komutun hiçbir çıktısı yoktur; tüm bilgi koddadır.

[ -f erisim.log ]; echo "dosya var -> $?"
[ -f yok.log ];    echo "dosya yok -> $?"
dosya var -> 0
dosya yok -> 1

[ bir sözdizim öğesi değil, çalıştırılabilir bir komut adıdır; bu yüzden çevresindeki boşluklar zorunludur ve kapanış ] onun son argümanıdır. Testlerin ayrıntısı Koşullar ve Testler dersinde ele alınacak.

Kodun Sayısal Sınırları

Çıkış kodu 0–255 aralığında bir bayta sığar. Bu aralığın dışındaki değerler 256’ya göre indirgenir:

(exit 300); echo "300 -> $?"
(exit -1);  echo "-1  -> $?"
300 -> 44
-1  -> 255

300mod256=44300 \bmod 256 = 44 ve 1mod256=255-1 \bmod 256 = 255. Betiğinizde 255’ten büyük bir kod döndürmenin anlamı yoktur; sayı sessizce başka bir şeye dönüşür.

Ayrılmış Kodlar

Kabuk bazı kodları kendi kullanımı için ayırır. Bu kodları kendi hata sınıflarınıza vermek, betiğinizin çağıranı yanıltmasına yol açar.

yokboylebirkomut 2>/dev/null; echo "bulunamayan komut  -> $?"
./calismaz.txt   2>/dev/null; echo "calistirilamayan   -> $?"
bulunamayan komut  -> 127
calistirilamayan   -> 126

127, komutun arama yolunda bulunamadığını; 126, bulunduğunu ama çalıştırılamadığını (çalıştırma izni yok ya da dizin) bildirir. Bir betiği çağırdığınızda 127 alıyorsanız sorun betiğin içinde değil, adında veya arama yolundadır.

Sinyalle sonlanan süreçler için kabuk 128 + sinyal numarası biçiminde kod üretir:

bash -c 'trap - INT; kill -INT $$'; echo "INT  -> $?"
bash -c 'kill -TERM $$';            echo "TERM -> $?"
INT  -> 130
TERM -> 143

SIGINT 2 numaralıdır (128+2=130128 + 2 = 130), SIGTERM 15 (128+15=143128 + 15 = 143). Klavyeden kesilen bir betiğin 130 döndürmesinin nedeni budur. Sinyaller ve yakalanmaları Sağlam Betik Yazımı konusunda ayrıntılı ele alınacak.

Kullanılabilir aralık pratikte 1–125’tir. exit komutu argümansız çağrılırsa, betikte çalışan son komutun kodunu döndürür.

Koşullu Zincirleme

Kabuk, çıkış koduna bağlı iki zincirleme işleci tanımlar.

&& işleci, solundaki komut 0 döndürürse sağdakini çalıştırır. || işleci, solundaki komut sıfırdan farklı döndürürse sağdakini çalıştırır. Her iki durumda da sağdaki komut çalışmazsa zincirin kodu soldakinin kodudur.

grep -q '404' erisim.log && echo "kutukte 404 var"
grep -q '999' erisim.log || echo "999 gecmiyor"
kutukte 404 var
999 gecmiyor

Bu işleçler kısa devre değerlendirme (short-circuit evaluation) yapar — Programlama Temelleri kursunda mantıksal işleçler için tanımlanan davranışın aynısıdır. Fark, burada değerlendirilenin bir ifade değil, çalıştırılan bir süreç olmasıdır: kısa devre yalnızca zaman kazandırmaz, bir işin hiç yapılmamasını sağlar.

Önceki derste güvenli yerinde dönüştürme deseni bu işleçle yazılmıştı:

sort kucuk.txt > kucuk.yeni && mv kucuk.yeni kucuk.txt

sort başarısız olursa mv hiç çalışmaz ve özgün dosya yerinde kalır. Yıkıcı işlemi başarı koşuluna bağlamak, kabukta hata yönetiminin en yalın biçimidir.

Zincirleme Koşullu Dallanma Değildir

&& ve || aynı önceliğe sahiptir ve soldan sağa birleşir. Bu iki kural, sık görülen bir yanlış anlamayı doğurur: koşul && gövde || yedek yazımının ifelse ile eşdeğer olduğu sanılır. Değildir.

yanlis() { echo "govde calisti"; return 1; }

true && yanlis || echo "YEDEK CALISTI"
govde calisti
YEDEK CALISTI

Koşul doğruydu, gövde çalıştı — ama gövde başarısızlık bildirdiği için yedek de çalıştı. ifelse böyle davranmaz:

if true; then yanlis; else echo "YEDEK"; fi
govde calisti

Kural: &&|| zinciri yalnızca gövdenin asla başarısız olmayacağı durumda ifelse yerine geçer. Bu güvence nadiren verilebildiği için, iki dallı seçimde if yazmak doğru varsayılandır. && ve ||, tek dallı korumalar için — “başarılıysa devam et”, “başarısızsa uyar ve çık” — uygundur.

Soldan sağa birleşme, karışık zincirlerde de kendini gösterir:

false || echo "birinci" && echo "ikinci"
birinci
ikinci

false || echo "birinci" önce değerlendirilir; echo başarılı olduğu için zincirin kodu 0’dır ve && sağdakini çalıştırır. Okunması güç zincirler yerine gruplama kullanın: { ...; } içindeki komut listesinin kodu, listenin son komutunun kodudur.

Değilleme

! öneki, bir komutun kodunu tersine çevirir: 0 ise sıfırdan farklı, sıfırdan farklı ise 0 yapar.

! grep -q '999' erisim.log; echo "degilleme -> $?"
degilleme -> 0

“Kütükte 999 geçmiyorsa” koşulunu ifade etmenin doğrudan yolu budur. Değillemenin ikinci bir işlevi de vardır: katı kip altında (set -e) beklenen başarısızlıkları bastırmak. Bu kullanım Hata Yönetimi dersinde ele alınacak.

Konunun Toparlanması

Bu konuda kurulan dört kavram, kursun geri kalanının sözlüğünü oluşturur. Kütükten en çok istenen beş yolu çıkaran hat, bu sözlüğün tamamını kullanır:

[ -r erisim.log ] || { echo 'kutuk okunamiyor' >&2; exit 2; }
cut -d' ' -f7 erisim.log | sort | uniq -c | sort -rn | head -5

Birinci satır bir çıkış kodu sınaması, bir zincirleme işleci, bir gruplama ve standart hataya yönlendirme içerir. İkinci satır dört komutluk bir boru hattıdır. İkisi birlikte, sonraki konuda yazılacak rapor.sh betiğinin çekirdeğidir.

Özet

  • Her süreç sonlanırken 0–255 aralığında bir çıkış kodu bırakır; 0 başarı, sıfırdan farklı her değer başarısızlıktır. Kod $? ile okunur.
  • Farklı başarısızlık türleri farklı kodlarla ayrılır; grep eşleşmesizlik için 1, dosya hatası için 2 döndürür.
  • 126, 127 ve 128 + sinyal numarası kabuk tarafından ayrılmıştır; kendi kodlarınız için 1–125 aralığı kullanılır.
  • && ve || kısa devre değerlendirme yapar; aynı öncelikte olup soldan sağa birleşir.
  • koşul && gövde || yedek yazımı, gövdenin başarısız olabildiği durumda ifelse ile eşdeğer değildir.

Sonraki Adım

Bu konudaki her şey tek satırlık komutlarla yapıldı. Aynı hattı her gün çalıştırmak, üzerine seçenek eklemek ve başkasına devretmek gerektiğinde tek satır yetmez. Sonraki konu betik dosyasını kurar: yorumlayıcı bildiriminin ne işe yaradığını, betiğin nasıl çalıştırılabilir hâle geldiğini ve kabuğun bir dosyayı yürütürken hangi kabuğu kullandığını ele alarak başlar.

İ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