Ders 22 / 24
Betik Hata Ayıklama
Sözdizim denetimi, izleme kipi ve istem biçimi, izlemenin yönlendirilmesi, değişken ve çağrı yığını incelemesi.
İçindekiler
Betik düzgün duruyor ve arkasında iz bırakmıyor. Yanlış sonuç ürettiğinde bunu nasıl bulacağımız ayrı bir sorudur.
Kabuk betiklerinde hata ayıklamayı zorlaştıran iki özellik vardır. Birincisi, çalışan
şeyin çoğunlukla kabuğun kendisi değil çağırdığı programlar olması; ikincisi, genişletme
kurallarının sonucu yazılan metinden farklı kılabilmesi. İkinci nokta belirleyicidir:
rm "$dizin/$ad" satırının ne sildiğini, kabuğun o satırı nasıl genişlettiğini görmeden
bilemezsiniz.
Bu dersin araçları tam olarak o genişletilmiş hâli görünür kılar.
Çalıştırmadan Denetlemek
bash -n betiği ayrıştırır ama hiçbir komut çalıştırmaz. Betik Anatomisi dersinde
tanıtılmıştı; yıkıcı işlem içeren betiklerde ilk adımdır.
Sınanan bozuk.sh, kapatılmamış bir koşul içerir:
#!/usr/bin/env bash # kutuk ozeti — sozdizimi hatasi barindirir kutuk="$1" if [ -f "$kutuk" ]; then echo "satir sayisi: $(wc -l < "$kutuk")"
bash -n bozuk.sh
bozuk.sh: line 7: syntax error: unexpected end of file from `if' command on line 5
Hata iletisi iki satır numarası verir: sorunun fark edildiği yer ve açık kalan yapının başladığı yer. İkincisi genellikle daha yararlıdır.
-n yalnızca sözdizimini görür. Var olmayan komutlar, yanlış seçenekler, tanımsız
değişkenler ve mantık hataları bu denetimden geçer. Bir betiğin bash -n ile temiz
çıkması, çalışacağı anlamına gelmez.
İzleme Kipi
set -x seçeneği, her komutu genişletmelerden sonra ve çalıştırmadan önce standart
hataya yazar. Betiğin tamamı için bash -x betik biçiminde de açılabilir.
bash -x ayikla.sh
+ kutuk=erisim.log + adet=3 + en_cok erisim.log 3 + cut '-d ' -f7 erisim.log + sort + uniq -c + sort -k1,1nr + head -3
Üç bilgi görülüyor. Değişkenler beklenen değerleri aldı. Fonksiyon erisim.log ve 3
argümanlarıyla çağrıldı. Boru hattının halkaları ayrı ayrı listelendi ve head -3
satırındaki 3, değişkenin genişlemiş hâli.
Dördüncü satırdaki '-d ' yazımına dikkat edin: kabuk, tırnaklama gerektiren argümanları
izleme çıktısında tırnak içinde gösterir. Bu, boşluk içeren bir argümanın gerçekten tek
parça geçip geçmediğini gösteren en doğrudan kanıttır.
İzleme İstemini Biçimlendirmek
Öntanımlı + öneki, uzun betiklerde hangi satırın çalıştığını söylemez. PS4 değişkeni
bu öneki belirler ve her satır yazılmadan önce genişletilir.
export PS4='+ ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-ana}: ' bash -x ayikla.sh
+ ayikla.sh:2:ana: kutuk=erisim.log + ayikla.sh:3:ana: adet=3 + ayikla.sh:7:ana: en_cok erisim.log 3 + ayikla.sh:5:en_cok: cut '-d ' -f7 erisim.log + ayikla.sh:5:en_cok: sort + ayikla.sh:5:en_cok: uniq -c + ayikla.sh:5:en_cok: sort -k1,1nr + ayikla.sh:5:en_cok: head -3
Her satır artık dosya adı, satır numarası ve fonksiyon adı taşıyor. PS4 ataması tek
tırnak içinde yazılmıştır: değişkenler atama anında değil, her izleme satırı yazılırken
genişlemelidir.
FUNCNAME[0] üst düzeyde tanımsızdır; :-ana öntanımlı değeri set -u altında da
çalışmasını sağlar.
Seçmeli İzleme
Uzun bir betiğin tamamını izlemek, aranan satırı binlerce satırlık çıktının içinde bırakır.
set -x ve set +x çiftiyle yalnızca ilgilenilen bölüm izlenir.
#!/usr/bin/env bash echo "izlenmeyen bolum" set -x kod=404 sinif="${kod:0:1}" set +x echo "sonuc: ${sinif}xx"
izlenmeyen bolum + kod=404 + sinif=4 + set +x sonuc: 4xx
Kapanış satırı da izleme çıktısına düşer; kapanışın kendisi çalıştırılan bir komuttur.
İzlemeyi Ayırmak
İzleme çıktısı standart hataya gider. Betiğin uyarıları da oraya gittiği için, ikisi karışır ve betiğin çıktısını kütüğe alan bir zamanlanmış görevde okunmaz hâle gelir.
BASH_XTRACEFD değişkeni izlemeyi başka bir tanıtıcıya yönlendirir:
#!/usr/bin/env bash exec 9> izleme.log BASH_XTRACEFD=9 set -x kod=404 sinif="${kod:0:1}" set +x echo "rapor ciktisi: ${sinif}xx"
rapor ciktisi: 4xx
izleme.log içeriği:
+ kod=404 + sinif=4 + set +x
Terminale yalnızca raporun kendisi düştü. Yönlendirme dersinde tanıtılan exec ile
tanıtıcı açma deseni burada işlevini buluyor: 3 ve üzeri numaralar bu tür kullanımlar için
serbesttir.
Ayrıntılı Kip
set -v, komutları genişletmeden önce, kaynak dosyadaki hâliyle yazar.
#!/usr/bin/env bash set -v kod=404 echo "sinif: ${kod:0:1}xx"
kod=404
echo "sinif: ${kod:0:1}xx"
sinif: 4xx
-x genişletilmiş hâli, -v yazılmış hâli gösterir. İkisi birlikte açıldığında bir
genişletmenin girdisi ve çıktısı yan yana görülür; genişletme kurallarının yanlış
anlaşıldığı durumlarda en doğrudan tanı yoludur.
Her Komuttan Önce Çalışan Kanca
trap ... DEBUG her basit komuttan önce çalışır. BASH_COMMAND değişkeni, çalıştırılmak
üzere olan komutun metnini tutar.
#!/usr/bin/env bash trap 'printf "[%s] %s\n" "$LINENO" "$BASH_COMMAND" >&2' DEBUG kod=404 sinif="${kod:0:1}" echo "sonuc: ${sinif}xx"
[3] kod=404
[4] sinif="${kod:0:1}"
[5] echo "sonuc: ${sinif}xx"
sonuc: 4xx
DEBUG kancası, set -x ile elde edilemeyen bir şey sağlar: kancanın gövdesi rasgele
kod olabilir. Belirli bir değişken beklenmedik bir değere geçtiğinde durmak ya da her
komuttan önce bir değişkenin değerini yazmak bu yolla yapılır.
Bedeli maliyettir: kanca her komut için çalışır. Üretim betiklerinde bırakılmaz.
Değişkenleri İncelemek
declare -p, bir değişkenin tipini ve içeriğini yeniden kullanılabilir biçimde yazar.
Diziler için echo yeterli değildir; declare -p eleman sınırlarını gösterir.
kodlar=(200 404 500) declare -A sayim=([404]=4 [500]=3) declare -p kodlar sayim
declare -a kodlar=([0]="200" [1]="404" [2]="500") declare -A sayim=([404]="4" [500]="3" )
-a dizinli, -A ilişkisel diziyi gösteriyor. Seyrek bir dizide dizinlerin atladığı,
boş bir elemanın gerçekten boş olduğu ancak bu çıktıda görülür.
Çağrı Yığınını Okumak
Bash, çağrı zincirini üç koşut dizide tutar: FUNCNAME fonksiyon adlarını, BASH_SOURCE
dosya adlarını, BASH_LINENO çağrı satırlarını. Bir hata işleyicisinde bunları yazmak,
hatanın nereden geldiğini gösterir.
nerede() {
local i
for (( i = 1; i < ${#FUNCNAME[@]}; i++ )); do
printf ' %s() <- %s:%s\n' "${FUNCNAME[i]}" "${BASH_SOURCE[i]}" "${BASH_LINENO[i-1]}"
done
}
ic() { nerede; }
dis() { ic; }
dis
ic() <- ./yigin.sh:8 dis() <- ./yigin.sh:9 main() <- ./yigin.sh:10
Zincir içten dışa okunur: nerede çağrısı ic içinden geldi, ic çağrısı dis
içinden, dis çağrısı üst düzeyden. Dizin kaydırması (BASH_LINENO[i-1]) bilinçlidir;
üç dizi aynı olayın farklı yönlerini farklı hizalamalarla tutar.
Bu fonksiyon, önceki derste kurulan ERR kancasına eklendiğinde betik, hatanın yalnızca
satırını değil çağrı yolunu da bildirir.
Bir Yöntem
Araçların sırası, hata ayıklamanın yöntemini de verir:
bash -nile sözdizimini geçirin.- Katı kip başlığını ekleyin; hataların çoğu kendiliğinden görünür hâle gelir.
- Sorunun hangi bölümde olduğunu daraltın — bölümün sonuna geçici bir
exitkoyup ikiye bölmek en hızlı yoldur. - Daraltılan bölümü
set -xile izleyin vePS4ile konum bilgisi ekleyin. - Genişletmenin girdisi ile çıktısı arasında fark arıyorsanız
-vile birlikte açın. - Karmaşık veri yapılarını
declare -pile yazdırın.
Bu sıra, en ucuz aracı en başa koyar. set -x ile başlanan bir oturum, bash -n’in bir
saniyede bulacağı bir sözdizim hatasını aramakla geçebilir.
Özet
bash -nyalnızca sözdizimini denetler; çalıştırmaz ve mantık hatalarını görmez.set -xher komutu genişletmelerden sonra yazar; tırnaklamanın doğru çalışıp çalışmadığını gösteren doğrudan kanıttır.PS4izleme önekini belirler ve her satır için yeniden genişletilir; tek tırnak içinde atanmalıdır.BASH_XTRACEFDizleme çıktısını ayrı bir tanıtıcıya yollayarak betiğin kendi iletilerinden ayırır.set -vyazıldığı hâli,set -xgenişletilmiş hâli gösterir; birlikte kullanıldıklarında genişletmenin girdisi ve çıktısı karşılaştırılır.declare -pdizi içeriğini sınırlarıyla birlikte,FUNCNAME/BASH_SOURCE/BASH_LINENOçağrı yığınını verir.
Sonraki Adım
Bu 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, taşınabilir olmayan seçenek. Sonraki ders bu sınıfı çalıştırmadan yakalayan
statik çözümleme aracını ele alır ve rapor.sh betiğini onun denetiminden geçirir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.