İçeriğe geç
academia.sh

Ders 23 / 24

Statik Çözümleme

Çalıştırmadan hata bulma, yaygın kabuk hata sınıfları, yorumlayıcı bildirimine göre taşınabilirlik denetimi ve uyarı bastırmanın doğru kullanımı.

İçindekiler

Önceki dersin araçları hatayı ortaya çıktıktan sonra bulur. Kabuk betiklerindeki hataların büyük bölümü ise ortaya çıkmadan önce belirlidir: tırnaklanmamış genişletme, sınanmamış çıkış kodu, dosya listesini komut çıktısından üretme, yorumlayıcı bildiriminin izin vermediği bir özelliğin kullanılması.

Bu hatalar kodun kendisine bakılarak, çalıştırılmadan görülebilir. Statik çözümleme (static analysis), kaynak metni ayrıştırıp bilinen hatalı desenleri arayan yöntemdir. Kabuk için bu iş, betiği ayrıştırıp yüzlerce desen kuralını uygulayan bir araca devredilir.

Aracın değeri, bu kursta tek tek anlatılan tuzakların hepsini birden hatırlamasıdır.

Bir Denetim

Aşağıdaki betik, önceki derslerde ele alınan hataların çoğunu bir arada taşıyor:

#!/bin/sh
kutuk=$1
for f in $(ls kutukler); do
  echo "islenen: $f"
done
if [ $kutuk = "erisim.log" ]; then
  adet=`wc -l < $kutuk`
  echo "satir: $adet"
fi
cd /tmp
rm -rf $kutuk_yedek

Çözümleyicinin uzun biçimli çıktısı, bulguyu kaynak satırın altında işaretler ve düzeltilmiş yazımı önerir. Aynı bulgular tek satırlık biçimde daha derli toplu okunur:

shellcheck -f gcc kusurlu.sh
kusurlu.sh:3:10: error: Iterating over ls output is fragile. Use globs. [SC2045]
kusurlu.sh:6:6: note: Double quote to prevent globbing and word splitting. [SC2086]
kusurlu.sh:7:8: note: Use $(...) notation instead of legacy backticks `...`. [SC2006]
kusurlu.sh:7:17: note: Double quote to prevent globbing and word splitting. [SC2086]
kusurlu.sh:10:1: warning: Use 'cd ... || exit' or 'cd ... || return' in case cd fails. [SC2164]
kusurlu.sh:11:8: warning: kutuk_yedek is referenced but not assigned. [SC2154]

Beş ayrı hata sınıfı, on bir satırlık bir betikte. Sonuncusunun sonucu, set -u olmayan bir betikte rm -rf komutunun argümansız çalışmasıdır.

Öntanımlı biçim daha ayrıntılıdır: her bulgunun altına Did you mean: başlığıyla düzeltilmiş satırı yazar. Tek bir bulguyla uğraşırken bu biçim, toplu denetimde ise yukarıdaki tek satırlık biçim uygundur.

Yakalanan Hata Sınıfları

Uyarılar dört öbeğe ayrılır ve her biri bu kursta ayrı bir dersin konusuydu.

Genişletme ve tırnaklama. Tırnaklanmamış değişken genişletmesi, "$@" yerine $* kullanımı, dizi genişletmesinin yanlış biçimi. Değişkenler ve Tırnak Kuralları dersinin tamamı bu sınıfa aittir. En sık görülen uyarı budur.

Çıkış kodu. Sınanmayan cd, sınanmayan komut yerine koyma, local ile birlikte atama yapılması, boru hattı kodunun kaybolması. Hata Yönetimi dersindeki tuzaklar burada yakalanır.

Kabuk deyimleri. Dosya listesini komut çıktısından üretme, read çağrısında -r eksikliği, eski ters tırnak yazımı, [ ] içinde yanlış işleç. Döngüler ve Koşullar derslerinin uyarıları.

Tanımsız ad. Hiç atanmamış bir değişkenin okunması, yazım yanlışıyla farklı bir ada dönüşen değişkenler. Bu sınıf, yorumlanan bir dilde çalışma anında bile fark edilmeyebilir.

Her uyarının bir kimliği vardır (SC ve dört rakam). Kimlik iki işe yarar: uyarının ayrıntılı açıklamasına ulaşmak ve — gerektiğinde — o uyarıyı hedefli biçimde bastırmak.

Şiddet Düzeyleri

Uyarılar dört düzeye ayrılır: error, warning, info, style. -S seçeneği belirli bir düzeyin altındakileri gizler:

shellcheck -S warning -f gcc kusurlu.sh
kusurlu.sh:3:10: error: Iterating over ls output is fragile. Use globs. [SC2045]
kusurlu.sh:10:1: warning: Use 'cd ... || exit' or 'cd ... || return' in case cd fails. [SC2164]
kusurlu.sh:11:8: warning: kutuk_yedek is referenced but not assigned. [SC2154]

Üç bulgu kaldı; info ve style düzeyindeki tırnaklama ve ters tırnak uyarıları gizlendi.

Bu, var olan bir kod tabanına çözümleme eklerken yararlıdır: önce error düzeyi temizlenir, sonra eşik kademeli olarak düşürülür. Eşiği kalıcı olarak yüksek tutmak ise aracın değerinin çoğunu bırakmak demektir — info düzeyindeki tırnaklama uyarıları en sık gerçek hataya dönüşenlerdir.

Taşınabilirlik Denetimi

Çözümleyici, yorumlayıcı bildirimini okur ve kuralları ona göre uygular. #!/bin/sh bildiren bir betikte bash eklentileri hata sayılır:

