Buyukweb
Linux'ta Bash Script Yazma: Temel Otomasyon Rehberi

Linux'ta Bash Script Yazma: Temel Otomasyon Rehberi

Bash script ile Linux görevlerini nasıl otomatize edersiniz? Değişkenler, koşullar, döngüler ve fonksiyonlar ile temel bash scripting öğrenin.

Büyükweb Editör EkibiHosting, Sunucu ve Sistem Yönetimi Editörü11 dakika okuma

Linux'ta Bash Script Yazma: Sunucu Yönetimi için Pratik Otomasyon Rehberi (2026)

Bash script, Linux sunucusunda tekrar eden işleri otomatize etmenin en doğal yoludur: yedekleme, log temizliği, disk kullanımı uyarısı, servis monitoring, deploy script'leri — hepsi 50-150 satırlık bash kodu ile çözülür. Python veya Go gerektirmez; başka kütüphane gerektirmez; her Linux dağıtımında çalışır. Bu rehberde Buyukweb VDS müşterilerimiz için bash'i sıfırdan değil, gerçek sunucu senaryolarına yönelik anlatıyoruz: variable, döngü, hata yönetimi, cron entegrasyonu, gerçek-dünya örnekleri.

Buyukweb perspektifi: VDS'de root yetkiniz var; bash script'leriniz /usr/local/bin/ veya ~/bin/ altına koyulup cron ile zamanlanır. Buyukweb ekibi sunuculara müdahale ederken çoğunlukla bash script'i tercih eder — Ansible/Puppet karmaşıklığına gerek yok küçük-orta sunucuda. Tercih etmeyin: internetten kopyalanan "30 saniyede sunucu kuran" mega script'leri körlemesine curl | bash ile çalıştırmayı; her satırı anlayarak kopyalayın. Senaryoda hata olursa kritik veri kaybedebilirsiniz. Önce dry-run, sonra apply alışkanlığı kurun.

İlk Bash Script — 5 Dakikada Çalışır Hale

# 1. Dosya oluştur
nano disk-rapor.sh

İçeriği:

#!/bin/bash
# Disk kullanım raporu — basit örnek

