Bash scriptleri yazarken en çok ihtiyaç duyduğumuz şey, belirli koşullara göre farklı davranışlar sergileyebilmek. Bir dosyanın var olup olmadığını kontrol etmek, bir servisin çalışıp çalışmadığını anlamak, kullanıcıdan gelen girdiyi değerlendirmek… Bunların hepsinin temelinde koşullu ifadeler yatıyor. if, elif ve case yapılarını doğru kullanmak, yazdığın scriptleri gerçek anlamda işe yarar hale getirir. Bu yazıda bu üç yapıyı tüm detaylarıyla, gerçek dünya örnekleriyle inceleyeceğiz.
if Yapısının Temelleri
if yapısı, bash’in en temel karar mekanizması. Sözdizimi oldukça basit ama ince noktaları var:
if [ koşul ]; then
# koşul doğruysa burası çalışır
fi
Burada dikkat edilmesi gereken birkaç şey var. Köşeli parantezlerin içinde boşluk bırakmak zorunlu. [koşul] yazmak hata verir, [ koşul ] şeklinde yazman gerekiyor. then kelimesi ya aynı satırda noktalı virgülle ayrılarak ya da bir alt satırda yazılabilir.
Basit bir örnek görelim:
#!/bin/bash
DISK_KULLANIM=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_KULLANIM" -gt 80 ]; then
echo "UYARI: Disk kullanimi %$DISK_KULLANIM seviyesinde!"
echo "Temizlik yapilmasi gerekiyor."
fi
Bu script kök dizinin disk kullanımını kontrol ediyor ve %80’i geçmişse uyarı veriyor. Gerçek bir monitoring scriptinin çekirdeği bu kadar basit.
Karşılaştırma Operatörleri
Sayısal karşılaştırmalarda kullanılan operatörler:
- -eq: Eşit (equal)
- -ne: Eşit değil (not equal)
- -gt: Büyüktür (greater than)
- -lt: Küçüktür (less than)
- -ge: Büyük eşit (greater or equal)
- -le: Küçük eşit (less or equal)
String karşılaştırmalarda:
- =: Eşit
- !=: Eşit değil
- -z: String boş mu?
- -n: String dolu mu?
Dosya kontrolleri:
- -f: Düzenli dosya mı?
- -d: Dizin mi?
- -e: Var mı?
- -r: Okunabilir mi?
- -w: Yazılabilir mi?
- -x: Çalıştırılabilir mi?
- -s: Boyutu sıfırdan büyük mü?
elif ile Çoklu Koşullar
Tek bir if çoğu zaman yeterli gelmiyor. Birden fazla koşulu değerlendirmen gerektiğinde elif devreye giriyor:
#!/bin/bash
SERVIS="nginx"
DURUM=$(systemctl is-active "$SERVIS" 2>/dev/null)
if [ "$DURUM" = "active" ]; then
echo "$SERVIS servisi calisiyor."
elif [ "$DURUM" = "inactive" ]; then
echo "$SERVIS servisi durmus. Baslatiliyor..."
systemctl start "$SERVIS"
elif [ "$DURUM" = "failed" ]; then
echo "KRITIK: $SERVIS servisi basarisiz durumda!"
echo "Log kontrol ediliyor..."
journalctl -u "$SERVIS" --no-pager -n 20
else
echo "$SERVIS servisi bulunamadi veya durum bilinmiyor."
fi
Bu yapıda else bloğu isteğe bağlı ama kesinlikle yazmanı öneririm. Beklenmedik durumları yakalamak için çok değerli.
Çift Köşeli Parantez: [[ ]]
Modern bash scripting’de [[ ]] kullanımı [ ]‘ye göre çok daha güvenli ve güçlü. Neden?
- String karşılaştırmalarında tırnak işareti zorunlu değil
&&ve||operatörlerini doğrudan kullanabilirsin- Regex desteği var (
=~operatörü ile) - Boş değişkenler hata vermez
#!/bin/bash
HOSTNAME=$(hostname)
IP_ADRES=$(hostname -I | awk '{print $1}')
if [[ "$HOSTNAME" =~ ^prod- ]]; then
echo "Bu bir production sunucu: $HOSTNAME"
echo "IP: $IP_ADRES"
echo "Dikkatli olun!"
elif [[ "$HOSTNAME" =~ ^test- || "$HOSTNAME" =~ ^dev- ]]; then
echo "Bu bir test/dev sunucu: $HOSTNAME"
echo "Rahatca deney yapabilirsiniz."
else
echo "Sunucu isimlendirmesi standart disinda: $HOSTNAME"
fi
Bu örnekte =~ operatörü ile regex kullanıyoruz. Sunucu adının “prod-” ile başlayıp başlamadığını kontrol etmek, özellikle birden fazla ortamı yöneten sysadmin’ler için hayat kurtarıcı.
Mantıksal Operatörler ile Koşulları Birleştirmek
Birden fazla koşulu aynı satırda değerlendirmek için && (ve) ile || (veya) kullanılır:
#!/bin/bash
KULLANICI="deploy"
HEDEF_DIZIN="/var/www/myapp"
if [[ -d "$HEDEF_DIZIN" && -w "$HEDEF_DIZIN" ]]; then
echo "Dizin mevcut ve yazilabilir. Deploy basliyor..."
elif [[ -d "$HEDEF_DIZIN" && ! -w "$HEDEF_DIZIN" ]]; then
echo "HATA: Dizin var ama yazma izniniz yok!"
echo "Dizin sahibi: $(stat -c '%U' $HEDEF_DIZIN)"
exit 1
else
echo "Hedef dizin bulunamadi. Olusturuluyor..."
mkdir -p "$HEDEF_DIZIN"
chown "$KULLANICI":"$KULLANICI" "$HEDEF_DIZIN"
fi
! operatörü koşulu tersine çeviriyor. -w "$HEDEF_DIZIN" “dizin yazılabilir mi?” soruyorken, ! -w "$HEDEF_DIZIN" “dizin yazılamaz mı?” soruyor.
case Yapısı: Çok Seçenekli Kararlar
case yapısı, özellikle bir değişkenin birden fazla olası değerine karşı işlem yapman gerektiğinde if-elif zincirine göre çok daha okunabilir bir alternatif sunuyor. Sözdizimi:
case "$degisken" in
deger1)
# işlemler
;;
deger2)
# işlemler
;;
*)
# hiçbiri değilse
;;
esac
;; her case bloğunu sonlandırır. *) ise “wildcard” yani hiçbir koşul eşleşmediğinde çalışan blok.
Klasik bir menü scripti örneği:
#!/bin/bash
echo "=== Sunucu Yonetim Menusu ==="
echo "1) Servisleri listele"
echo "2) Disk kullanimi goster"
echo "3) Aktif baglantilari goster"
echo "4) Cikis"
echo ""
read -p "Seciminizi yapin (1-4): " SECIM
case "$SECIM" in
1)
echo "--- Aktif Servisler ---"
systemctl list-units --type=service --state=active --no-pager
;;
2)
echo "--- Disk Kullanimi ---"
df -hT
;;
3)
echo "--- Aktif Baglantilar ---"
ss -tuln
;;
4)
echo "Cikis yapiliyor..."
exit 0
;;
*)
echo "Gecersiz secim: $SECIM"
echo "Lutfen 1-4 arasinda bir deger girin."
exit 1
;;
esac
case ile Pattern Matching
case yapısının en güçlü özelliklerinden biri wildcard pattern desteği. Glob pattern’leri kullanabilirsin:
#!/bin/bash
DOSYA_ADI="$1"
if [[ -z "$DOSYA_ADI" ]]; then
echo "Kullanim: $0 <dosya_adi>"
exit 1
fi
case "$DOSYA_ADI" in
*.tar.gz|*.tgz)
echo "Gzip ile sikistirilmis tar arsivi"
tar -xzf "$DOSYA_ADI"
;;
*.tar.bz2|*.tbz2)
echo "Bzip2 ile sikistirilmis tar arsivi"
tar -xjf "$DOSYA_ADI"
;;
*.tar.xz)
echo "XZ ile sikistirilmis tar arsivi"
tar -xJf "$DOSYA_ADI"
;;
*.zip)
echo "ZIP arsivi"
unzip "$DOSYA_ADI"
;;
*.gz)
echo "Gzip dosyasi"
gunzip "$DOSYA_ADI"
;;
*.sh)
echo "Shell script dosyasi. Calistiriliyor..."
bash "$DOSYA_ADI"
;;
*)
echo "Bilinmeyen dosya formati: $DOSYA_ADI"
exit 1
;;
esac
Bu tür bir araç arşiv formatlarını otomatik tanıyıp uygun komutu çalıştırıyor. Günlük sysadmin işlerinde bu kadar pratik bir script çok zaman kazandırıyor.
Birden Fazla Değeri Aynı Blokta Ele Almak
| (pipe) karakteri ile birden fazla değeri aynı case bloğuna yönlendirebilirsin:
#!/bin/bash
read -p "Devam etmek istiyor musunuz? (evet/hayir): " CEVAP
case "${CEVAP,,}" in
evet|e|yes|y|1)
echo "Islem basliyor..."
;;
hayir|h|no|n|0)
echo "Islem iptal edildi."
exit 0
;;
*)
echo "Anlasilmayan cevap. Cikiluyor."
exit 1
;;
esac
Burada ${CEVAP,,} kullanıyoruz. Bu bash’in parametre genişletme özelliği, değişkeni küçük harfe çeviriyor. Bu sayede “EVET”, “Evet”, “evet” hepsini aynı şekilde yakalıyoruz.
Gerçek Dünya Senaryosu: Yedekleme Scripti
Şimdiye kadar öğrendiklerimizi bir araya getirelim. Hem if-elif hem de case yapısını kullanan gerçekçi bir yedekleme scripti:
#!/bin/bash
# Yedekleme scripti - /etc/backup.conf dosyasina gore calisir
# Kullanim: ./backup.sh [full|incremental|verify]
LOG_DOSYASI="/var/log/backup.log"
YEDEK_DIZIN="/backup"
TARIH=$(date +%Y%m%d_%H%M%S)
MOD="${1:-full}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DOSYASI"
}
# Yedek dizini kontrolu
if [[ ! -d "$YEDEK_DIZIN" ]]; then
log "HATA: Yedek dizini bulunamadi: $YEDEK_DIZIN"
exit 1
elif [[ ! -w "$YEDEK_DIZIN" ]]; then
log "HATA: Yedek dizinine yazma izni yok"
exit 1
fi
# Disk alanı kontrolu
BOSTA_ALAN=$(df "$YEDEK_DIZIN" | tail -1 | awk '{print $4}')
if [[ "$BOSTA_ALAN" -lt 5242880 ]]; then
log "UYARI: Yedek dizininde yetersiz alan (min 5GB gerekli)"
exit 1
fi
case "$MOD" in
full)
log "Tam yedekleme basliyor..."
tar -czf "$YEDEK_DIZIN/full_$TARIH.tar.gz"
/etc /home /var/www 2>>"$LOG_DOSYASI"
if [[ $? -eq 0 ]]; then
log "Tam yedekleme tamamlandi: full_$TARIH.tar.gz"
else
log "HATA: Tam yedekleme basarisiz!"
exit 1
fi
;;
incremental)
log "Artimli yedekleme basliyor..."
ONCEKI_YEDEK=$(ls -t "$YEDEK_DIZIN"/full_*.tar.gz 2>/dev/null | head -1)
if [[ -z "$ONCEKI_YEDEK" ]]; then
log "Onceki tam yedek bulunamadi. Once full yedek alinmali."
exit 1
fi
tar -czf "$YEDEK_DIZIN/inc_$TARIH.tar.gz"
--newer "$ONCEKI_YEDEK" /etc /home /var/www 2>>"$LOG_DOSYASI"
log "Artimli yedekleme tamamlandi"
;;
verify)
log "Yedek dogrulama basliyor..."
SON_YEDEK=$(ls -t "$YEDEK_DIZIN"/*.tar.gz 2>/dev/null | head -1)
if [[ -z "$SON_YEDEK" ]]; then
log "Dogrulanacak yedek bulunamadi"
exit 1
fi
tar -tzf "$SON_YEDEK" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
log "Yedek dosyasi gecerli: $SON_YEDEK"
else
log "HATA: Yedek dosyasi bozuk: $SON_YEDEK"
exit 1
fi
;;
*)
log "Gecersiz mod: $MOD"
echo "Kullanim: $0 [full|incremental|verify]"
exit 1
;;
esac
log "Islem basariyla tamamlandi."
exit 0
Bu script birkaç önemli sysadmin pratiğini bir arada gösteriyor: Önce gereksinimleri doğrula, sonra işlemi yap. Çıkış kodlarını ($?) kontrol et. Her şeyi logla.
Çıkış Kodları ve Koşullu İfadeler
Bash’te her komut bir çıkış kodu döndürür. 0 başarıyı, diğer değerler hatayı temsil eder. Bu çıkış kodlarını if ile doğrudan kullanabilirsin:
#!/bin/bash
HEDEF_HOST="$1"
if ping -c 1 -W 2 "$HEDEF_HOST" &>/dev/null; then
echo "$HEDEF_HOST erişilebilir durumda"
else
echo "$HEDEF_HOST erişilemiyor!"
fi
# SSH bağlantısı kontrolu
if ssh -o ConnectTimeout=5 -o BatchMode=yes "$HEDEF_HOST" "exit" 2>/dev/null; then
echo "SSH bağlantısı başarılı"
else
echo "SSH bağlantısı kurulamadı"
fi
Burada ping ve ssh komutlarının çıkış kodlarını doğrudan if koşulu olarak kullanıyoruz. Bu yaklaşım çok temiz ve okunabilir.
Yaygın Hatalar ve Dikkat Edilmesi Gerekenler
Değişkenleri tırnak içinde kullanmak: "$DEGISKEN" şeklinde yazmak, değişken boş olduğunda ya da boşluk içerdiğinde beklenmedik hataları önler.
-eq ile = karıştırmak: Sayısal karşılaştırmada -eq, string karşılaştırmada = kullan. [ "10" -eq "10" ] doğru çalışır ama [ "abc" -eq "abc" ] hata verir.
exit kodunu doğru kullanmak: Scriptin başarıyla tamamlandığında exit 0, hata durumunda exit 1 (ya da farklı hata kodları) döndürmeyi unutma. Bu, scripti cron’a ya da başka bir script içine gömdüğünde hayati önem taşıyor.
case’de ;; unutmak: Her blok ;; ile bitmeli. Unutursan bir sonraki bloğa “fall-through” olur. Bash 4.0+ ile ;& kullanarak kasıtlı fall-through yapabilirsin ama çoğu durumda istemezsin.
[ ] yerine [[ ]] tercih etmek: Özellikle string karşılaştırmalarında [[ ]] çok daha güvenli. Eski POSIX uyumluluğu gerekmiyorsa [[ ]] kullan.
Sonuç
if, elif ve case yapıları bash scripting’in bel kemiği. Bu üç yapıyı doğru kullanmayı öğrendiğinde, yüzlerce tekrarlayan işi otomatize edebilir, sunucu kontrollerini scriptlerle yönetebilir, deploy süreçlerini güvenli hale getirebilirsin.
Pratik bir öneri: Yazdığın her koşullu ifadede kendinize şunu sorun, “bu değişken boş olursa ne olur?” ve “bu komut hata verirse ne olur?” Bu iki soruyu cevaplandırarak yazdığın script, production ortamında çok daha sağlam çalışır.
case yapısını çok seçenekli menülerde ve pattern matching gereken yerlerde, if-elif yapısını ise karmaşık mantıksal koşullar için kullanmak en iyi pratik. İkisini bir arada kullanmaktan çekinme, birbirini güzel tamamlıyorlar.
Son olarak: Her scripte #!/bin/bash shebang’ını ve set -e (hata durumunda dur) ile set -u (tanımsız değişkende hata ver) bayraklarını eklemeyi alışkanlık haline getir. Bu küçük detaylar, uzun vadede debugging saatlerinden kurtarır.