İçeriğe geç
academia.sh

Ders 09 / 24

Koşullar ve Testler

Koşulun bir çıkış kodu olması, dosya/dizgi/sayı testleri, tek ve çift ayraç arasındaki ayrıştırma farkı, kalıp eşleme ve çoklu seçim.

İçindekiler

Betik, kendisine verilen kütüğün var olduğunu ve okunabildiğini varsayıyor. Varsayımı sınamak bir koşul gerektirir. Programlama Temelleri kursunda koşul, mantıksal bir ifadeydi ve doğru ya da yanlış değerine indirgenirdi.

Kabukta böyle bir tip yoktur. Koşul, çalıştırılan bir komuttur; sonucu çıkış kodudur. if yapısı bir ifadeyi değerlendirmez, bir komut çalıştırır ve kodu 0 ise gövdeyi yürütür. Bu tek fark, kabuk koşullarının tüm sözdizim tuhaflıklarını açıklar.

if Bir Komut Alır

if komut; then
  ...
elif baska_komut; then
  ...
else
  ...
fi

Herhangi bir komut koşul olabilir:

if grep -q '404' erisim.log; then
  echo "kutukte 404 var"
fi

Koşullar için kullanılan [ ... ] yazımı bir sözdizim öğesi değil, test adlı komutun ikinci adıdır. Bu yüzden [ ile ilk argüman arasında boşluk zorunludur ve kapanış ] bu komutun son argümanıdır. [-f dosya] yazımı [-f adında bir komut arar ve bulunamaz.

Dosya Testleri

test, dosya sistemine ilişkin bir işleçler ailesi tanır.

İşleç Doğru olduğu durum
-e yol var (türü ne olursa olsun)
-f düz dosya
-d dizin
-r, -w, -x okuma / yazma / çalıştırma izni var
-s dosya var ve boyutu sıfırdan büyük
-L sembolik bağ
a -nt b a, b’den daha yeni değiştirilmiş
[ -f erisim.log ]; echo "duz dosya  -> $?"
[ -d erisim.log ]; echo "dizin      -> $?"
[ -x erisim.log ]; echo "calistirma -> $?"
duz dosya  -> 0
dizin      -> 1
calistirma -> 1

İzin testleri, dosyanın izin bitlerini değil çağıran sürecin o dosyada o işlemi yapıp yapamayacağını sınar; süper kullanıcı için sonuç farklı çıkabilir. Linux’a Giriş kursunda kurulan izin modeli burada davranışa dönüşür.

-e ile -f ayrımı ihmal edilmemelidir: bir dizin de -e sınamasını geçer, ama cut ile okunamaz.

Dizgi ve Sayı Testleri

Dizgi testleri: -z boş mu, -n boş değil mi, = eşit mi, != farklı mı.

Sayı testleri ayrı işleçler kullanır: -eq, -ne, -lt, -le, -gt, -ge.

İki ailenin ayrı olması gereklidir, çünkü kabukta her değer bir karakter dizisidir ve “eşitlik” iki farklı anlama gelir:

[ "007" =   "7" ]; echo "dizgi esitligi -> $?"
[ "007" -eq "7" ]; echo "sayi esitligi  -> $?"
dizgi esitligi -> 1
sayi esitligi  -> 0

007 ile 7 farklı dizgilerdir ama aynı sayıdır. Hangi anlamın istendiğine yazan karar verir; işleç seçimi bu kararın bildirimidir.

Sayı işleçleri yalnızca tam sayılarla çalışır. Ondalıklı değer verilirse test hata bildirir. Ondalıklı karşılaştırma gerekiyorsa bir hesaplama aracına başvurulur; bu, Aritmetik ve Komut Yerine Koyma dersinde ele alınacak.

Tek Ayracın Ayrıştırma Sorunu

[ sıradan bir komut olduğu için argümanları önce kabuk tarafından genişletilir ve kelimelere bölünür. Bölme sonucu, test’in beklediği argüman sayısını değiştirebilir.

bos=""
[ $bos = "x" ]
bash: [: =: unary operator expected

Genişletme sonrası [ komutuna yalnızca =, x, ] argümanları ulaştı; test bunu tek işlenenli bir ifade sandı. Aynı sorun boşluk içeren değerde ters yönde çıkar:

d="iki kelime"
[ $d = "iki kelime" ]
bash: [: too many arguments

Her iki hata da tırnaklamayla giderilir:

[ "$bos" = "x" ]; echo "kod=$?"
kod=1

Eski betiklerde [ "x$a" = "xb" ] biçiminde bir kalıp görülür. Amacı, değişkenin değerinin bir işleçle karışmasını önlemektir; tırnaklama yeterli olduğu için bu kalıp artık gereksizdir, ama karşılaşıldığında ne yaptığı bilinmelidir.

Çift Ayraç

Bash ve ksh, [[ ... ]] adında ayrı bir sözdizim öğesi tanır. Bu bir komut değildir; kabuk onu ayrıştırma aşamasında tanır ve içindeki genişletmelere kelime bölme ile yol adı genişletmesi uygulamaz.

bos=""; d="iki kelime"
[[ $bos = "x" ]];        echo "bos     -> $?"
[[ $d = "iki kelime" ]]; echo "bosluklu -> $?"
bos     -> 1
bosluklu -> 0

Tırnaksız yazımlar hata vermedi. Bu, [[ ]] içinde tırnak gerekmediği anlamına gelir — ama tırnak yazmak da zarar vermez ve alışkanlığı bozmamak için yeğlenebilir.

Çift ayracın üç ek yeteneği vardır.

Kalıp eşleme. = veya == işlecinin sağ yanı tırnaksızsa yol adı kalıbı olarak yorumlanır:

yol="/api/veri"
[[ $yol == /api/* ]]   && echo "api altinda"
[[ $yol == "/api/*" ]] || echo "tirnaklanmis kalip harfi harfine"
api altinda
tirnaklanmis kalip harfi harfine

Sağ yanın tırnaklanması kalıbı sıradan dizgiye çevirir. Bu, kalıp eşlemesinin istenmediği durumlarda gerekli olan denetimdir.

Düzenli ifade eşleme. =~ işleci sağ yanı genişletilmiş düzenli ifade sayar:

kod="404"
[[ $kod =~ ^[45][0-9][0-9]$ ]] && echo "hata sinifi kodu"
hata sinifi kodu

Düzenli ifade yazımı Metin İşleme konusunda ayrıntılı ele alınacak. Burada önemli olan, kalıbın tırnaksız yazılmasıdır: tırnaklanan bir sağ yan harfi harfine eşleşme arar.

Mantıksal işleçler. && ve ||, ayraç içinde koşulları birleştirir:

[[ -f erisim.log && -r erisim.log ]] && echo "okunabilir dosya"
okunabilir dosya

[ ] içinde aynı iş için -a ve -o işleçleri bulunur, ancak bunlar ayrıştırma belirsizliği ürettikleri için önerilmez. Taşınabilir yazımda iki ayrı test && ile zincirlenir:

[ -f erisim.log ] && [ -r erisim.log ] && echo "okunabilir dosya"

[[ ]] bash eklentisidir; POSIX kabuk dilinde yoktur. #!/bin/sh bildiren bir betikte kullanılamaz. Karar kuralı: bash bildiriliyorsa [[ ]] yeğlenir — tırnaklama hatalarına karşı bağışıktır; POSIX gerekiyorsa [ ] ve titiz tırnaklama kullanılır.

Çoklu Seçim

Aynı değeri birçok olasılığa karşı sınayan ifelif zincirleri yerine case yazılır. Kalıplar yol adı kalıbı sözdizimini kullanır.

for k in 200 301 404 500 xyz; do
  case "$k" in
    2??) sinif="basarili" ;;
    3??) sinif="yonlendirme" ;;
    4??) sinif="istemci hatasi" ;;
    5??) sinif="sunucu hatasi" ;;
    *)   sinif="tanimsiz" ;;
  esac
  printf '%-4s -> %s\n' "$k" "$sinif"
done
200  -> basarili
301  -> yonlendirme
404  -> istemci hatasi
500  -> sunucu hatasi
xyz  -> tanimsiz

case, ilk eşleşen dalı çalıştırır ve durur; dallar arasında düşme yoktur. Bir kalıba birden çok seçenek | ile eklenir: 200|201|204). Son dal olarak yazılan *), hiçbir kalıba uymayan değerleri yakalar ve her case yapısında bulunmalıdır — eksikse tanınmayan değer sessizce yok sayılır.

case POSIX’tir ve [[ ]] gerektirmeden kalıp eşleme sağlar; taşınabilir betiklerde kalıp sınamasının doğru aracıdır.

Betiğe Uygulama

rapor.sh girdilerini sınayacak biçimde genişletilir. Hata sınıflarına ayrı kodlar verilmesi, önceki dersteki sözleşmenin sürdürülmesidir.

kutuk="${1:-erisim.log}"

if ! [[ "$adet" =~ ^[0-9]+$ ]] || [ "$adet" -lt 1 ]; then
  echo "hata: -n icin pozitif tam sayi bekleniyor: $adet" >&2
  exit 2
fi

if [ ! -e "$kutuk" ]; then
  echo "hata: kutuk yok: $kutuk" >&2
  exit 3
elif [ ! -f "$kutuk" ]; then
  echo "hata: duz dosya degil: $kutuk" >&2
  exit 3
elif [ ! -r "$kutuk" ]; then
  echo "hata: okuma izni yok: $kutuk" >&2
  exit 3
elif [ ! -s "$kutuk" ]; then
  echo "uyari: kutuk bos: $kutuk" >&2
fi

Sayı sınamasında [ "$adet" -lt 1 ] testinden önce düzenli ifade sınaması yapılmasının nedeni sıradır: adet sayı değilse -lt hata verirdi. || kısa devre yaptığı için ikinci sınamaya yalnızca birincisi geçtiğinde ulaşılır.

Dört çağrı dört yolu izler:

./rapor.sh yok.log;  echo "kod=$?"
./rapor.sh -n abc;   echo "kod=$?"
./rapor.sh /etc;     echo "kod=$?"
hata: kutuk yok: yok.log
kod=3
hata: -n icin pozitif tam sayi bekleniyor: abc
kod=2
hata: duz dosya degil: /etc
kod=3

Özet

  • Kabukta koşul bir komuttur; if çıkış koduna bakar. [ ], test komutunun ikinci adıdır ve çevresindeki boşluklar zorunludur.
  • Dizgi eşitliği =, sayı eşitliği -eq ile sınanır; 007 ile 7 dizgi olarak farklı, sayı olarak aynıdır.
  • [ ] içindeki genişletmeler kelime bölmeye uğradığı için tırnaklama zorunludur; boş veya boşluklu değer aksi hâlde ayrıştırma hatası üretir.
  • [[ ]] ayrıştırma düzeyinde tanınır, bölme uygulamaz, kalıp ve düzenli ifade eşlemesi ile mantıksal işleçler sunar; bash eklentisidir.
  • case ilk eşleşen dalı çalıştırır, düşme yoktur ve *) dalı her zaman yazılmalıdır.

Sonraki Adım

Rapor şu an tek bir listeyi üretiyor. Durum kodu sınıflarının her biri için ayrı sayım istendiğinde aynı boru hattını elle çoğaltmak gerekir. Sonraki ders yinelemeyi ele alır: liste üzerinde dönen döngüler, sayaçlı döngüler ve bir dosyayı satır satır okumanın — kabukta hiç de apaçık olmayan — doğru biçimi.

İ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