echo "=========================================="
echo "  Sunucu Disk Raporu - $(date +'%F %H:%M')"
echo "=========================================="
echo ""
echo "Hostname: $(hostname)"
echo "Uptime:   $(uptime -p)"
echo ""
echo "Disk kullanımı:"
df -h | grep -v tmpfs
echo ""
echo "En büyük 5 dizin (/home altı):"
du -sh /home/*/ 2>/dev/null | sort -rh | head -5
# 2. Çalıştırma izni ver
chmod +x disk-rapor.sh

# 3. Çalıştır
./disk-rapor.sh

Shebang Satırı

#!/bin/bash             # Doğrudan /bin/bash
#!/usr/bin/env bash     # PATH'tan bash bul (taşınabilir)
#!/bin/sh               # POSIX shell, daha sınırlı ama her sistemde var

Tavsiye: /usr/bin/env bash taşınabilirlik için en güvenli; macOS, BSD, Linux her yerde çalışır. /bin/sh ise dash veya ash olabilir; bash'a özgü özellikler (array, [[ ]]) çalışmaz.

Değişkenler

# Atama (eşittir etrafında BOŞLUK YOK)
ISIM="Buyukweb"
YIL=2026
DIZIN=/var/www
TARIH=$(date +%Y%m%d)         # komut çıktısını değişkene al

# Kullanım
echo "Şirket: $ISIM"
echo "Yıl: ${YIL}"             # küme parantezi ile sınırı belirt
echo "Bugün: $TARIH"
echo "Dizin: $DIZIN/site/"

# Read-only (sabit)
readonly MAX_RETRY=3

# Default değer (parameter expansion)
LOG_DIR="${LOG_DIR:-/var/log/myapp}"   # eğer set edilmemişse default

# Komut satırı argümanları
echo "Script adı: $0"
echo "İlk argüman: $1"
echo "Tüm argümanlar: $@"
echo "Argüman sayısı: $#"

Önemli: Değişkenleri çift tırnak içinde kullanın ("$VAR" şeklinde). Boşluklu dosya adlarında veya glob expansion'a karşı koruma sağlar. Tek tırnak değişken expand etmez ('$VAR' literal yazılır).

Koşullar

# if-else
if [ -f "/etc/hostname" ]; then
  echo "Dosya mevcut"
elif [ -d "/etc" ]; then
  echo "Dizin var ama dosya yok"
else
  echo "Hiçbiri yok"
fi

# Daha modern: [[ ]] (bash-only, POSIX değil)
if [[ "$ISIM" == "Buyukweb" && $YIL -ge 2026 ]]; then
  echo "Doğru"
fi

# case
case "$1" in
  start)   echo "Başlatılıyor"; ;;
  stop)    echo "Durduruluyor"; ;;
  restart) echo "Yeniden başlatılıyor"; ;;
  *)       echo "Kullanım: $0 {start|stop|restart}"; exit 1 ;;
esac

Yaygın Test Operatörleri

Test Anlam
-f FILE dosya mı?
-d DIR dizin mi?
-e PATH varlığı (dosya/dizin/link)
-r FILE okunabilir mi?
-w FILE yazılabilir mi?
-x FILE çalıştırılabilir mi?
-z STR string boş mu?
-n STR string boş değil mi?
STR1 == STR2 eşit
NUM1 -eq NUM2 sayısal eşit
NUM1 -gt NUM2 büyük
NUM1 -lt NUM2 küçük

Döngüler

# for - liste üzerinde
for SERVIS in nginx mariadb php-fpm; do
  systemctl status "$SERVIS" --no-pager
done

# for - C-style
for ((i=1; i<=10; i++)); do
  echo "İterasyon $i"
done

# for - dosyalar üzerinde
for LOG in /var/log/*.log; do
  echo "Log: $LOG ($(stat -c %s "$LOG") byte)"
done

# while - koşul tabanlı
SAYAC=0
while [ $SAYAC -lt 5 ]; do
  echo "Sayaç: $SAYAC"
  ((SAYAC++))
done

# while - dosyayı satır satır oku
while IFS= read -r SATIR; do
  echo "Satır: $SATIR"
done < /etc/hostname

# until - while'ın tersi (false olduğu sürece)
until ping -c 1 8.8.8.8 &>/dev/null; do
  echo "Network bekliyor..."
  sleep 2
done

Fonksiyonlar

# Tanımlama
log_mesaj() {
  local LEVEL=$1
  local MESAJ=$2
  local TARIH=$(date +'%F %T')
  echo "[$TARIH] [$LEVEL] $MESAJ" | tee -a /var/log/script.log
}

# Çağırma
log_mesaj "INFO" "Script başlatıldı"
log_mesaj "ERROR" "Veritabanı bağlantısı başarısız"

# Değer döndüren fonksiyon
disk_yuzdesi() {
  local YOL=${1:-/}
  df "$YOL" | tail -1 | awk '{print $5}' | tr -d '%'
}

YUZDE=$(disk_yuzdesi /home)
echo "Disk yüzdesi: %$YUZDE"

# Exit code ile başarı/başarısızlık
servis_calisir_mi() {
  systemctl is-active "$1" &>/dev/null
}

if servis_calisir_mi "nginx"; then
  echo "Nginx çalışıyor"
else
  echo "Nginx kapalı"
fi

Local değişken kullanın: Fonksiyon içinde local VAR=... ile tanımlamazsanız global olur ve dış değişkenleri kirletir.

Hata Yönetimi (set Bayrakları)

Production script'inin başına şu satırları mutlaka ekleyin:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

Açıklama:

  • set -e — herhangi bir komut hata verirse script dur (exit code != 0)
  • set -u — tanımsız değişken kullanılırsa hata
  • set -o pipefail — pipe içindeki herhangi komut başarısızsa pipeline başarısız sayılsın
  • IFS=$'\n\t' — alan ayırıcıyı yeni-satır ve tab yap (boşluklu dosya adı sorununu önler)
# Manuel hata kontrolü
mkdir -p /backup || { echo "ERROR: Backup dizini oluşturulamadı"; exit 1; }

# Cleanup için trap
TEMP_DIR=$(mktemp -d)
cleanup() {
  echo "Temizlik yapılıyor..."
  rm -rf "$TEMP_DIR"
}
trap cleanup EXIT INT TERM

Pratik Script: Otomatik Veritabanı + Dosya Yedekleme

#!/bin/bash
# /usr/local/bin/site-yedek.sh
set -euo pipefail

# Konfigürasyon
DB_USER="backup_user"
DB_PASS="GUVENLI_SIFRE"
DB_NAME="wp_buyukweb"
SITE_DIR="/var/www/buyukweb.com"
YEDEK_DIR="/backup/site"
TARIH=$(date +'%Y%m%d_%H%M%S')
GUNLER=14                                  # eski yedek saklama gün sayısı

# Logging
LOG="/var/log/site-yedek.log"
log() { echo "[$(date +'%F %T')] $*" | tee -a "$LOG"; }

# Yedek dizini
mkdir -p "$YEDEK_DIR"

log "Yedekleme başladı"

# Veritabanı yedeği
log "MySQL dump alınıyor..."
mysqldump --single-transaction --quick \
  -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" \
  | gzip > "$YEDEK_DIR/db-$TARIH.sql.gz"
log "DB yedek tamam: $(du -h "$YEDEK_DIR/db-$TARIH.sql.gz" | cut -f1)"

# Dosya yedeği
log "Dosya tar.gz alınıyor..."
tar -czf "$YEDEK_DIR/files-$TARIH.tar.gz" \
  -C "$(dirname "$SITE_DIR")" "$(basename "$SITE_DIR")"
log "Dosya yedek tamam: $(du -h "$YEDEK_DIR/files-$TARIH.tar.gz" | cut -f1)"

# Eski yedekleri temizle
log "$GUNLER günden eski yedekler siliniyor..."
find "$YEDEK_DIR" -name '*.gz' -mtime +"$GUNLER" -delete

# Toplam disk kullanımı
log "Toplam yedek boyutu: $(du -sh "$YEDEK_DIR" | cut -f1)"
log "Yedekleme tamam"

Cron'a ekle:

# crontab -e
0 3 * * * /usr/local/bin/site-yedek.sh

Pratik Script: Disk Kotası Uyarı (mail bildirim)

#!/bin/bash
set -euo pipefail

ESIK=85                                # %85 üzerinde uyarı
EMAIL="[email protected]"

DURUM=""
while read -r KULLANIM YOL; do
  YUZDE=${KULLANIM%\%}
  if [ "$YUZDE" -ge "$ESIK" ]; then
    DURUM+="UYARI: $YOL %$YUZDE dolu\n"
  fi
done < <(df -h --output=pcent,target | tail -n +2 | awk '$1 ~ /[0-9]+%/')

if [ -n "$DURUM" ]; then
  echo -e "$DURUM" | mail -s "Disk Uyarısı: $(hostname)" "$EMAIL"
fi

Pratik Script: Servis Sağlık Kontrolü ve Otomatik Yeniden Başlat

#!/bin/bash
set -euo pipefail

SERVISLER=("nginx" "mariadb" "php-fpm")
LOG="/var/log/servis-saglik.log"

log() { echo "[$(date +'%F %T')] $*" >> "$LOG"; }

for SERVIS in "${SERVISLER[@]}"; do
  if ! systemctl is-active --quiet "$SERVIS"; then
    log "$SERVIS DOWN — yeniden başlatılıyor"
    if systemctl restart "$SERVIS"; then
      log "$SERVIS başarıyla başlatıldı"
    else
      log "HATA: $SERVIS yeniden başlatılamadı, manuel müdahale gerekiyor"
      echo "$SERVIS yeniden başlatılamadı: $(hostname)" \
        | mail -s "ACIL: Servis Hatası" [email protected]
    fi
  fi
done

Cron 5 dakikada bir:

*/5 * * * * /usr/local/bin/servis-saglik.sh

