---
title: 'Zamanlanmış Görevler'
source: 'https://academia.sh/tr/kurslar/kabuk-programlama/zamanlanmis-gorevler'
course: 'Kabuk Programlama'
language: tr
updated: '2026-08-17T18:10:00+00:00'
license: 'CC BY-SA 4.0'
---

# Zamanlanmış Görevler

Süreli görev tanımının okunması, zamanlayıcı ortamının etkileşimli kabuktan farkı, çıktı yönetimi, üst üste binmeyi engelleyen sarmalayıcı.

Betik doğru, sağlam ve denetlenmiş durumda — ama hâlâ elle çalıştırılıyor. Bir raporun
değeri düzenli üretilmesindedir: her sabah bir önceki günün kütüğünü çözümleyen ve sonucu
bir yere bırakan bir araç, elle çalıştırılan aynı araçtan farklı bir şeydir.

Bu ders, bir betiği zamanlanmış çalışmaya hazırlamayı ele alır. Konu yalnızca zamanlama
tanımı değildir; asıl mesele, betiğin **insan olmayan bir çağırıcı** tarafından, dar bir
ortamda ve gözlemsiz çalıştırılacak olmasıdır.

## Süreli Görev Tanımı

Unix sistemlerinde süreli görevler bir zamanlayıcı hizmetine tanımlanır. Tanım tablosu,
kullanıcı başına tutulur ve her satırı beş zaman alanı ile bir komuttan oluşur:

```
dakika  saat  ayın günü  ay  haftanın günü  komut
```

| Alan | Aralık |
|---|---|
| dakika | 0–59 |
| saat | 0–23 |
| ayın günü | 1–31 |
| ay | 1–12 |
| haftanın günü | 0–7 (0 ve 7 pazar) |

Her alan dört biçimden birini alır: sabit sayı, `*` (her değer), `a-b` (aralık), `a,b,c`
(liste). Bir aralığın ardına `/n` eklenirse `n` adımlı seçim yapılır.

```
30 6 * * *          her gun 06:30
0 */4 * * *         her dort saatte bir, saat basi
15 2 * * 1          her pazartesi 02:15
0 0 1 * *           her ayin birinci gunu gece yarisi
*/10 * * * *        her on dakikada bir
```

İki alan birlikte kısıt getirir ama **ayın günü ile haftanın günü** ikilisi istisnadır:
ikisi de `*` dışında bir değer alırsa koşullar "ve" ile değil "veya" ile birleşir. `0 0 13 * 5`
tanımı, ayın 13'ünde **ya da** cuma günü çalışır. Bu, tanımların en sık yanlış okunan
noktasıdır.

Tanım tablosu `crontab -e` ile düzenlenir, `crontab -l` ile listelenir. `crontab -r`
tabloyu **onay sormadan siler**; `-e` ile arasında tek harf bulunduğu için, listeleme
alışkanlığı `-l` üzerinden kurulmalıdır.

Bazı zamanlayıcılar `@daily`, `@hourly`, `@reboot` gibi kısaltmalar tanır. Bunlar POSIX'te
tanımlı değildir; taşınabilir tanımlarda beş alanlı biçim kullanılır.

Zamanlayıcı hizmetinin bir başka sınıfı da servis yöneticisinin zamanlayıcı birimleridir.
Bağımlılık tanımlama, kaçırılan çalışmaları telafi etme ve çalışmaların kütüğünü servis
altyapısıyla birleştirme yetenekleri sunar. Servis yöneticisi modeli Sistem Yönetimi
kursunun konusudur.

## Zamanlayıcı Ortamı

Zamanlanmış görevlerin başarısız olmasının en yaygın nedeni, betikte değil ortamdadır.
Zamanlayıcı, komutu **oturum açma kabuğu olmadan** çalıştırır: kabuk başlangıç dosyaları
okunmaz, kabuk fonksiyonları ve takma adlar yoktur, ortam değişkenlerinin çoğu
tanımsızdır.

