Bash ile İnteraktif Menü Tasarımı: select, dialog ve whiptail Kullanımı

Kullanıcılardan girdi almanın en ilkel yolu read komutuyla bir şeyler sormak. Ama yazdığın script büyüdükçe, seçenekler çoğaldıkça bu yaklaşım hem çirkin görünmeye hem de yönetilmesi güç bir hale gelmeye başlıyor. İşte tam bu noktada interaktif menüler devreye giriyor. Bash’in yerleşik select yapısından başlayıp dialog ve whiptail gibi araçlarla profesyonel görünümlü TUI (Text User Interface) arayüzleri oluşturabilirsin. Bu yazıda sıfırdan başlayıp gerçek dünya senaryolarına kadar her şeyi ele alacağız.

Neden İnteraktif Menü?

Script yazarken hedef kitleni düşünmek zorundasın. Eğer script’i sadece kendin kullanacaksan her şeyi hardcode edebilir ya da argüman alacak şekilde düzenleyebilirsin. Ama sistemi tanımayan bir operatöre, müşteriye ya da yeni bir ekip üyesine kullandıracaksan işler değişiyor.

İnteraktif menüler şu durumlarda hayat kurtarır:

  • Sunucu kurulum ve konfigürasyon scriptleri
  • Yedekleme ve geri yükleme araçları
  • Servis yönetim panelleri
  • Kullanıcı yönetim scriptleri
  • Deploy ve rollback araçları

Şimdi araçlarımıza bakalım.

Bash’in Yerleşik select Yapısı

select, harici bir araç gerektirmeden temel menüler oluşturmana izin veren Bash built-in’idir. Sözdizimi for döngüsüne benzer ve kullanımı son derece kolaydır.

Temel select Kullanımı

#!/bin/bash

echo "Hangi servisi yönetmek istiyorsunuz?"

select servis in "Nginx" "Apache" "MySQL" "PostgreSQL" "Çıkış"; do
    case $servis in
        "Nginx")
            echo "Nginx seçildi."
            systemctl status nginx
            ;;
        "Apache")
            echo "Apache seçildi."
            systemctl status apache2
            ;;
        "MySQL")
            echo "MySQL seçildi."
            systemctl status mysql
            ;;
        "PostgreSQL")
            echo "PostgreSQL seçildi."
            systemctl status postgresql
            ;;
        "Çıkış")
            echo "Çıkılıyor..."
            break
            ;;
        *)
            echo "Geçersiz seçenek. Lütfen listeden bir numara girin."
            ;;
    esac
done

select çalıştığında seçenekleri otomatik olarak numaralandırır ve PS3 değişkeninde tanımlı olan prompt’u gösterir. Varsayılan prompt #? şeklindedir, bunu özelleştirmek için:

#!/bin/bash

PS3="Seçiminizi yapın (1-4): "

ortamlar=("Development" "Staging" "Production" "İptal")

select ortam in "${ortamlar[@]}"; do
    case $ortam in
        "Development"|"Staging"|"Production")
            echo "Seçilen ortam: $ortam"
            echo "Deploy işlemi başlatılıyor..."
            # deploy_script.sh $ortam
            break
            ;;
        "İptal")
            echo "İşlem iptal edildi."
            break
            ;;
        *)
            echo "Hatalı seçim: $REPLY"
            ;;
    esac
done

Burada $REPLY değişkeni kullanıcının girdiği ham değeri tutar, $ortam ise seçilen string’i tutar. İkisi arasındaki farkı anlamak önemli.

select ile İç İçe Menüler

Gerçek dünyada tek seviyeli menüler yeterli olmaz. İşte iç içe menü örneği:

#!/bin/bash

PS3="Ana menü seçimi: "

ana_menu() {
    echo ""
    echo "=== Sunucu Yönetim Paneli ==="
    select secim in "Servis Yönetimi" "Disk İşlemleri" "Kullanıcı Yönetimi" "Çıkış"; do
        case $secim in
            "Servis Yönetimi")
                servis_menu
                break
                ;;
            "Disk İşlemleri")
                disk_menu
                break
                ;;
            "Kullanıcı Yönetimi")
                kullanici_menu
                break
                ;;
            "Çıkış")
                echo "Güle güle!"
                exit 0
                ;;
            *)
                echo "Geçersiz seçim."
                ;;
        esac
    done
    ana_menu
}

