Ders 10 / 24
Döngüler
Liste, sayaç ve koşul tabanlı yineleme, dosya adları üzerinde güvenli dönme, satır satır okumanın doğru biçimi ve döngü denetimi.
İçindekiler
Rapor şu an tek bir liste üretiyor. Durum kodu sınıflarının her biri için ayrı sayım istendiğinde aynı boru hattını dört kez elle yazmak gerekir. Programlama Temelleri kursunda bu, yinelemenin klasik gerekçesiydi.
Kabuktaki döngüler aynı yapıları kurar, ama listenin nereden geldiği kabuğun genişletme kurallarına bağlıdır. Bu ders döngü biçimlerini ve — kabuk programlamanın en sık yanlış yazılan parçası olan — dosyayı satır satır okuma desenini ele alır.
Liste Üzerinde Dönme
for döngüsü, kendisine verilen kelime listesinin her elemanı için gövdeyi çalıştırır.
for sinif in 2 3 4 5; do adet=$(cut -d' ' -f9 erisim.log | grep -c "^${sinif}") echo "${sinif}xx -> $adet" done
2xx -> 18 3xx -> 3 4xx -> 6 5xx -> 3
$( ... ) gösterimi bir komutun çıktısını değere çevirir; ayrıntısı bu konunun son
dersinde ele alınacak.
Listedeki “kelime” kavramı, önceki derslerde kurulan kelime bölme kavramıyla aynıdır. Liste bir değişkenden geliyorsa, tırnak kullanımı doğrudan döngünün tur sayısını belirler:
kodlar="200 404 500" for k in $kodlar; do printf '<%s> ' "$k"; done; echo for k in "$kodlar"; do printf '<%s> ' "$k"; done; echo
<200> <404> <500> <200 404 500>
Bu, kelime bölmenin bilinçli olarak istendiği ender durumlardan biridir. Yine de değeri tek bir dizgide tutup bölünmesine güvenmek kırılgandır; birden çok değer taşınacaksa dizi kullanmak doğru araçtır. Diziler bu konunun yedinci dersinin konusudur.
Dosya Adları Üzerinde Dönme
Bir dizindeki dosyalar üzerinde dönmenin doğru yolu, yol adı kalıbını doğrudan listeye yazmaktır:
mkdir -p kutukler; : > 'kutukler/a.log'; : > 'kutukler/b c.log' for f in kutukler/*.log; do echo " [$f]"; done
[kutukler/a.log] [kutukler/b c.log]
Boşluk içeren ad tek parça kaldı; çünkü yol adı genişletmesinin sonucu kelime bölmeye uğramaz. Bu, ikinci derste kurulan kuralın pratik karşılığıdır.
Aynı işi for f in $(ls kutukler) biçiminde yazmak yanlıştır: ls çıktısı bir metindir
ve kelime bölmeye uğrar; boşluk içeren her ad parçalanır. Kabukta dosya listesi üretmenin
aracı ls değil, yol adı genişletmesidir.
Kalıbın hiçbir dosyayla eşleşmemesi ayrı bir tuzaktır:
for f in kutukler/*.yok; do echo " [$f]"; done
[kutukler/*.yok]
Eşleşme yoksa kabuk kalıbı harfi harfine bırakır ve döngü bir kez, var olmayan bir dosya adıyla döner. İki çözüm vardır. Taşınabilir olanı, gövdenin başına varlık sınaması koymaktır:
for f in kutukler/*.yok; do [ -e "$f" ] || continue echo " [$f]" done
Bash’e özgü olanı nullglob seçeneğidir; eşleşmeyen kalıp boş listeye genişler ve döngü
hiç dönmez:
shopt -s nullglob for f in kutukler/*.yok; do echo " [$f]"; done shopt -u nullglob
Seçenek genel bir ayardır ve etkin olduğu sürece tüm kalıpları etkiler; betiğin başında açılıp bırakılması, ilerideki satırlarda beklenmedik davranışa yol açabilir.
Sayaçlı Döngüler
Sabit sayıda tur için üç yazım vardır.
Ayraç genişletmesi derleme zamanında sabit bir liste üretir:
for i in {1..5}; do printf '%s ' "$i"; done; echo
1 2 3 4 5
Ayraç genişletmesi değişken içeremez — {1..$n} beklendiği gibi çalışmaz, çünkü ayraç
genişletmesi değişken genişletmesinden önce uygulanır. Bu, genişletme sırasının
gözlemlenebilir sonuçlarından biridir.
C biçimli döngü bash eklentisidir ve değişken sınır alabilir:
for ((i = 1; i <= 5; i++)); do printf '%s ' "$i"; done; echo
1 2 3 4 5
Dizi üreten komut, taşınabilir üçüncü yoldur: for i in $(seq 1 "$n"). Komut
çağırması nedeniyle en pahalı seçenektir, ama POSIX kabuk dilinde çalışır.
Koşul Tabanlı Döngüler
while, koşul komutu 0 döndürdüğü sürece; until sıfırdan farklı döndürdüğü sürece
gövdeyi yineler.
sayac=0 until [ "$sayac" -ge 3 ]; do echo "sayac=$sayac" sayac=$((sayac + 1)) done
sayac=0 sayac=1 sayac=2
İkisi birbirinin yerine kullanılabilir; until, koşulun değillemesini yazmaktan kurtarır
ve okunurluğu artırdığı durumlarda yeğlenir.
Dosyayı Satır Satır Okumak
Kabuk programlamada en sık yanlış yazılan desen budur. Doğru biçim üç parçadan oluşur ve her parçanın ayrı bir gerekçesi vardır:
while IFS= read -r satir; do ... done < dosya
-r seçeneği ters bölü işlemesini kapatır. Bu seçenek olmadan read, ters bölüyü bir
kaçış karakteri sayar ve veriyi bozar.
IFS= ataması satırın başındaki ve sonundaki boşlukların kırpılmasını engeller.
Yönlendirme, boru değil. Boru hattının sağ ucundaki döngü alt kabukta çalışır ve atadığı değişkenler kaybolur; bu, Boru Hattı dersinde gösterilmişti.
Üç parçanın etkisi ölçülebilir. Aşağıdaki dosya üç satırdır: ilkinde iki boşluk, ikincisi ters bölüyle başlar, üçüncüsünün iki ucunda boşluk vardır.
printf 'bir iki\n\\ters bolulu\n bosluklu kenar \n' > garip.txt while read satir; do printf '<%s>\n' "$satir"; done < garip.txt
<bir iki> <ters bolulu> <bosluklu kenar>
while IFS= read -r satir; do printf '<%s>\n' "$satir"; done < garip.txt
<bir iki> <\ters bolulu> < bosluklu kenar >
İlk yazımda ters bölü yutuldu ve kenar boşlukları kırpıldı. İkincisi dosyayı olduğu gibi verdi.
Dördüncü bir ayrıntı, son satırda satır sonu bulunmamasıdır. read satır sonu görmeden
dosya sonuna ulaşırsa okuduğunu değişkene yazar ama sıfırdan farklı kod döndürür; döngü
koşulu bu kodu yanlış anlar ve son satır işlenmez:
printf 'birinci\nikinci' > eksik.txt while IFS= read -r s; do echo "okundu: $s"; done < eksik.txt
okundu: birinci
Koşula bir ek yapılır: değişken boş değilse döngü bir tur daha döner.
while IFS= read -r s || [ -n "$s" ]; do echo "okundu: $s"; done < eksik.txt
okundu: birinci okundu: ikinci
Bu kalıp, dış kaynaklı verinin okunduğu her betikte gereklidir; satır sonuyla bitmeyen dosyalar nadir değildir.
Satırı Alanlara Bölerek Okumak
read birden çok değişken alırsa satırı IFS’e göre böler ve son değişkene kalan her
şeyi verir. Kütüğün alanları boşlukla ayrıldığı için doğrudan okunabilir:
head -3 erisim.log | while read -r adres _ _ _ _ yontem yol _ kod bayt; do printf '%-12s %-4s %-18s %s %s\n' "$adres" "${yontem#\"}" "$yol" "$kod" "$bayt" done
10.0.0.12 GET /index.html 200 5120 10.0.0.31 GET /statik/stil.css 200 2048 10.0.0.12 GET /urun/12 200 8410
_ adı bir sözleşmedir, dil öğesi değil: ilgilenilmeyen alanlar aynı değişkene atanır.
${yontem#\"} gösterimi baştaki tırnağı siler; dizgi işleme gösterimleri Aritmetik ve
Komut Yerine Koyma dersinde toparlanacak.
Bu döngü, alan sayısı sabit olan veriler için yeterlidir. Alan sayısı değişken olduğunda ya da hesaplama gerektiğinde alan tabanlı bir araç daha uygundur; Metin İşleme konusunda aynı iş tek satıra inecek.
Döngü Denetimi
break içinde bulunduğu döngüyü sonlandırır, continue turu atlar. İkisi de sayı
argümanı alarak dış döngülere etki edebilir.
for k in 200 301 404 500 xyz 200; do case "$k" in [0-9][0-9][0-9]) : ;; *) echo "gecersiz kod, atlaniyor: $k"; continue ;; esac if [ "$k" -ge 500 ]; then echo "sunucu hatasi bulundu: $k"; break; fi echo "islendi: $k" done
islendi: 200 islendi: 301 islendi: 404 sunucu hatasi bulundu: 500
break 2 iki düzey dışarı çıkar:
for a in 1 2; do for b in x y; do [ "$b" = y ] && break 2 echo "$a$b" done done
1x
Sayılı break okunurluğu hızla bozar; iki düzeyden fazlasında döngüyü bir fonksiyona
alıp return kullanmak daha anlaşılır olur. Fonksiyonlar sonraki dersin konusudur.
Betiğe Uygulama
rapor.sh ikinci bir bölüm kazanır: durum kodu sınıflarının dağılımı.
kutuk=erisim.log echo "== Durum kodu siniflari ==" for sinif in 2 3 4 5; do adet=$(cut -d' ' -f9 "$kutuk" | grep -c "^${sinif}") case "$sinif" in 2) ad="basarili" ;; 3) ad="yonlendirme" ;; 4) ad="istemci hatasi" ;; 5) ad="sunucu hatasi" ;; esac printf ' %sxx %-16s %3d\n' "$sinif" "$ad" "$adet" done
== Durum kodu siniflari == 2xx basarili 18 3xx yonlendirme 3 4xx istemci hatasi 6 5xx sunucu hatasi 3
Sayıların toplamı otuzdur; kütükteki satır sayısına eşittir. Bu tür bir iç tutarlılık sınaması, rapor betiklerinin doğruluğunu ucuz biçimde denetlemenin yoludur.
Özet
forlistesi, kabuğun genişletme kurallarıyla oluşur; tırnaksız değişken bölünür, tırnaklı bölünmez.- Dosya listesi
lsçıktısından değil yol adı genişletmesinden üretilir; eşleşme yoksa kalıp harfi harfine kalır ve[ -e "$f" ] || continueya danullglobgerekir. - Ayraç genişletmesi değişken alamaz, çünkü değişken genişletmesinden önce uygulanır.
- Satır satır okumanın doğru biçimi
while IFS= read -r satir; do ... done < dosyayazımıdır; boru kullanılırsa döngü alt kabuğa düşer. - Satır sonuyla bitmeyen dosyalarda son satır ancak
|| [ -n "$satir" ]ekiyle işlenir.
Sonraki Adım
Betik büyüdükçe aynı iş — hata bildirme, bölüm başlığı yazma, bir alanı sayma — birden
çok yerde tekrar ediyor. Sonraki ders fonksiyonları ele alır: kabukta fonksiyon tanımının
ne olduğunu, argümanların nasıl geçtiğini, return ile exit arasındaki farkı ve
kabuğun kapsam modelinin Programlama Temelleri kursundakinden hangi noktada ayrıldığını.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.