En belirgin fark arama yoludur. Dar bir ortamda çalıştırılan aynı betik:

```sh
env -i PATH=/usr/bin:/bin bash ortam.sh
```

```
PATH=/usr/bin:/bin
shellcheck bulundu mu: hayir
```

Etkileşimli oturumda bulunan bir araç, zamanlanmış çalışmada bulunamayabilir. Sonuç,
"betik elle çalışıyor ama zamanlayıcıda çalışmıyor" biçiminde ortaya çıkar ve zamanlayıcı
sessiz olduğu için fark edilmesi gecikir.

Üç önlem alınır.

**Arama yolunu betikte belirtin.** Betiğin başına `export PATH=...` yazmak, çağırma
ortamından bağımsızlık sağlar. Kullanılan araçların yolları biliniyorsa bu, en sağlam
çözümdür.

**Dosya yollarını saltık yazın.** Zamanlayıcının başlangıç dizini garanti değildir; göreli
yollar farklı bir dizinde çözülür. Betiğin okuduğu ve yazdığı her yol saltık olmalıdır.

**Gereken ortam değişkenlerini betikte tanımlayın.** Yerel ayar, dil ve saat dilimi
değişkenleri etkileşimli oturumdan devralınmaz. Bu kursta `export LC_ALL=C` satırının
betiğin başına konması, bu nedenle de gereklidir: sıralama düzeni, zamanlayıcı ortamında
başka türlü belirsiz kalırdı.

Tanım tablosuna özgü bir ayrıntı daha vardır: komut alanında `%` karakteri satır sonu
anlamına gelir ve kaçırılmadan yazılamaz. Tarih biçimlendirmesi gibi `%` içeren komutlar
doğrudan tabloya yazılmaz — bir sarmalayıcı betiğe konur.

## Çıktı Nereye Gider

Zamanlayıcı, görevin ürettiği standart çıktı ve standart hatayı toplayıp kullanıcıya
iletmeye çalışır. İletim yapılandırılmamışsa çıktı kaybolur ya da yerel bir posta
kuyruğunda birikir.

Bu davranış iki sonuç doğurur. Birincisi, her çalıştırmada çıktı üreten bir görev sürekli
bildirim üretir; bildirimler bir süre sonra okunmaz olur. İkincisi, hata iletileri de aynı
yolla kaybolur.

Doğru düzen, çıktıyı açıkça yönetmektir:

```
30 6 * * * /opt/rapor/rapor-gorevi.sh
```

ve yönlendirmeleri betiğin içinde yapmak. Betiğin içinde yapıldığında yollar okunur kalır,
`%` sorunu doğmaz ve yönlendirme mantığı sürüm denetimine girer.

Konunun ilk dersinde kurulan akış ayrımı burada karşılığını bulur: **raporun kendisi bir
dosyaya, tanı iletileri ayrı bir dosyaya** yazılır. İkisi karıştırılırsa raporu okuyan bir
sonraki adım uyarı metnini veri sanar.

## Sarmalayıcı Betik

Zamanlanmış görev, iş yapan betiği doğrudan çağırmaz; onu bir sarmalayıcıyla sarar.
Sarmalayıcı ortamı kurar, kilidi alır, çıktıyı yönlendirir ve iş betiğini çağırır.

```sh
#!/usr/bin/env bash
set -Eeuo pipefail
export PATH=/usr/local/bin:/usr/bin:/bin
export LC_ALL=C

KOK="/opt/rapor"
KILIT="$KOK/kilit"
KUTUK_DIZINI="$KOK/kutuk"

mkdir -p "$KUTUK_DIZINI"

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

"$KOK/rapor.sh" -n 3 "$KOK/erisim.log" \
  >> "$KUTUK_DIZINI/rapor.out" 2>> "$KUTUK_DIZINI/rapor.err"
```

Beş karar bir arada duruyor.

**Kilit.** Tuzaklar ve Temizlik dersinde kurulan `mkdir` deseni burada gerçek işlevini
buluyor: her on dakikada bir çalışan ve bazen on dakikadan uzun süren bir görev, kilit
olmadan kendi kendine çakışır. `75` kodu "geçici hata" bildirir.