servis_menu() {
    PS3="Servis işlemi: "
    echo ""
    echo "--- Servis Yönetimi ---"
    select islem in "Başlat" "Durdur" "Yeniden Başlat" "Durum Göster" "Ana Menüye Dön"; do
        case $islem in
            "Başlat"|"Durdur"|"Yeniden Başlat"|"Durum Göster")
                echo "$islem işlemi seçildi."
                break
                ;;
            "Ana Menüye Dön")
                PS3="Ana menü seçimi: "
                break
                ;;
            *)
                echo "Geçersiz seçim."
                ;;
        esac
    done
}

disk_menu() {
    PS3="Disk işlemi: "
    echo ""
    echo "--- Disk İşlemleri ---"
    select islem in "Disk Kullanımı" "Bölüm Listesi" "Ana Menüye Dön"; do
        case $islem in
            "Disk Kullanımı")
                df -h
                break
                ;;
            "Bölüm Listesi")
                lsblk
                break
                ;;
            "Ana Menüye Dön")
                PS3="Ana menü seçimi: "
                break
                ;;
        esac
    done
}

kullanici_menu() {
    echo "Kullanıcı yönetimi henüz implemente edilmedi."
}

ana_menu

select oldukça sade ve her sistemde çalışır. Ama görsel olarak zayıf. Daha profesyonel bir görünüm istiyorsan dialog veya whiptail şart.

dialog ile Profesyonel TUI Arayüzleri

dialog, ncurses tabanlı bir araç olup terminal içinde pencereler, form alanları, dosya seçiciler ve daha fazlasını oluşturmana izin veriyor. Debian/Ubuntu’da apt install dialog, RHEL/CentOS’ta yum install dialog ile kurabilirsin.

dialog Temel Widget’ları

dialog‘un en çok kullandığım widget’ları şunlar:

  • –msgbox: Bilgi mesajı gösterir
  • –yesno: Evet/Hayır onay kutusu
  • –inputbox: Metin girişi
  • –passwordbox: Şifre girişi (karakterler gizlenir)
  • –menu: Seçim menüsü
  • –checklist: Çoklu seçim listesi
  • –radiolist: Tekli seçim listesi
  • –gauge: İlerleme çubuğu
  • –textbox: Metin dosyası görüntüleyici
  • –fselect: Dosya seçici

Gerçek Dünya: Sunucu Kurulum Scripti

Aşağıdaki örnek, dialog kullanarak interaktif bir web sunucusu kurulum scripti oluşturuyor:

#!/bin/bash

# Geçici dosya çıktılar için
GECICI=$(mktemp)

# Çıkışta geçici dosyayı temizle
trap "rm -f $GECICI" EXIT

# Ekranı temizle
clear

# Hoşgeldin mesajı
dialog --title "Web Sunucu Kurulum Aracı" 
       --msgbox "Bu araç web sunucunuzu otomatik olarak yapılandıracaktır.nnDevam etmek için OK butonuna basın." 
       10 50

# Web sunucu seçimi
dialog --title "Web Sunucu Seçimi" 
       --menu "Kurmak istediğiniz web sunucuyu seçin:" 
       15 50 4 
       "1" "Nginx (Önerilen)" 
       "2" "Apache2" 
       "3" "Lighttpd" 
       "4" "Çıkış" 
       2> $GECICI

SUNUCU_SECIM=$?

# ESC veya Cancel kontrolü
if [ $SUNUCU_SECIM -ne 0 ]; then
    dialog --msgbox "Kurulum iptal edildi." 7 30
    exit 1
fi

SUNUCU=$(cat $GECICI)

# Seçime göre paket adı belirle
case $SUNUCU in
    1) PAKET="nginx" ;;
    2) PAKET="apache2" ;;
    3) PAKET="lighttpd" ;;
    4) exit 0 ;;
