---
title: 'Tuzaklar ve Temizlik'
source: 'https://academia.sh/tr/kurslar/kabuk-programlama/tuzaklar-ve-temizlik'
course: 'Kabuk Programlama'
language: tr
updated: '2026-08-17T18:10:00+00:00'
license: 'CC BY-SA 4.0'
---

# Tuzaklar ve Temizlik

Sinyal kavramı, tuzak kurma, çıkışta çalışan temizlik kancası, geçici dosyanın güvenli yaratımı ve eşzamanlı çalışmayı engelleyen kilit.

Katı kip betiği hata anında durduruyor. Durduğu yerde ne bıraktığı ise ayrı bir sorudur:
yarım yazılmış çıktı dosyası, temizlenmemiş geçici dizin, başka bir sürecin beklediği bir
kilit. Betiğin doğru sonlanması, doğru çalışması kadar önemlidir.

Bilgisayarlar Nasıl Çalışır kursunda kesme, donanımın işlemciye "işini böl" demesiydi.
**Sinyal (signal)**, aynı fikrin süreçler düzeyindeki karşılığıdır: çekirdeğin ya da başka
bir sürecin, çalışan bir sürece gönderdiği asenkron bildirim. Bu ders sinyalleri yakalamayı
ve çıkışta yapılacak temizliği kurar.

## Sinyaller

Bir süreç sinyal aldığında öntanımlı davranışı uygular — çoğu sinyal için bu, sonlanmaktır.
Betiklerde ilgilenilen sinyaller azdır:

| Sinyal | Numara | Kaynak |
|---|---|---|
| `SIGHUP` | 1 | denetleyen terminal kapandı |
| `SIGINT` | 2 | klavyeden kesme |
| `SIGTERM` | 15 | düzgün sonlandırma isteği |
| `SIGKILL` | 9 | koşulsuz sonlandırma |
| `SIGPIPE` | 13 | okuma ucu kapalı boruya yazma |

Çıkış Kodları dersinde sinyalle sonlanan sürecin **128 + sinyal numarası** kodu döndürdüğü
gösterilmişti; `SIGTERM` için 143, `SIGINT` için 130.

`SIGKILL` ve `SIGSTOP` yakalanamaz, yok sayılamaz ve yeniden tanımlanamaz. Bu, işletim
sisteminin süreç üzerinde son sözü söylemesini güvence altına alır. Dolayısıyla hiçbir
temizlik kancası, `SIGKILL` ile sonlandırılan bir betikte çalışmaz — temizliğin **tek**
dayanağı olamaz.

## Tuzak Kurmak

`trap 'komut' sinyal...` biçimi, sinyal geldiğinde çalışacak komutu belirler. Kabuk
ayrıca üç **sözde sinyal** tanır: `EXIT`, `ERR` ve `DEBUG`.

`EXIT` en önemlisidir: kabuk hangi yolla sonlanırsa sonlansın — normal bitiş, `exit`
çağrısı, katı kipin erken çıkışı, yakalanan bir sinyalin işleyicisinden sonra — çalışır.

```sh
#!/usr/bin/env bash
set -Eeuo pipefail

gecici=$(mktemp -d)
temizle() { rm -rf "$gecici"; }
trap temizle EXIT

echo "veri" > "$gecici/ara.txt"
echo "gecici dizin icerigi: $(ls "$gecici")"
```

```
gecici dizin icerigi: ara.txt
```

Betik bittiğinde dizin silinmiş olur. Hata yolunda da aynısı geçerlidir:

```sh
#!/usr/bin/env bash
set -Eeuo pipefail
gecici=$(mktemp -d)
trap 'rm -rf "$gecici"' EXIT
echo "islem basladi"
false
echo "BU SATIR YAZILMAZ"
```

```
islem basladi
```

Katı kip `false` satırında betiği durdurdu; `EXIT` tuzağı yine de çalıştı ve geçici dizin
kalmadı.

Tuzak, kurulduğu andan itibaren geçerlidir. Bu yüzden **kaynak yaratılır yaratılmaz**
kurulur; iki satır arasında bir hata oluşursa temizlik yapılmamış olur.

## Çıkış Kodunu Korumak