Pratik Script: Log Rotasyonu

logrotate kullanmadan basit:

#!/bin/bash
set -euo pipefail

LOG_DIR="/var/log/myapp"
ESKI_GUN=30
SIKISTIR_GUN=7

# 7 günden eski log'ları sıkıştır
find "$LOG_DIR" -name "*.log" -type f -mtime +"$SIKISTIR_GUN" \
  ! -name "*.gz" -exec gzip {} \;

# 30 günden eski sıkıştırılmış log'ları sil
find "$LOG_DIR" -name "*.log.gz" -type f -mtime +"$ESKI_GUN" -delete

Pratik Script: Apache Access Log Analizi

#!/bin/bash
LOG_DOSYA="/var/log/nginx/access.log"

echo "=== En çok ziyaret edilen 10 URL ==="
awk '{print $7}' "$LOG_DOSYA" | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== En çok istek atan 10 IP ==="
awk '{print $1}' "$LOG_DOSYA" | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== HTTP durum kodu dağılımı ==="
awk '{print $9}' "$LOG_DOSYA" | sort | uniq -c | sort -rn

echo ""
echo "=== 404 alan URL'ler ==="
awk '$9 == 404 {print $7}' "$LOG_DOSYA" | sort | uniq -c | sort -rn | head -10

Best Practices (Kalıcı Alışkanlıklar)