esac

# Ek bileşen seçimi (checklist)
dialog --title "Ek Bileşenler" 
       --checklist "Kurmak istediğiniz ek bileşenleri seçin (SPACE ile seçin):" 
       15 60 5 
       "php" "PHP Desteği" OFF 
       "ssl" "SSL/TLS Sertifika Kurulumu" ON 
       "firewall" "UFW Firewall Yapılandırması" ON 
       "logrotate" "Log Rotation Ayarları" OFF 
       "monitoring" "Temel İzleme Araçları" OFF 
       2> $GECICI

BILESENLER=$(cat $GECICI)

# Onay ekranı
dialog --title "Kurulum Özeti" 
       --yesno "Aşağıdaki bileşenler kurulacak:nnWeb Sunucu: $PAKETnEk Bileşenler: $BILESENLERnnDevam etmek istiyor musunuz?" 
       12 60

if [ $? -ne 0 ]; then
    dialog --msgbox "Kurulum iptal edildi." 7 30
    exit 1
fi

# Kurulum simülasyonu (gauge ile ilerleme gösterimi)
{
    echo 10; sleep 1
    echo 30; sleep 1
    echo 50; sleep 1
    echo 70; sleep 1
    echo 90; sleep 1
    echo 100; sleep 1
} | dialog --title "Kurulum İlerliyor" 
           --gauge "Paketler yükleniyor, lütfen bekleyin..." 
           8 50 0

dialog --title "Kurulum Tamamlandı" 
       --msgbox "$PAKET başarıyla kuruldu!nnServisi başlatmak için:nsystemctl start $PAKET" 
       10 50

Bu scriptte dikkat etmen gereken birkaç önemli nokta var. dialog çıktısını stderr’a yazar, bu yüzden 2> $GECICI kullanıyoruz. Ayrıca $? dönüş değeri OK için 0, Cancel için 1, ESC için 255 döner.

whiptail: dialog’un Hafif Kardeşi

whiptail, newt kütüphanesini kullanan ve dialog‘a kıyasla çok daha hafif bir alternatif. Raspberry Pi gibi kısıtlı sistemlerde, Docker container’larında ya da Debian tabanlı sistemlerde sıklıkla tercih ediliyor. Raspberry Pi’nin raspi-config aracı da whiptail ile yazılmış.

whiptail‘in dialog‘dan en önemli farkı --gauge widget’ını desteklememesi. Bunun dışında sözdizimi neredeyse birebir aynı.

whiptail ile Kullanıcı Yönetim Scripti

#!/bin/bash

GECICI=$(mktemp)
trap "rm -f $GECICI" EXIT

# whiptail'in mevcut olup olmadığını kontrol et
if ! command -v whiptail &> /dev/null; then
    echo "whiptail bulunamadı. Kuruluyor..."
    apt-get install -y whiptail 2>/dev/null || 
    yum install -y newt 2>/dev/null || 
    { echo "whiptail kurulamadı."; exit 1; }
fi

BOYUT=$(stty size)
SATIRLAR=$(echo $BOYUT | cut -d' ' -f1)
SUTUNLAR=$(echo $BOYUT | cut -d' ' -f2)