`EXIT` tuzağının gövdesi, betiğin çıkış kodunu değiştirmemelidir. Kodun okunması için
gövdenin **ilk** komutu `$?` okumalıdır:

```sh
#!/usr/bin/env bash
temizle() { local kod=$?; echo "temizlik, korunan kod=$kod"; }
trap temizle EXIT
exit 7
```

```
temizlik, korunan kod=7
```

```sh
echo $?
```

```
7
```

Tuzak gövdesi kendisi bir `exit` çağırırsa betiğin kodu o olur; çağırmazsa özgün kod
korunur. Temizlik başarısızlığının betiğin sonucunu değiştirmesi istenmiyorsa, gövdedeki
komutların kodları bastırılmalıdır.

## Sinyali Yeniden Göndermek

Bir sinyali yakalayıp temizlik yapmak, sürecin o sinyalle sonlandığı bilgisini yok eder:
betik normal biterse çıkış kodu 0 olur ve çağıran, sürecin kesildiğini anlayamaz.

Doğru desen üç adımlıdır: temizle, tuzağı kaldır, sinyali kendine yeniden gönder.

```sh
#!/usr/bin/env bash
temizle() { echo "temizlik calisti (kaynak: $1)"; }
trap 'temizle EXIT' EXIT
trap 'temizle TERM; trap - TERM; kill -TERM $$' TERM
echo "hazir"
sleep 30
```

Betik arka planda başlatılıp `SIGTERM` gönderildiğinde:

```
hazir
temizlik calisti (kaynak: TERM)
temizlik calisti (kaynak: EXIT)
```

```sh
echo $?
```

```
143
```

Kod `143`; yani `128 + 15`. Süreci yöneten bir servis yöneticisi ya da zamanlayıcı, bu
kodu görerek betiğin hata vermediğini, sonlandırıldığını ayırt edebilir.

`trap - SINYAL` yazımı tuzağı kaldırıp öntanımlı davranışı geri getirir. Bu adım atlanırsa
`kill` çağrısı yine kendi tuzağını tetikler ve sonsuz döngü oluşur.

`EXIT` tuzağının da çalıştığına dikkat edin: temizlik iki kez yürüdü. Bu yüzden temizlik
fonksiyonu **yinelemeye dayanıklı (idempotent)** yazılmalıdır — `rm -rf` ve `rmdir` çağrıları
zaten yok olan bir yolu ikinci kez silmeye çalıştığında zarar vermez, ama sayaç artıran ya
da ileti gönderen bir temizlik iki kez çalışırsa yanlış olur.

## Geçici Dosyaları Güvenle Yaratmak

Geçici dosya adını elle üretmek — `/tmp/rapor.$$` gibi — üç açıdan yanlıştır.

**Öngörülebilir.** Süreç numaraları dar bir aralıktadır ve yeniden kullanılır; bir
saldırgan adı önceden tahmin edip o yola bir sembolik bağ koyabilir. Betik dosyayı
açtığında bağın gösterdiği yere yazar.

**Yarış içerir.** "Dosya var mı" sınaması ile yaratma arasındaki aralık, araya girmeye
açıktır.

**Çakışabilir.** Aynı betiğin iki örneği aynı süreç numarasını farklı zamanlarda alabilir.

`mktemp`, adı öngörülemez biçimde üretir ve dosyayı **atomik olarak** yaratır; ad
çakışırsa yeniden dener. `-d` seçeneği dizin yaratır ve birden çok geçici dosya gerektiğinde
tek bir temizlik noktası sağlar.

```sh
gecici=$(mktemp -d)
trap 'rm -rf "$gecici"' EXIT
```

`mktemp` yarattığı dosya ve dizinleri yalnızca sahibinin erişebileceği izinlerle açar;
elle `touch` edilen bir dosyada bu güvence yoktur.

`rm -rf "$gecici"` satırındaki tırnak zorunludur. Değişken tanımsız kalırsa — `set -u`
olmadan — komut `rm -rf` hâline gelir; tırnak, boşluk içeren yollarda ikinci bir korumadır.
Katı kipin `set -u` seçeneği bu satırın en önemli koruyucusudur.

## Eşzamanlı Çalışmayı Engellemek

Zamanlanmış bir rapor betiği, önceki çalışma bitmeden yeniden başlatılabilir. İki örnek
aynı geçici dosyalara yazarsa sonuç bozulur.