#!/bin/sh
kodlar=(200 404)
if [[ ${#kodlar[@]} -gt 1 ]]; then
  echo "birden cok kod"
fi
In tasinabilir.sh line 2:
kodlar=(200 404)
       ^-------^ SC3030 (warning): In POSIX sh, arrays are undefined.


In tasinabilir.sh line 3:
if [[ ${#kodlar[@]} -gt 1 ]]; then
   ^-----------------------^ SC3010 (warning): In POSIX sh, [[ ]] is undefined.

Bu, Betik Anatomisi dersindeki kuralın araçla uygulanmasıdır: POSIX bildirip bash özelliği kullanmak, betiğin başka bir sistemde sessizce bozulmasına yol açar. Aynı betik bash bildirdiğinde bu iki uyarı üretilmez.

Denetimin sınırı da buradan görülür: araç, bildirilen kabuğa göre karar verir. Bildirim yanlışsa denetim de yanlış yapılır. Bu yüzden yorumlayıcı bildirimi, betiğin gerçekten kullandığı özelliklere göre yazılmalıdır.

Uyarı Bastırma

Bir uyarının bilinçli olarak yok sayılması gerektiğinde, kaynak koda bir yönerge yazılır:

#!/usr/bin/env bash
# shellcheck disable=SC2086
kutuk="a b.log"
echo $kutuk

Yönerge, kendisinden sonraki komuta uygulanır; dosyanın başına, ilk komuttan önce konursa tüm dosyayı kapsar.

Bastırma üç kurala bağlıdır.

Kimlik verilir. disable yönergesi kimliksiz yazılabilir ama o zaman tüm uyarıları kapatır ve gelecekte oluşacak gerçek hataları da gizler.

Kapsam daraltılır. Dosya düzeyinde bastırma, o dosyadaki her satırı denetimden çıkarır. Uyarının doğduğu satırın hemen üstüne yazmak yeğlenir.

Gerekçe yazılır. Bastırılan uyarının neden yanlış alarm olduğu bir yorumla belirtilir. Gerekçesiz bir bastırma, kodu okuyan bir sonraki kişi için “burada ne olduğunu bilmiyorum” demektir.

Kelime bölmenin gerçekten istendiği durumlarda daha iyi bir çözüm vardır: değeri bir diziye almak. "${dizi[@]}" yazımı hem uyarı üretmez hem de niyeti kodda görünür kılar. Bastırma, yeniden yazmanın mümkün olmadığı hâllere bırakılır.

Betiği Denetimden Geçirmek

rapor.sh bu kurs boyunca, çözümleyicinin uyaracağı desenlerden kaçınılarak yazıldı. Aşağıdaki küçük betik aynı kuralları izler:

#!/usr/bin/env bash
set -Eeuo pipefail
kutuk="${1:-erisim.log}"
[ -r "$kutuk" ] || { echo "okunamiyor: $kutuk" >&2; exit 3; }
adet=$(grep -c '' "$kutuk")
printf 'satir: %d\n' "$adet"
shellcheck temiz.sh; echo "kod=$?"
kod=0

Çıkış kodu 0 ve hiçbir çıktı yok. Araç, bulgu varsa sıfırdan farklı kod döndürür; bu, denetimi bir doğrulama adımına dönüştürmeyi mümkün kılar:

shellcheck rapor.sh && bash -n rapor.sh && echo "denetim gecti"

Sürüm denetim sistemine gönderim öncesinde ya da bir sürekli tümleştirme adımında bu komutun çalıştırılması, kabuk betiklerinin kalitesini korumanın en ucuz yoludur.

Statik Çözümlemenin Sınırı

Araç, kodun anlamını değil biçimini görür. Yakalayamadıkları:

  • Yanlış alan numarası ($7 yerine $8 yazmak).
  • Yanlış düzenli ifade (kalıp geçerli ama beklenen kümeyi betimlemiyor).
  • Yanlış iş mantığı (yüzdenin yanlış toplama bölünmesi).
  • Çalışma ortamına bağlı hatalar (var olmayan komut, yetersiz izin).

Bu sınıflar için sınama gerekir: betiği bilinen bir girdiyle çalıştırıp çıktısını beklenen çıktıyla karşılaştırmak. Bu kursun erisim.log dosyasının sabit ve yeniden üretilebilir olması, tam olarak böyle bir sınamayı mümkün kılmak içindir.

Statik çözümleme sınamanın yerini tutmaz; sınamanın bulacağı hata sayısını azaltır.

Özet

  • Statik çözümleme, betiği çalıştırmadan ayrıştırıp bilinen hatalı desenleri arar.
  • Yakalanan başlıca sınıflar tırnaklama, çıkış kodu, kabuk deyimleri ve tanımsız adlardır; hepsi bu kursta ayrı derslerin konusuydu.
  • Denetim, yorumlayıcı bildirimine göre yapılır; POSIX bildiren betikte bash eklentileri uyarı üretir.
  • Bastırma kimlikle, dar kapsamla ve gerekçeyle yapılır; kelime bölme isteniyorsa doğru çözüm dizi kullanmaktır.
  • Araç bulgu varsa sıfırdan farklı kod döndürür; bu, denetimi bir doğrulama adımına çevirir.
  • Statik çözümleme biçimi görür, anlamı görmez; yanlış alan numarası ve yanlış iş mantığı ancak sınamayla bulunur.

Sonraki Adım

Betik doğru, sağlam ve denetlenmiş durumda — ama hâlâ elle çalıştırılıyor. Bir raporun değeri düzenli üretilmesindedir. Son ders zamanlanmış görevleri ele alır: süreli görev tanımının okunması, zamanlayıcı ortamının etkileşimli kabuktan hangi noktalarda ayrıldığı, çıktının nereye yazılacağı ve üst üste binen çalışmaların engellenmesi.

İ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