**Ortam.** `PATH` ve `LC_ALL` betiğin içinde sabitlendi; zamanlayıcının verdiği ortama
güvenilmiyor.

**Saltık yollar.** Hiçbir göreli yol yok; başlangıç dizini ne olursa olsun betik aynı
dosyaları bulur.

**Ayrık çıktı.** Rapor `rapor.out`, tanı iletileri `rapor.err` dosyasına ekleniyor.
Yönlendirmeler `>>` biçiminde; her çalıştırma öncekinin üzerine yazmıyor.

**Katı kip.** Sarmalayıcının kendisi de betiktir ve aynı kurallara tabidir.

Kütük dosyalarının sonsuza dek büyümesi ayrı bir sorundur ve dosya döndürme araçlarıyla
çözülür; bu araçlar Sistem Yönetimi kursunda ele alınır.

## Görevi Sınamak

Zamanlanmış bir görev, tanımlanmadan önce zamanlayıcının koşullarında sınanmalıdır. Sınama
sırası şudur:

1. Sarmalayıcıyı elle çalıştırın; çıktı dosyalarının doğru yerde oluştuğunu doğrulayın.
2. Aynı çağrıyı dar bir ortamda yineleyin: `env -i PATH=/usr/bin:/bin ./rapor-gorevi.sh`.
   Bu, zamanlayıcı ortamına yakın bir benzetimdir.
3. Kilidi sınayın: bir örnek çalışırken ikincisini başlatın ve `75` kodunu doğrulayın.
4. Hata yolunu sınayın: var olmayan bir kütük adı verin ve `rapor.err` dosyasına anlamlı
   bir ileti düştüğünü görün.
5. Tanımı tabloya, gerçek zamanından birkaç dakika sonrasına yazın ve ilk çalışmayı
   gözleyin.

Çıkış kodu bu zincirin son halkasıdır. Görevin başarısız olduğunu bir izleme sistemine
bildiren şey çıkış kodudur; bu yüzden betiğin ürettiği kodlar bilinçli seçilmiş olmalıdır.
Bu kursta kullanılan kod düzeni şöyleydi:

| Kod | Anlamı |
|---|---|
| 0 | rapor üretildi |
| 2 | kullanım hatası (geçersiz seçenek ya da değer) |
| 3 | girdi hatası (kütük yok, okunamıyor) |
| 75 | geçici hata, sonra yeniden dene (kilit alınamadı) |
| 128 + n | sinyalle sonlandırıldı |

## Tamamlanmış Betik

Kurs boyunca geliştirilen betiğin son hâli:

