---
title: 'Betik Hata Ayıklama'
source: 'https://academia.sh/tr/kurslar/kabuk-programlama/betik-hata-ayiklama'
course: 'Kabuk Programlama'
language: tr
updated: '2026-08-17T18:10:00+00:00'
license: 'CC BY-SA 4.0'
---

# 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.

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:

```sh
#!/usr/bin/env bash
# kutuk ozeti — sozdizimi hatasi barindirir
kutuk="$1"

if [ -f "$kutuk" ]; then
  echo "satir sayisi: $(wc -l < "$kutuk")"
```

```sh
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.

```sh
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.

```sh
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.

```sh
#!/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:

```sh
#!/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.

```sh
#!/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.

```sh
#!/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.

```sh
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.

```sh
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:

1. `bash -n` ile sözdizimini geçirin.
2. Katı kip başlığını ekleyin; hataların çoğu kendiliğinden görünür hâle gelir.
3. Sorunun hangi bölümde olduğunu daraltın — bölümün sonuna geçici bir `exit` koyup
   ikiye bölmek en hızlı yoldur.
4. Daraltılan bölümü `set -x` ile izleyin ve `PS4` ile konum bilgisi ekleyin.
5. Genişletmenin girdisi ile çıktısı arasında fark arıyorsanız `-v` ile birlikte açın.
6. Karmaşık veri yapılarını `declare -p` ile 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 -n` yalnızca sözdizimini denetler; çalıştırmaz ve mantık hatalarını görmez.
- `set -x` her komutu genişletmelerden **sonra** yazar; tırnaklamanın doğru çalışıp
  çalışmadığını gösteren doğrudan kanıttır.
- `PS4` izleme önekini belirler ve her satır için yeniden genişletilir; tek tırnak içinde
  atanmalıdır.
- `BASH_XTRACEFD` izleme çıktısını ayrı bir tanıtıcıya yollayarak betiğin kendi
  iletilerinden ayırır.
- `set -v` yazıldığı hâli, `set -x` genişletilmiş hâli gösterir; birlikte kullanıldıklarında
  genişletmenin girdisi ve çıktısı karşılaştırılır.
- `declare -p` dizi 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.