1. shebang her zaman: #!/bin/bash veya #!/usr/bin/env bash

2. set -euo pipefail her production script'inde

3. Değişkenleri "$" ve çift tırnak içinde kullan: "${VAR}"

4. Komut substitution: $(...)  — eski `...` yerine kullan

5. Check exit code:
   if komut; then ... fi
   veya: komut || hata_isle

6. Hardcoded path yerine değişken:
   ISIM="/var/www/site"
   tar -C "$ISIM" .

7. Hassas veri ENV'den oku, script'te değil:
   DB_PASS="${DB_PASSWORD:?DB_PASSWORD env tanımlı olmalı}"

8. Logging: tüm önemli adımları log'a yaz
   log() { echo "[$(date +'%F %T')] $*" | tee -a "$LOG_FILE"; }

9. Cleanup için trap kullan:
   trap 'rm -f "$TMPFILE"' EXIT

10. shellcheck ile linting yap:
    sudo apt install shellcheck
    shellcheck script.sh

11. Script'lerin yedeği: git ile takip et, /usr/local/bin'e symlink

12. Deneme yapmadan production'da çalıştırma:
    bash -n script.sh   # syntax check
    bash -x script.sh   # debug, her satırı yazdır
    DRY_RUN=1 ./script.sh   # kendi flag'inle dry-run modu

Cron ile Zamanlama

# Cron formatı: dakika saat gün-ay ay haftanin-günü komut
# *    *    *    *    *
# 0-59 0-23 1-31 1-12 0-7

# Her dakika
* * * * * /usr/local/bin/health.sh

# Her 5 dakikada
*/5 * * * * /usr/local/bin/check.sh

# Her gün gece 3'te
0 3 * * * /usr/local/bin/backup.sh

# Pazartesi sabah 6'da
0 6 * * 1 /usr/local/bin/weekly-report.sh

# Ayın 1'inde gece yarısı
0 0 1 * * /usr/local/bin/monthly-cleanup.sh

# Crontab düzenle
crontab -e

# Mevcut cron'ları gör
crontab -l

# Cron log'u (ayarlanmışsa)
tail -f /var/log/cron       # AlmaLinux/Rocky
tail -f /var/log/syslog | grep CRON    # Ubuntu

Cron tuzakları: PATH cron ortamında minimumdur (/usr/bin:/bin). Script'iniz /usr/local/bin veya /opt/... altındaysa tam yol yazın. Veya script başına PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ekleyin.

Sık Sorulan Sorular

Buyukweb VDS'de bash script'lerimi nereye koyayım?