```sh
#!/usr/bin/env bash
# rapor.sh — erisim kutugunden ozet rapor uretir.
# Kullanim: rapor.sh [-n SAYI] [KUTUK]
set -Eeuo pipefail
export LC_ALL=C

BETIK_ADI="rapor.sh"
SATIR_KALIBI='^[0-9.]+ - - \[[^]]+\] "[A-Z]+ [^ ]+ HTTP/[0-9.]+" [0-9]{3} [0-9]+$'
gecici=""

hata()  { printf '%s: hata: %s\n'  "$BETIK_ADI" "$*" >&2; }
uyari() { printf '%s: uyari: %s\n' "$BETIK_ADI" "$*" >&2; }
oldur() { hata "$1"; exit "${2:-1}"; }
bolum() { printf '\n== %s ==\n' "$*"; }

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
trap 'hata "$LINENO. satirda beklenmeyen hata"' ERR

ozet() {
  awk '
    { istek++; bayt += $10; sinif[substr($9, 1, 1)]++ }
    END {
      printf "  istek   : %d\n", istek
      printf "  bayt    : %d\n", bayt
      printf "  ortalama: %.1f bayt\n", (istek ? bayt / istek : 0)
      for (s = 2; s <= 5; s++)
        printf "  %sxx %4d  %%%.1f\n", s, sinif[s], (istek ? sinif[s] * 100 / istek : 0)
    }
  ' "$1"
}

en_cok_yol() {
  cut -d' ' -f7 "$1" \
    | sed -E 's#/[0-9]+#/:id#g' \
    | sort | uniq -c | sort -k1,1nr -k2,2 | head -"$2"
}

hata_yollari() {
  awk '$9 ~ /^[45]/ { n[$7]++ } END { for (y in n) printf "%3d %s\n", n[y], y }' "$1" \
    | sort -k1,1nr -k2,2
}

adet=5
while getopts ':n:' secenek; do
  case "$secenek" in
    n)  adet="$OPTARG" ;;
    :)  oldur "-$OPTARG deger bekliyor" 2 ;;
    \?) oldur "bilinmeyen secenek -$OPTARG" 2 ;;
  esac
done
shift $((OPTIND - 1))

kutuk="${1:-erisim.log}"
[[ "$adet" =~ ^[1-9][0-9]*$ ]] || oldur "-n icin pozitif tam sayi bekleniyor: $adet" 2
[ -f "$kutuk" ] || oldur "duz dosya degil ya da yok: $kutuk" 3
[ -r "$kutuk" ] || oldur "okuma izni yok: $kutuk" 3
[ -s "$kutuk" ] || uyari "kutuk bos: $kutuk"

gecici=$(mktemp -d)
gecerli="$gecici/gecerli.log"
grep -E "$SATIR_KALIBI" "$kutuk" | awk 'NF == 10' > "$gecerli" || true
bozuk=$(( $(grep -c '' "$kutuk") - $(grep -c '' "$gecerli") ))
[ "$bozuk" -eq 0 ] || uyari "$bozuk satir beklenen bicimde degil, cozumleme disi"

bolum "Kutuk: $kutuk"
ozet "$gecerli"

bolum "En cok istenen $adet yol"
en_cok_yol "$gecerli" "$adet"

bolum "Hata donduren yollar"
hata_yollari "$gecerli"
```

Betiğin son eklentisi, çözümlemeden önce geçerli satırları ayıran adımdır. Düzenli
İfadeler dersindeki satır kalıbı ile Alan Tabanlı İşleme dersindeki alan sayısı sınaması
birlikte uygulanıyor: ikisi de tek başına yetersizdi. Elenen satırlar sayılıp standart
hataya bildiriliyor, sayımlar ise yalnızca geçerli satırlar üzerinden yapılıyor — böylece
bozuk bir girdi raporu sessizce çarpıtmıyor.

Temiz kütükle:

```sh
./rapor.sh -n 3
```

```

== Kutuk: erisim.log ==
  istek   : 30
  bayt    : 117780
  ortalama: 3926.0 bayt
  2xx   18  %60.0
  3xx    3  %10.0
  4xx    6  %20.0
  5xx    3  %10.0

== En cok istenen 3 yol ==
   6 /api/veri
   6 /urun/:id
   4 /index.html

== Hata donduren yollar ==
  4 /yok.html
  3 /api/veri
  2 /gizli
```

İki bozuk satır eklenmiş bir kütükle:

```sh
./rapor.sh -n 2 tumu.log
```

```
rapor.sh: uyari: 2 satir beklenen bicimde degil, cozumleme disi

== Kutuk: tumu.log ==
  istek   : 30
  bayt    : 117780
  ortalama: 3926.0 bayt
  2xx   18  %60.0
...
```

Uyarı standart hataya, rapor standart çıktıya gitti ve sayımlar bozuk satırlardan
etkilenmedi.

Betiğin bilinen bir davranışı vardır: çıktısı `head` gibi erken kapanan bir komuta
bağlandığında, `pipefail` etkin olduğu için `ERR` kancası `SIGPIPE` nedeniyle tetiklenir.
Bu, katı kipin bilinçli bir ödünüdür; çıktıyı kısaltmak `-n` seçeneğiyle yapılmalıdır.

## Özet

