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
ve . 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 (), SIGTERM 15 (). 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 if–else
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ı.
if–else 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 if–else
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;
grepeş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 || yedekyazımı, gövdenin başarısız olabildiği durumdaif–elseile 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.