kullanici_ekle() {
    # Kullanıcı adı girişi
    whiptail --title "Kullanıcı Ekle" 
             --inputbox "Eklemek istediğiniz kullanıcı adını girin:" 
             10 50 "" 
             2> $GECICI

    [ $? -ne 0 ] && return

    KULLANICI=$(cat $GECICI)

    if [ -z "$KULLANICI" ]; then
        whiptail --msgbox "Kullanıcı adı boş olamaz!" 8 40
        return
    fi

    if id "$KULLANICI" &>/dev/null; then
        whiptail --msgbox "Bu kullanıcı zaten mevcut: $KULLANICI" 8 50
        return
    fi

    # Şifre girişi
    whiptail --title "Şifre Belirleme" 
             --passwordbox "Kullanıcı şifresini girin:" 
             10 50 
             2> $GECICI

    [ $? -ne 0 ] && return
    SIFRE=$(cat $GECICI)

    # Grup seçimi
    whiptail --title "Grup Seçimi" 
             --checklist "Kullanıcıyı eklemek istediğiniz grupları seçin:" 
             15 50 5 
             "sudo" "Yönetici Yetkileri" OFF 
             "docker" "Docker Grubu" OFF 
             "www-data" "Web Sunucu Grubu" OFF 
             "git" "Git Grubu" OFF 
             "backup" "Yedekleme Grubu" OFF 
             2> $GECICI

    GRUPLAR=$(cat $GECICI | tr -d '"')

    # Onay
    whiptail --title "Onay" 
             --yesno "Kullanıcı oluşturulacak:nnKullanıcı: $KULLANICInGruplar: $GRUPLARnnOnaylıyor musunuz?" 
             12 50

    if [ $? -eq 0 ]; then
        useradd -m -s /bin/bash "$KULLANICI"
        echo "$KULLANICI:$SIFRE" | chpasswd

        for grup in $GRUPLAR; do
            usermod -aG "$grup" "$KULLANICI" 2>/dev/null
        done

        whiptail --msgbox "Kullanıcı '$KULLANICI' başarıyla oluşturuldu." 8 50
    fi
}

kullanici_sil() {
    KULLANICILAR=$(awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $5}' /etc/passwd)

    if [ -z "$KULLANICILAR" ]; then
        whiptail --msgbox "Silinecek kullanıcı bulunamadı." 8 40
        return
    fi

    MENU_ITEMS=""
    while IFS= read -r satir; do
        AD=$(echo $satir | cut -d' ' -f1)
        ACIKLAMA=$(echo $satir | cut -d' ' -f2-)
        MENU_ITEMS="$MENU_ITEMS $AD "$ACIKLAMA""
    done <<< "$KULLANICILAR"

    eval "whiptail --title 'Kullanıcı Sil' 
                   --menu 'Silmek istediğiniz kullanıcıyı seçin:' 
                   15 50 8 
                   $MENU_ITEMS" 
                   2> $GECICI

    [ $? -ne 0 ] && return

    SILINECEK=$(cat $GECICI)

    whiptail --title "Emin misiniz?" 
             --yesno "'$SILINECEK' kullanıcısı silinecek. Bu işlem geri alınamaz!nnDevam etmek istiyor musunuz?" 
             10 50

    if [ $? -eq 0 ]; then
        userdel -r "$SILINECEK" 2>/dev/null
        whiptail --msgbox "Kullanıcı '$SILINECEK' silindi." 8 40
    fi
}

# Ana döngü
while true; do
    whiptail --title "Kullanıcı Yönetim Paneli" 
             --menu "İşlem seçin:" 
             15 50 5 
             "1" "Kullanıcı Ekle" 
             "2" "Kullanıcı Sil" 
             "3" "Kullanıcıları Listele" 
             "4" "Çıkış" 
             2> $GECICI

    [ $? -ne 0 ] && break

    case $(cat $GECICI) in
        1) kullanici_ekle ;;
        2) kullanici_sil ;;
        3) whiptail --textbox /etc/passwd 20 70 ;;
        4) break ;;
    esac
done

clear
echo "Kullanıcı yönetim panelinden çıkıldı."

dialog ve whiptail Arasında Seçim Yapmak

Script’ini farklı sistemlerde çalıştıracaksan hangisinin mevcut olduğunu otomatik tespit eden bir wrapper kullanmak işini kolaylaştırır:

#!/bin/bash

# dialog veya whiptail'i otomatik seç
if command -v dialog &>/dev/null; then
    DIALOG="dialog"
elif command -v whiptail &>/dev/null; then
    DIALOG="whiptail"
else
    echo "Hata: dialog veya whiptail bulunamadı!"
    echo "Kurmak için: apt install dialog"
    exit 1
fi

GECICI=$(mktemp)
trap "rm -f $GECICI" EXIT