- Süreli görev tanımı beş zaman alanı ve bir komuttan oluşur; ayın günü ile haftanın günü
  birlikte verildiğinde koşullar "veya" ile birleşir.
- Zamanlayıcı, komutu oturum açma kabuğu olmadan çalıştırır; arama yolu dardır, başlangıç
  dosyaları okunmaz ve ortam değişkenlerinin çoğu tanımsızdır.
- Betikte arama yolu ve yerel ayar sabitlenir, tüm dosya yolları saltık yazılır.
- Çıktı açıkça yönetilir: rapor ve tanı iletileri ayrı dosyalara eklenir.
- Sarmalayıcı betik ortamı kurar, kilidi alır ve çıktıyı yönlendirir; kilit üst üste binen
  çalışmaları engeller.
- Çıkış kodları izleme sisteminin tek girdisidir; kullanım, girdi ve geçici hata sınıfları
  ayrı kodlarla bildirilir.

## Kurs Kapanışı

Bu kurs tek bir soruyla başladı: bir komut yazdığı veriyi tam olarak nereye yazar? Yanıtı
üç akış ve dosya tanıtıcısıydı. Oradan çıkan her şey, aynı fikrin genişletilmesidir.

**Akışlar ve boru hatları** komutları birbirine bağlamayı kurdu: yönlendirmenin komuttan
önce uygulanması, boru hattındaki süreçlerin eşzamanlı çalışması, alt kabukta kaybolan
değişkenler ve çıkış kodu sözleşmesi.

**Betik yapısı** tek satırlık hatları bir programa dönüştürdü. Bu bölümün merkezinde,
kabuğu diğer dillerden ayıran şey duruyordu: genişletmenin sonucu yeniden çözümlenir.
Kelime bölme, yol adı genişletmesi ve alıntılama kuralları bu kursun akademik çekirdeğidir;
kabuk betiklerindeki hataların çoğu buradan doğar.

**Metin işleme** işi asıl araçlarına devretti: kalıp dili, satır süzme, akış editörü, alan
tabanlı işleme ve sıralama ailesi. Bölümü kapatan seçim ölçütü — en dar aracı seç, üç
araçtan fazlasını gerektiren problemde aracı değiştir — bu kursun pratik mirasıdır.

**Sağlam betik yazımı** betiği gözlemsiz çalışmaya hazırladı: katı kip, temizlik kancaları,
izleme araçları, statik çözümleme ve zamanlanmış çalışma.

Kurs boyunca aynı betik geliştirildi. `rapor.sh` sabit bir boru hattı olarak başladı;
değişken kazandı, argüman arayüzü edindi, girdilerini sınamayı öğrendi, fonksiyonlara
ayrıldı, sayımlarını tek geçişe indirdi, hatalarda durmayı ve arkasını toplamayı öğrendi.
Son hâli, denetimden geçen ve zamanlayıcıya verilebilen bir araçtır.

Kabuğun sınırı da bu yolculukta göründü. Kabuk, süreçleri birbirine bağlamakta ve dosya
sistemiyle çalışmakta rakipsizdir. Karmaşık veri yapıları, ondalıklı hesap, yapılandırılmış
veri biçimleri ve kapsamlı hata yönetimi için tasarlanmamıştır. İyi bir kabuk betiği,
kendi sınırını bilen betiktir.

Sonraki kurs — **Sistem Yönetimi** — bu betiğin çalıştığı sistemi konu alır. `rapor.sh`
bir zamanlayıcıya verildi; peki o zamanlayıcının kendisi bir servis olarak nasıl tanımlanır
ve denetlenir? Betik bir süreç yarattı; süreçler nasıl izlenir, önceliklendirilir ve
sinyallerle yönetilir? Kütük dosyaları büyüyor; disk ve dosya sistemi yapılandırması nasıl
değiştirilir? Bu kursta sinyaller, çıkış kodları ve süreçler bir betiğin içinden görüldü.
Sonraki kurs onlara sistemin dışından bakar.