Kilit almanın taşınabilir yolu dizin yaratmaktır: `mkdir`, hedef zaten varsa başarısız olan
**atomik** bir işlemdir. Dosya yaratmak aynı güvenceyi vermez, çünkü "var mı" sınaması ile
yaratma iki ayrı adımdır.

```sh
#!/usr/bin/env bash
set -Eeuo pipefail
KILIT="rapor.kilit"

if ! mkdir "$KILIT" 2>/dev/null; then
  echo "baska bir calisma suruyor, cikiliyor" >&2
  exit 75
fi
trap 'rmdir "$KILIT"' EXIT

echo "kilit alindi, is yapiliyor"
sleep 1
```

Bir örnek çalışırken ikincisi başlatıldığında:

```
kilit alindi, is yapiliyor
baska bir calisma suruyor, cikiliyor
ikinci calisma kodu=75
```

Tuzağın `mkdir` başarılı olduktan **sonra** kurulması zorunludur. Önce kurulsaydı, kilidi
alamayan ikinci örnek çıkarken başkasının kilidini silerdi.

`75` kodu, "geçici hata, sonra yeniden dene" anlamında yaygın bir sözleşmedir; zamanlayıcı
bu kodu gerçek bir hatadan ayırt edebilir.

Bu desenin bilinen zayıflığı, betik `SIGKILL` ile sonlandırıldığında ya da makine
kapandığında kilidin kalmasıdır. Uzun ömürlü sistemlerde kilit dizinine süreç numarası
yazılır ve kilit alınamadığında o sürecin hâlâ yaşayıp yaşamadığı sınanır.

## Betiğe Uygulama

`rapor.sh` katı kip başlığını ve temizlik altyapısını alır:

```sh
#!/usr/bin/env bash
# rapor.sh — erisim kutugunden ozet rapor uretir.
set -Eeuo pipefail
export LC_ALL=C

BETIK_ADI="rapor.sh"
gecici=""

temizle() {
  local kod=$?
  [ -n "$gecici" ] && rm -rf "$gecici"
  return "$kod"
}
trap temizle EXIT
trap 'temizle; trap - INT;  kill -INT  $$' INT
trap 'temizle; trap - TERM; kill -TERM $$' TERM

gecici=$(mktemp -d)
```

`gecici` değişkeninin tuzak kurulmadan **önce** boş dizgiyle ilklendirilmesi, `set -u`
altında gereklidir: tuzak, `mktemp` çağrısından önce bir hata oluşursa da çalışır ve o anda
değişken tanımsız olurdu.

Önceki dersteki `karsilastir` fonksiyonu artık geçici dosya bırakmıyor; `$gecici` altındaki
her şey çıkışta siliniyor.

## Özet

- Sinyal, çalışan bir sürece gönderilen asenkron bildirimdir; `SIGKILL` ve `SIGSTOP`
  yakalanamaz, bu yüzden temizlik tek dayanak olamaz.
- `trap ... EXIT`, kabuğun hangi yolla sonlandığından bağımsız olarak çalışır ve kaynak
  yaratılır yaratılmaz kurulur.
- `EXIT` tuzağının ilk komutu `$?` okumalıdır; yoksa betiğin çıkış kodu kaybolur.
- Sinyal yakalandığında temizlenip tuzak kaldırılır ve sinyal kendine yeniden gönderilir;
  böylece çıkış kodu `128 + sinyal` olarak korunur.
- Geçici dosya `mktemp` ile yaratılır: ad öngörülemez, yaratım atomiktir ve izinler
  dardır.
- `mkdir` atomik olduğu için taşınabilir kilit mekanizması olarak kullanılır; tuzak
  kilidi aldıktan sonra kurulur.

## Sonraki Adım

Betik artık düzgün duruyor ve arkasında iz bırakmıyor. Yanlış sonuç üretiyorsa bunu nasıl
bulacağımız ise başka bir soru. Sonraki ders hata ayıklama araçlarını ele alır: her komutu
çalıştırmadan önce yazan izleme kipi, izleme çıktısını okunur kılan biçim değişkeni,
çalıştırmadan yapılan sözdizim denetimi ve izlemenin betiğin yalnızca bir bölümüne
uygulanması.
