İçindekiler
Terminalde kusursuz çalışan bir script’in cron tarafından başlatıldığında hata vermesi, Linux dünyasının klasik “ama benim makinemde çalışıyordu” vakalarındandır. Suç çoğu zaman cron’da değil; interaktif shell ile cron job’ının farklı PATH, çalışma dizini ve ortam değişkenleri kullanmasındadır.
``
Aynı kullanıcı, neden farklı sonuç?
Terminal açtığımızda Bash genellikle .bashrc, .profile veya .bash_profile gibi başlangıç dosyalarını okuyabilir. Bu dosyalar PATH, dil ayarları, alias’lar ve uygulamalara özel değişkenler tanımlar. Cron ise öngörülebilir olmak amacıyla oldukça sade bir ortam oluşturur ve interaktif shell yapılandırmasını kendiliğinden yüklemez.
| Özellik | İnteraktif shell | Cron ortamı |
|---|---|---|
PATH |
Genellikle genişletilmiş | Kısa ve sistem odaklı |
| Başlangıç dosyaları | Çoğunlukla okunur | Genellikle okunmaz |
| Çalışma dizini | Kullanıcının seçtiği dizin | Çoğu zaman kullanıcının ev dizini |
| Alias ve fonksiyonlar | Kullanılabilir | Genellikle tanımsız |
| TTY | Vardır | Yoktur |
| Dil değişkenleri | Kullanıcı ayarlarından gelir | Eksik veya farklı olabilir |
Shell bir komutun içinde / görmezse çalıştırılabilir dosyayı PATH dizinlerinde sırayla arar. Bunu basitçe şöyle düşünebiliriz:
Örneğin terminalinizde /usr/local/bin mevcutken cron’un PATH değerinde bu dizin yoksa node, python, docker veya özel bir CLI bulunamayabilir. Sonuç genellikle command not found olur; çıktı yönlendirilmediyse hatayı görmeyebilirsiniz bile.
Güvenilir çözüm: ortamı açıkça tanımlamak
Crontab içinde ihtiyaç duyulan shell ve PATH değerlerini belirtmek ilk savunma hattıdır:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
*/10 * * * * /home/ada/scripts/rapor.sh >> /home/ada/logs/rapor.log 2>&1
Buradaki 2>&1, standart hata akışını standart çıktıyla aynı log dosyasına gönderir. Böylece cron’un sessizliğini bozup gerçek hatayı yakalayabilirsiniz. Ancak SHELL=/bin/bash yazmak Bash’i otomatik olarak login veya interaktif shell yapmaz; .bashrc dosyasının okunacağı anlamına gelmez.
Script içinde kritik komutlar için mutlak yollar kullanmak daha da sağlamdır:
#!/usr/bin/env bash
set -euo pipefail
DATE=/usr/bin/date
PG_DUMP=/usr/bin/pg_dump
GZIP=/usr/bin/gzip
backup="/var/backups/app-$($DATE +%F).sql.gz"
$PG_DUMP appdb \vert $GZIP > "$backup"
Bu script, hangi programların çalışacağını belirsiz PATH aramasına bırakmaz. set -euo pipefail ise başarısız komutları, tanımsız değişkenleri ve boru hattı hatalarını görünür kılar.
Göreli yolların gizli tehlikesi
Script’in ./config.json dosyasını okuması, cron’un doğru dizinde başladığını garanti etmez. Önce script dizinine geçmek güvenlidir:
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
/usr/bin/python3 generate_report.py --config ./config.json
Böylece script nereden çağrılırsa çağrılsın dosya yolları tutarlı kalır.
Ortamı karşılaştırarak hata ayıklama
Cron’un gördüğü ortamı dosyaya yazdırabilirsiniz:
* * * * * /usr/bin/env | /usr/bin/sort > /tmp/cron-env.txt 2>&1
Terminalde de env | sort > /tmp/shell-env.txt çalıştırıp iki dosyayı diff ile karşılaştırın. Ayrıca command -v python3, command -v node gibi komutlarla gerçek çalıştırılabilir yolları bulun.
.bashrc dosyasını zorla yüklemek kısa vadede işe yarayabilir, fakat interaktif terminale özel komutlar cron altında yeni sorunlar çıkarabilir. En iyi yaklaşım; bağımlılıkları, yolları, çalışma dizinini ve loglamayı açıkça tanımlayan, kendi ortamını bilen bağımsız script’ler yazmaktır. Cron sürpriz yapmaz; yalnızca bizim terminalde fark etmeden taşıdığımız görünmez bagajı yanında getirmez.
Yorumlar