Sunucuda root olduğunuz için /usr/local/bin/ altına koyun (tüm kullanıcılar PATH'tan erişir). Sadece kişisel script'ler ~/bin/'de tutulabilir. Her zaman chmod +x script.sh ile çalıştırılabilir yapın.

Neden #!/bin/sh yerine #!/bin/bash kullanmalıyım?

/bin/sh farklı dağıtımlarda farklı shell olabilir (Debian'da dash, BusyBox'ta ash). Bash'a özgü array'ler, [[ ]], <(), parameter expansion özellikleri çalışmayabilir. Production ortamında bash kullanın veya gerçekten POSIX-uyumlu yazıp shellcheck -s sh script.sh ile doğrulayın.

shellcheck nedir, neden kullanmalıyım?

shellcheck bir bash linter — script'inizdeki yaygın hataları (tırnak unutmak, gereksiz cat kullanımı, race condition'lar) tespit eder. Buyukweb sunucularında apt install shellcheck veya dnf install ShellCheck ile kurabilirsiniz; CI'de de çalıştırılabilir.

Bash mı Python mı, hangisini ne zaman seçmeliyim?

  • Bash: sistem komutu zinciri (mysqldump, tar, find, sed), <500 satır, hızlı tek-amaçlı görev
  • Python: karmaşık veri işleme (JSON parsing, API çağrıları), yapısal kod, test gereksinimi

Dosya yedekleme için bash; Stripe API ile fatura kontrolü için Python.

Cron'da script çalışmıyor, debug nasıl?

# Script'in çıktısını log'a al
0 3 * * * /usr/local/bin/script.sh >> /var/log/script.log 2>&1

# Cron PATH eksik olabilir, scriptin ilk satırına ekle:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Cron çalıştığını test et
* * * * * /bin/echo "Cron çalıştı $(date)" >> /tmp/cron-test.log

Script'imde hassas veri (şifre) var, nasıl güvenli saklayayım?

Asla script içinde hardcode etmeyin. 3 yöntem:

  1. ENV variable: source /root/.env ile yükle
  2. Konfig dosyası: chmod 600 /etc/script.conf (sadece root okur)
  3. systemd EnvironmentFile: systemd unit ile kombinasyon
# /etc/script.conf
DB_PASS="GuvenliSifre123!"

# Script'te
source /etc/script.conf
mysqldump -u user -p"$DB_PASS" db > yedek.sql

Bash'in alternatifi var mı (zsh, fish)?

Production sunucularda bash standart. zsh, fish daha kullanıcı dostu ama:

  • Sistem script'leri (cron, init) bash bekler
  • Standart Linux paketleri bash'ta gelir
  • shellcheck ve diğer araçlar bash optimize

Kullanıcı shell'iniz olarak zsh seçebilirsiniz, ama script'leri bash'ta yazın.

set -e her zaman doğru mu?

Çoğu zaman evet. Ama bazı durumlarda istenmeyen davranışa yol açabilir:

set -e
GREP_SONUC=$(grep "x" dosya || true)   # || true ekle ki grep boş bulunca dur etmesin

İncelikli senaryolarda || true, || { ... } veya if komut; then ... fi kullanın.

Script'imi systemd service yapmak için?

Cron yerine systemd timer + service kombinasyonu daha esnek:

# /etc/systemd/system/site-yedek.service
[Unit]
Description=Site Backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/site-yedek.sh
StandardOutput=journal
StandardError=journal

# /etc/systemd/system/site-yedek.timer
[Unit]
Description=Daily site backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl enable --now site-yedek.timer
sudo systemctl list-timers
journalctl -u site-yedek.service

Sonuç

Bash script Linux sunucu yönetiminin en pratik aracı. Yedekleme, monitoring, deploy, log analiz — hepsini 50-150 satırda halledersiniz. Buyukweb VDS müşterilerimize tavsiyemiz: set -euo pipefail ile başlayın, shellcheck ile lint yapın, cron veya systemd timer ile zamanlayın, hassas veriyi ENV'de tutun, her önemli adımı log'a yazın. 5-10 günlük yatırımla yıllık 100+ saat kazanırsınız.

Soru ve teknik destek için: 0850 302 60 70.


İlgili Büyükweb Hizmetleri

Bash otomasyon için altyapı paketleri:

Sorularınız için 0850 302 60 70 numaralı destek hattımıza veya iletişim sayfamıza yazabilirsiniz.

Linux & Komut Satırı İlgili Hizmetlerimiz

Bu yazıda anlatılan teknik konuyu profesyonel altyapıyla deneyimleyin

Etiketler:

#bash#linux#komut satırı#terminal#sunucu yönetimi

Bu yazıyı paylaş