Bash script yazarken en çok göz ardı edilen konulardan biri kullanıcıyla etkileşim kurma ve dışarıdan veri alma mekanizmalarıdır. Oysa production ortamında çalışan scriptlerin büyük çoğunluğu ya bir kullanıcıdan bir şeyler sorar ya da komut satırından argüman bekler. Bu iki yöntemi düzgün kullanmayı öğrenmek, yazdığın scriptleri gerçek anlamda kullanılabilir araçlara dönüştürür.
read Komutu ile Kullanıcıdan Girdi Alma
read komutu, bash scriptinde kullanıcıdan interaktif girdi almanın temel yoludur. Basit görünse de içinde barındırdığı seçeneklerle oldukça güçlü bir araçtır.
Temel Kullanım
En basit haliyle read bir değişken adı bekler ve kullanıcı Enter’a basana kadar girdiği her şeyi o değişkene atar.
#!/bin/bash
echo "Sunucu adini girin:"
read sunucu_adi
echo "Bağlanılacak sunucu: $sunucu_adi"
Bu kadar basit. Ama pratikte bu yapıyı pek kullanmıyoruz çünkü echo ve read‘i birleştirmenin daha temiz bir yolu var.
-p ile Prompt Gösterme
-p parametresi, kullanıcıya gösterilecek prompt metnini doğrudan read komutuna eklememizi sağlar:
#!/bin/bash
read -p "Yedeklenecek dizin yolunu girin: " dizin_yolu
read -p "Hedef sunucu IP adresi: " hedef_ip
read -p "SSH kullanicisi: " ssh_user
echo ""
echo "Yedekleme başlatılıyor..."
echo "Kaynak: $dizin_yolu"
echo "Hedef: $ssh_user@$hedef_ip"
-s ile Parola Okuma
Parola gibi hassas bilgileri okurken ekranda görünmemesini isteriz. -s (silent) parametresi tam bunun için:
#!/bin/bash
read -p "Kullanici adi: " kullanici
read -s -p "Parola: " parola
echo "" # Paroladan sonra satir atlama olmaz, manuel ekliyoruz
echo "Giriş deneniyor: $kullanici kullanicisi ile..."
# Gerçek hayatta burada ssh veya curl komutu olurdu
-s parametresi kullanıldığında kullanıcının bastığı tuşlar ekranda görünmez ama değişkene atanır. Paroladan sonra echo "" yazmayı unutmayın, yoksa bir sonraki çıktı aynı satırda görünür.
-t ile Zaman Aşımı
Otomatik çalışan scriptlerde bazen kullanıcıdan onay beklersiniz ama script sonsuza kadar bekleyemez. -t parametresi saniye cinsinden zaman aşımı süresini belirler:
#!/bin/bash
echo "Uyari: /var/log dizini temizlenecek!"
echo "Devam etmek icin 10 saniye icinde 'evet' yazin."
if read -t 10 -p "Onay: " onay && [ "$onay" = "evet" ]; then
echo "Temizlik başlatılıyor..."
# find /var/log -name "*.log" -mtime +30 -delete
else
echo ""
echo "İşlem iptal edildi veya zaman aşımına uğradı."
fi
-n ile Karakter Sınırı
-n parametresi belirtilen sayıda karakter girildiğinde Enter beklenmeden okumayı tamamlar. Tek karakterlik onay almak için idealdir:
#!/bin/bash
read -n 1 -p "Devam etmek istiyor musunuz? [e/h]: " yanit
echo ""
case $yanit in
e|E)
echo "Devam ediliyor..."
;;
h|H)
echo "Çıkılıyor..."
exit 0
;;
*)
echo "Geçersiz seçim."
exit 1
;;
esac
IFS ile Çoklu Değer Okuma
read aynı anda birden fazla değişkene değer atayabilir. IFS (Internal Field Separator) ile ayırıcı karakteri belirleyebilirsiniz:
#!/bin/bash
read -p "IP adresi ve port girin (örn: 192.168.1.1 8080): " ip port
echo "IP: $ip"
echo "Port: $port"
# CSV dosyasından satır okuma örneği
while IFS=',' read -r hostname ip_adresi rol; do
echo "Sunucu: $hostname | IP: $ip_adresi | Rol: $rol"
done < sunucular.csv
Komut Satırı Argümanları ile Çalışma
Interaktif scriptler her zaman doğru seçim değildir. Cron job’larda, pipeline’larda veya başka scriptlerden çağrıldığında argüman bazlı yaklaşım çok daha uygun olur.
Pozisyonel Parametreler
Bash’te komut satırı argümanları $1, $2, $3… şeklinde erişilir. $0 scriptin adını, $# toplam argüman sayısını, $@ tüm argümanları ayrı ayrı, $* ise tek string olarak tutar:
#!/bin/bash
# Kullanım: ./yedekle.sh /kaynak/dizin /hedef/dizin
if [ $# -lt 2 ]; then
echo "Kullanim: $0 <kaynak_dizin> <hedef_dizin>"
echo "Örnek: $0 /var/www /backup/www"
exit 1
fi
KAYNAK="$1"
HEDEF="$2"
if [ ! -d "$KAYNAK" ]; then
echo "Hata: Kaynak dizin bulunamadi: $KAYNAK"
exit 1
fi
echo "Yedekleme: $KAYNAK -> $HEDEF"
rsync -avz "$KAYNAK" "$HEDEF"
getopts ile Bayrak Tabanlı Argüman İşleme
Gerçek araçlar gibi -v, -f dosya, -p 8080 şeklinde çalışan scriptler yazmak için getopts kullanılır. Bu bash’in built-in argüman ayrıştırma aracıdır:
#!/bin/bash
# Kullanim: ./deploy.sh -e production -b main -v
ENVIRONMENT=""
BRANCH="main"
VERBOSE=false
while getopts "e:b:vh" opt; do
case $opt in
e)
ENVIRONMENT="$OPTARG"
;;
b)
BRANCH="$OPTARG"
;;
v)
VERBOSE=true
;;
h)
echo "Kullanim: $0 -e <ortam> [-b <branch>] [-v]"
echo ""
echo "Seçenekler:"
echo " -e Deployment ortami (zorunlu): production, staging, dev"
echo " -b Git branch (varsayilan: main)"
echo " -v Verbose mod"
echo " -h Bu yardim mesajini göster"
exit 0
;;
?)
echo "Geçersiz parametre: -$OPTARG" >&2
exit 1
;;
:)
echo "-$OPTARG parametresi bir değer gerektiriyor." >&2
exit 1
;;
esac
done
if [ -z "$ENVIRONMENT" ]; then
echo "Hata: -e parametresi zorunludur."
exit 1
fi
[ "$VERBOSE" = true ] && echo "Verbose mod aktif"
echo "Ortam: $ENVIRONMENT | Branch: $BRANCH"
getopts sözdiziminde : işaretinin anlamı şöyle:
- “e:b:vh” ifadesinde
e:veb:yanında:varsa bu parametreler bir değer bekler - Baştaki
:yoksa geçersiz parametre hatası otomatik gösterilir, varsa manuel yönetirsiniz OPTARGdeğişkeni parametreye verilen değeri tutarOPTINDbir sonraki işlenecek argümanın indeksini tutar
shift ile Pozisyonel Parametre Kaydırma
getopts ile işlenen parametreleri atladıktan sonra kalan argümanlara ulaşmak için shift kullanılır:
#!/bin/bash
FORCE=false
OUTPUT_DIR="/tmp"
while getopts "fo:" opt; do
case $opt in
f) FORCE=true ;;
o) OUTPUT_DIR="$OPTARG" ;;
esac
done
# getopts'un işlediği argümanları atla
shift $((OPTIND - 1))
# Kalan argümanlar pozisyonel parametreler
echo "Zorla: $FORCE"
echo "Çıktı dizini: $OUTPUT_DIR"
echo "İşlenecek dosyalar:"
for dosya in "$@"; do
echo " - $dosya"
done
Gerçek Dünya Senaryosu: Sunucu Kurulum Scripti
Şimdiye kadar öğrendiklerimizi birleştiren kapsamlı bir örnek yapalım. Bu script hem interaktif hem de argüman tabanlı çalışabiliyor:
#!/bin/bash
# sunucu_kur.sh - Web sunucusu kurulum scripti
# Kullanim: ./sunucu_kur.sh [-t nginx|apache] [-d domain] [-i]
set -euo pipefail
SUNUCU_TURU=""
DOMAIN=""
INTERAKTIF=false
LOG_DOSYASI="/var/log/sunucu_kur.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DOSYASI"
}
kullanim_goster() {
cat << EOF
Kullanim: $0 [SEÇENEKLER]
Seçenekler:
-t Sunucu turu: nginx veya apache (zorunlu)
-d Domain adi (örn: example.com)
-i Interaktif mod - eksik bilgileri sorar
-h Yardim
Örnekler:
$0 -t nginx -d example.com
$0 -i
EOF
}
interaktif_doldur() {
if [ -z "$SUNUCU_TURU" ]; then
echo "Hangi web sunucusunu kurmak istiyorsunuz?"
echo " 1) nginx"
echo " 2) apache"
read -p "Seciminiz [1-2]: " secim
case $secim in
1) SUNUCU_TURU="nginx" ;;
2) SUNUCU_TURU="apache" ;;
*) echo "Geçersiz seçim."; exit 1 ;;
esac
fi
if [ -z "$DOMAIN" ]; then
read -p "Domain adi (boş bırakabilirsiniz): " DOMAIN
fi
read -n 1 -p "$SUNUCU_TURU kurulacak. Onaylıyor musunuz? [e/h]: " onay
echo ""
[ "$onay" != "e" ] && [ "$onay" != "E" ] && { echo "İptal edildi."; exit 0; }
}
while getopts "t:d:ih" opt; do
case $opt in
t) SUNUCU_TURU="$OPTARG" ;;
d) DOMAIN="$OPTARG" ;;
i) INTERAKTIF=true ;;
h) kullanim_goster; exit 0 ;;
?) echo "Geçersiz parametre: -$OPTARG"; exit 1 ;;
esac
done
[ "$INTERAKTIF" = true ] && interaktif_doldur
if [ -z "$SUNUCU_TURU" ]; then
echo "Hata: Sunucu turu belirtilmedi. -t veya -i kullanin."
kullanim_goster
exit 1
fi
log "$SUNUCU_TURU kurulumu başlıyor..."
# apt-get install -y "$SUNUCU_TURU"
[ -n "$DOMAIN" ] && log "Domain: $DOMAIN için yapılandırma oluşturuluyor..."
log "Kurulum tamamlandi."
Giriş Doğrulama ve Güvenli Programlama
Kullanıcıdan veya argümandan gelen veriyi hiçbir zaman doğrulamadan kullanma. Bu hem güvenlik riski hem de beklenmedik hatalara kapı açar.
#!/bin/bash
# Güvenli girdi doğrulama örnekleri
dogrula_ip() {
local ip="$1"
local regex='^([0-9]{1,3}.){3}[0-9]{1,3}$'
if [[ ! $ip =~ $regex ]]; then
echo "Hata: Geçersiz IP formatı: $ip"
return 1
fi
IFS='.' read -r -a oktetler <<< "$ip"
for oktet in "${oktetler[@]}"; do
if [ "$oktet" -gt 255 ]; then
echo "Hata: IP okteti 255'ten büyük olamaz: $oktet"
return 1
fi
done
return 0
}
dogrula_port() {
local port="$1"
if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
echo "Hata: Geçersiz port numarasi: $port (1-65535 arasi olmali)"
return 1
fi
return 0
}
# Kullanim
read -p "Hedef IP: " hedef_ip
if ! dogrula_ip "$hedef_ip"; then
exit 1
fi
read -p "Port numarasi: " port_no
if ! dogrula_port "$port_no"; then
exit 1
fi
echo "Bağlantı: $hedef_ip:$port_no"
Döngü ile Tekrar Eden Girdi İsteme
Kullanıcı yanlış bir şey girdiğinde scriptten çıkmak yerine tekrar sormak çok daha iyi bir deneyim sunar:
#!/bin/bash
gecerli_ortan_al() {
local ortam
local gecerli_ortamlar=("production" "staging" "development" "test")
while true; do
read -p "Ortam seçin (production/staging/development/test): " ortam
for gecerli in "${gecerli_ortamlar[@]}"; do
if [ "$ortam" = "$gecerli" ]; then
echo "$ortam"
return 0
fi
done
echo "Geçersiz ortam: '$ortam'. Tekrar deneyin."
done
}
gecerli_parola_al() {
local parola1 parola2
while true; do
read -s -p "Yeni parola: " parola1
echo ""
read -s -p "Parola tekrar: " parola2
echo ""
if [ ${#parola1} -lt 8 ]; then
echo "Hata: Parola en az 8 karakter olmali."
continue
fi
if [ "$parola1" != "$parola2" ]; then
echo "Hata: Parolalar eşlesmiyor."
continue
fi
echo "$parola1"
return 0
done
}
ORTAM=$(gecerli_ortan_al)
echo "Seçilen ortam: $ORTAM"
Konfigürasyon Dosyası ile Entegrasyon
Production scriptlerinde kullanıcı her seferinde aynı şeyleri girmek zorunda kalmamalı. Girilen değerleri bir config dosyasına kaydedip sonraki çalıştırmada varsayılan olarak sunabilirsiniz:
#!/bin/bash
CONFIG_DOSYASI="$HOME/.deploy_config"
config_yukle() {
if [ -f "$CONFIG_DOSYASI" ]; then
# shellcheck source=/dev/null
source "$CONFIG_DOSYASI"
echo "Kayıtlı yapılandırma yüklendi: $CONFIG_DOSYASI"
fi
}
config_kaydet() {
cat > "$CONFIG_DOSYASI" << EOF
# Deploy scripti yapılandırması
# Son guncelleme: $(date)
KAYITLI_SUNUCU="$SUNUCU"
KAYITLI_KULLANICI="$KULLANICI"
KAYITLI_DIZIN="$DIZIN"
EOF
chmod 600 "$CONFIG_DOSYASI"
echo "Yapılandırma kaydedildi."
}
config_yukle
read -p "Sunucu adresi [${KAYITLI_SUNUCU:-bos}]: " girdi_sunucu
SUNUCU="${girdi_sunucu:-$KAYITLI_SUNUCU}"
read -p "Kullanici adi [${KAYITLI_KULLANICI:-$USER}]: " girdi_kullanici
KULLANICI="${girdi_kullanici:-${KAYITLI_KULLANICI:-$USER}}"
read -p "Hedef dizin [${KAYITLI_DIZIN:-/var/www/html}]: " girdi_dizin
DIZIN="${girdi_dizin:-${KAYITLI_DIZIN:-/var/www/html}}"
echo ""
echo "Sunucu: $SUNUCU"
echo "Kullanici: $KULLANICI"
echo "Dizin: $DIZIN"
read -n 1 -p "Bu ayarları kaydetmek istiyor musunuz? [e/h]: " kaydet_yanit
echo ""
[ "$kaydet_yanit" = "e" ] || [ "$kaydet_yanit" = "E" ] && config_kaydet
${degisken:-varsayilan} sözdizimi burada kritik rol oynuyor. Değişken boşsa veya tanımsızsa varsayılan değeri kullanır. Bu pattern’ı sıkça kullanacaksınız.
Önemli Pratik Notlar
Script yazarken dikkat etmeniz gereken birkaç nokta var:
- Argüman alıntılama:
$1yerine her zaman"$1"kullanın. Boşluk içeren değerlerde sorun çıkmasını önler - $@ vs $*: Argümanları döngüde işlerken
"$@"kullanın, her argümanı ayrı element olarak korur - OPTIND sıfırlama: Bir fonksiyon içinde
getoptskullanıyorsanız başındaOPTIND=1yapın - read ve pipeline:
echo "değer" | read degiskençalışmaz, pipe bir subshell açar. Bunun yerineread degisken <<< "değer"kullanın - Boş değer kontrolü:
readile alınan değerin boş olup olmadığını her zaman kontrol edin - Çıkış kodları: Hata durumunda
exit 1, başarıdaexit 0veya hiçexityazmayın (son komutun çıkış kodu kullanılır)
Sonuç
read ve argüman işleme, bash scriptlerini gerçek anlamda kullanışlı araçlara dönüştüren temel yapı taşlarıdır. İnteraktif mod canlı kullanım için iyiyken, argüman tabanlı yapı otomasyon ve cron job senaryolarında parlar. En iyi yaklaşım genellikle ikisini birden desteklemektir; tıpkı son örneğimizdeki gibi.
Doğrulama fonksiyonlarını ihmal etmeyin. Kullanıcıdan gelen her veri potansiyel olarak hatalı veya kötü niyetlidir. Config dosyasına kaydetme pattern’ı ise küçük ama kullanıcı deneyimini ciddi ölçüde iyileştiren bir detay, production araçlarında mutlaka düşünün.
Bir sonraki adım olarak bu öğrendiklerinizi hata yönetimi (trap, set -e) ve loglama mekanizmalarıyla birleştirmenizi öneririm. O zaman gerçekten production kalitesinde scriptler ortaya çıkmaya başlar.