# Artık $DIALOG değişkenini kullan
$DIALOG --title "Test" 
        --msgbox "Sistem: $DIALOG kullanılıyor." 
        8 40

Bu yaklaşım özellikle farklı Linux dağıtımlarında çalışan scriptlerde çok işe yarıyor. Ubuntu’da whiptail yüklü gelirken CentOS’ta dialog tercih edilebilir.

Renk ve Tema Özelleştirme

dialog araç renklerini özelleştirmene izin veriyor. .dialogrc dosyası oluşturarak ya da DIALOGRC environment variable’ı ile tema tanımlayabilirsin:

#!/bin/bash

# Geçici tema dosyası oluştur
TEMA=$(mktemp)
trap "rm -f $TEMA" EXIT

cat > $TEMA << 'EOF'
use_shadow = OFF
use_colors = ON
screen_color = (CYAN,BLUE,ON)
dialog_color = (BLACK,WHITE,OFF)
title_color = (BLUE,WHITE,ON)
border_color = (WHITE,WHITE,ON)
button_active_color = (WHITE,BLUE,ON)
button_inactive_color = (BLACK,WHITE,OFF)
tag_key_color = (RED,WHITE,ON)
item_selected_color = (WHITE,BLUE,ON)
EOF

export DIALOGRC=$TEMA

dialog --title "Özelleştirilmiş Tema" 
       --msgbox "Bu mesaj kutusu özel tema renkleriyle gösteriliyor!nnTema dosyası: $TEMA" 
       10 55

# Ana menü
dialog --title "Tema Demo" 
       --menu "Özelleştirilmiş renk teması:" 
       12 45 4 
       "1" "Birinci Seçenek" 
       "2" "İkinci Seçenek" 
       "3" "Üçüncü Seçenek" 
       "4" "Çıkış" 
       2>/dev/null

İpuçları ve Dikkat Edilmesi Gerekenler

Yıllar içinde edindiğim bazı pratik notlar:

  • Geçici dosya yönetimi: Her zaman mktemp kullan, tmp_file=/tmp/dialog_output gibi sabit isimler race condition’a yol açar.
  • trap kullanımı: Script sonlandığında geçici dosyaları temizlemek için mutlaka trap "rm -f $GECICI" EXIT ekle.
  • Terminal boyutu: stty size ile terminal boyutunu alıp dinamik pencere boyutlandırması yapabilirsin. Küçük terminallerde sabit boyutlu pencereler kesilir.
  • Root kontrolü: Sistem yönetimi scriptlerinde önce root kontrolü yap, ardından menüyü başlat.
  • Dönüş değerlerini kontrol et: dialog ve whiptail Cancel için 1, ESC için 255 döner. Her widget’tan sonra $? kontrol etmeyi alışkanlık haline getir.
  • stderr yönlendirmesi: dialog çıktısını stderr’a yazar. 2> $GECICI kullanmayı unutursan çıktıyı okuyamazsın.
  • SSH üzerinden kullanım: SSH bağlantılarında TERM değişkeni bazen sorun çıkarır. Gerekirse export TERM=xterm ekle.

Sonuç

select, dialog ve whiptail üçlüsü Bash scriptlerini gerçek anlamda kullanıcı dostu araçlara dönüştürüyor. Sade ve hızlı bir çözüm istiyorsan select yeterli. Görsel olarak daha etkileyici, form tabanlı veya çok adımlı bir arayüz istiyorsan dialog ilk tercihin olmalı. Hafiflik ve taşınabilirlik öncelikliyse whiptail‘e yönel.

En önemli tavsiyem şu: Script’ini yazarken araç seçimini en sona bırakma. Önce hangi akışın olması gerektiğini, hangi verilerin alınacağını ve hata durumlarının nasıl yönetileceğini planla. Sonra bu planı uygun araçla hayata geçir. İnteraktif menüler sadece estetik değil, script’in hatalı kullanılmasını önleyen bir güvenlik katmanı da oluşturuyor. Doğru seçenek sunmak, kullanıcının yanlış bir şey girmesinden çok daha güvenli.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir