Bash’te Kullanıcıdan Girdi Alma: read Komutu ve Argüman İşleme

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: ve b: yanında : varsa bu parametreler bir değer bekler
  • Baştaki : yoksa geçersiz parametre hatası otomatik gösterilir, varsa manuel yönetirsiniz
  • OPTARG değişkeni parametreye verilen değeri tutar
  • OPTIND bir 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: $1 yerine 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 getopts kullanıyorsanız başında OPTIND=1 yapın
  • read ve pipeline: echo "değer" | read degisken çalışmaz, pipe bir subshell açar. Bunun yerine read degisken <<< "değer" kullanın
  • Boş değer kontrolü: read ile alınan değerin boş olup olmadığını her zaman kontrol edin
  • Çıkış kodları: Hata durumunda exit 1, başarıda exit 0 veya hiç exit yazmayı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.

Yorum yapın