Açık Kaynak İşletim Sistemleri

10 - Kabuk Programlama

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2025

Bu hafta

  • Kabuk ve Betiklere Giriş
  • Değişkenler ve Veri İşleme
  • Kontrol Akışı: Koşullar ve Döngüler
  • Fonksiyonlara Giriş
  • Pratik Uygulamalar ve İyi Alışkanlıklar
  • Sorular & Cevaplar

Kabuk (Shell) Nedir? Kısa Bir Hatırlatma

  • Kabuk, kullanıcı ile işletim sistemi çekirdeği (kernel) arasında bir arayüzdür.
  • Komutlarımızı yorumlar ve işletim sistemine iletir.
  • En yaygın kabuklardan bazıları:
    • bash (Bourne Again Shell) - Derslerimizde genellikle bunu kullanıyoruz.
    • sh (Bourne Shell)
    • ksh (Korn Shell)
    • zsh (Z Shell)
    • fish (Friendly Interactive Shell)

Gündelik hayatta terminalde yazdığımız ls, cd, mkdir gibi komutları kabuk çalıştırır.

Kabuk Betiği (Shell Script) Nedir?

  • Kabuk betiği, bir dizi kabuk komutunu içeren bir metin dosyasıdır.
  • Bu komutlar, dosya çalıştırıldığında sırayla icra edilir.
  • Amacı:
    • Tekrarlayan görevleri otomatikleştirmek.
    • Karmaşık komut dizilerini basitleştirmek.
    • Sistem yönetimi görevlerini kolaylaştırmak.

Düşünün: Her sabah bilgisayarınızı açtığınızda belirli klasörleri yedeklemek, geçici dosyaları silmek ve bir uygulama başlatmak istiyorsunuz. Bunları her gün elle yapmak yerine bir kabuk betiği yazarak tek komutla halledebilirsiniz!

İlk Kabuk Betiğimiz: “Merhaba Dünya”

Her programlama dilinde olduğu gibi, “Merhaba Dünya” ile başlayalım!

  1. Bir metin editörü açın (örneğin nano, vim, gedit).
  2. Aşağıdaki satırları yazın ve dosyayı merhaba.sh olarak kaydedin:
#!/bin/bash

# Bu benim ilk kabuk betiğim
echo "Merhaba Dünya!"
  • #!/bin/bash: Shebang olarak bilinir. Betiğin hangi yorumlayıcı (interpreter) ile çalıştırılacağını belirtir. Burada bash kabuğunu kullanacağımızı söylüyoruz.
  • # Bu benim ilk kabuk betiğim: # ile başlayan satırlar yorum satırlarıdır. Kabuk tarafından göz ardı edilir, kodu açıklamak için kullanılır.
  • echo "Merhaba Dünya!": Ekrana “Merhaba Dünya!” yazdıran komut.

Betikleri Çalıştırılabilir Yapmak ve Çalıştırmak

Oluşturduğumuz merhaba.sh dosyasını çalıştırmak için iki adımımız var:

  1. Çalıştırma İzni Vermek: Dosyaya çalıştırma (execute) izni vermemiz gerekir. Terminalde:

    chmod +x merhaba.sh
    • chmod: Dosya izinlerini değiştiren komut.
    • +x: Çalıştırma izni ekle.
  2. Betiği Çalıştırmak: Terminalde, betiğin bulunduğu dizindeyken:

    ./merhaba.sh
    • ./: “Mevcut dizindeki” anlamına gelir. Güvenlik nedeniyle, sistem genellikle doğrudan merhaba.sh yazarak çalıştırmanıza izin vermez (PATH’de değilse).

Alıştırma 1: Kendi adınızı ve soyadınızı ekrana yazdıran adim.sh adında bir betik oluşturun, çalıştırma izni verin ve çalıştırın.

Yorum Satırları

  • Kodunuzu anlaşılır kılmak için yorum satırları çok önemlidir.
  • # karakteri ile başlayan her şey o satırın sonuna kadar yorum olarak kabul edilir.
  • Kabuk bu satırları işlemez.
#!/bin/bash

# Bu bir yorum satırıdır
# Bu da başka bir yorum
# Bu betik, sistemdeki kullanıcı sayısını gösterir (aslında göstermez, sadece örnektir)

echo "Yorumları anladık mı?" # Bu da satır sonu yorumu

Gelecekte kendi kodunuzu (veya başkasının kodunu) okurken yorumlar hayat kurtarır!

Değişkenler

  • Değişkenler, verileri geçici olarak saklamak için kullanılan isimlendirilmiş alanlardır.
  • Birçok programlama dilinde olduğu gibi kabuk programlamada da önemlidirler.

Değişken Tanımlama ve Kullanma

Değişken Tanımlama:

degisken_adi=değer

Önemli Kurallar:

  • Değişken adı ile = arasında ve = ile değer arasında boşluk olmamalıdır.
  • Değişken adları harf veya alt çizgi (_) ile başlamalıdır. Sayı içerebilir ama sayı ile başlayamaz.
  • Genellikle büyük harf kullanılır (zorunlu değil ama yaygın bir gelenektir).
#!/bin/bash

AD="Ahmet"
SOYAD="Yılmaz"
YAS=30

echo "Adım: $AD"
echo "Soyadım: $SOYAD"
echo "Yaşım: $YAS"

Değişkenin değerini kullanmak için başına $ işareti koyarız: $AD, $YAS.

Değişkenlerde Tırnak İşaretlerinin Önemi

Kabuk programlamada tırnak işaretleri çok önemlidir ve farklı anlamlara gelir:

  • Çift Tırnak ("..."):
    • İçindeki değişkenler ($degisken) ve komut yerine koyma ($(komut)) işlenir (genişletilir).
    • Boşluk içeren değerleri tek bir argüman olarak saklamak için kullanılır.
    MESAJ="Merhaba Dünya"
    echo "$MESAJ, bugün hava çok güzel." # MESAJ değişkeni genişletilir.
  • Tek Tırnak ('...'):
    • İçindeki her şey olduğu gibi (literals) kabul edilir. Değişkenler veya özel karakterler işlenmez.
    KULLANICI="ayse"
    echo 'Kullanıcı adı: $KULLANICI' # Çıktı: Kullanıcı adı: $KULLANICI
  • Ters Tırnak (`...`) veya $(...) (Komut Yerine Koyma):
    • İçindeki komut çalıştırılır ve komutun çıktısı değişkenin değeri olur.
    • $(...) kullanımı daha modern ve iç içe kullanıma daha uygundur.
    #!/bin/bash
    
    TARIH_SAAT_BACKTICK=`date`
    echo "Şu anki tarih ve saat (ters tırnak): $TARIH_SAAT_BACKTICK"
    
    TARIH_SAAT_DOLAR_PARANTEZ=$(date)
    echo "Şu anki tarih ve saat (dolar parantez): $TARIH_SAAT_DOLAR_PARANTEZ"
    
    KULLANICI_SAYISI=$(who | wc -l)
    echo "Sistemde aktif $KULLANICI_SAYISI kullanıcı var."

Alıştırma 2: kullanici_bilgisi.sh adında bir betik yazın.

  1. KULLANICI_ADI adında bir değişken tanımlayın ve kendi kullanıcı adınızı atayın.
  2. CALISMA_DIZINI adında bir değişken tanımlayın ve pwd komutunun çıktısını atayın.
  3. Bu değişkenleri kullanarak “Kullanıcı Adı: [ADINIZ], Çalışma Dizini: [DIZININIZ]” formatında bir mesaj yazdırın.
    • Mesajı hem çift tırnak hem de tek tırnak içinde yazdırmayı deneyin ve farkı gözlemleyin.

Kullanıcıdan Girdi Alma: read Komutu

  • Betiklerimizin interaktif olmasını, yani kullanıcıdan bilgi almasını isteyebiliriz.
  • read komutu bu iş için kullanılır. Kullanıcının girdiği değeri bir değişkene atar.
#!/bin/bash

echo "Lütfen adınızı giriniz:"
read KULLANICI_ADI

echo "Merhaba, $KULLANICI_ADI!"

# -p seçeneği ile kullanıcıya mesaj gösterip aynı satırda girdi alabiliriz:
read -p "Yaşınızı giriniz: " YAS
echo "$KULLANICI_ADI, $YAS yaşındasınız."

Alıştırma 3: Kullanıcıdan iki sayı girmesini isteyen ve bu sayıları ekrana “Girdiğiniz sayılar: [sayi1] ve [sayi2]” formatında yazdıran sayi_al.sh adında bir betik yazın.

Konumsal Parametreler

  • Betikler çalıştırılırken onlara argümanlar (parametreler) geçirebiliriz.
  • Bu argümanlara betik içinden özel değişkenlerle erişilir:
    • $0: Betiğin kendi adı.
    • $1: Birinci argüman.
    • $2: İkinci argüman.
    • $9: Dokuzuncu argüman. (10 ve sonrası için ${10} şeklinde kullanılır)
    • $#: Betiğe geçirilen toplam argüman sayısı.
    • $*: Tüm argümanları tek bir string olarak verir (tırnak içinde kullanıldığında).
    • $@: Tüm argümanları ayrı ayrı stringler olarak verir (tırnak içinde kullanıldığında genellikle daha güvenlidir).

Örnek (parametreler.sh):

#!/bin/bash

echo "Betiğin adı: $0"
echo "İlk argüman: $1"
echo "İkinci argüman: $2"
echo "Toplam argüman sayısı: $#"
echo "Tüm argümanlar (\$*): $*"
echo "Tüm argümanlar (\$@): $@"

Çalıştırma:

./parametreler.sh merhaba dünya 123

Çıktı:

Betiğin adı: ./parametreler.sh
İlk argüman: merhaba
İkinci argüman: dünya
Toplam argüman sayısı: 3
Tüm argümanlar ($*): merhaba dünya 123
Tüm argümanlar ($@): merhaba dünya 123

Alıştırma 4: selamla.sh adında bir betik yazın. Bu betik, komut satırından bir isim argümanı alacak ve “Merhaba, [isim]!” mesajını yazdıracak. Eğer isim verilmezse, “Lütfen bir isim giriniz.” mesajı versin. (İpucu: $# kontrolü yapabilirsiniz.)

Aritmetik İşlemler

Kabukta aritmetik işlemler için birkaç yöntem vardır:

  1. let Komutu:

    let "sonuc = 5 + 3"
    echo $sonuc # 8
    let x=10 y=20 z="x*y"
    echo $z # 200
  1. $((...)) Yapısı (Önerilen): Bu yapı daha modern ve esnektir.

    SAYI1=10
    SAYI2=5
    TOPLAM=$((SAYI1 + SAYI2))
    FARK=$((SAYI1 - SAYI2))
    CARPIM=$((SAYI1 * SAYI2)) # Çarpma için *
    BOLUM=$((SAYI1 / SAYI2))   # Bölme için /
    MOD=$((SAYI1 % SAYI2))     # Modülüs (kalan) için %
    
    echo "Toplam: $TOPLAM"
    echo "Fark: $FARK"
    echo "Çarpım: $CARPIM"
    echo "Bölüm: $BOLUM"
    echo "Mod: $MOD"
    
    # Artırma/Azaltma
    X=5
    X=$((X + 1)) # veya ((X++)) bash'e özgü
    echo "X: $X" # X: 6

Alıştırma 5: Kullanıcıdan iki sayı alan ve bu sayıların toplamını, farkını, çarpımını ve bölümünü ekrana yazdıran hesap_makinesi.sh adında bir betik yazın.

Koşullu İfadeler: if, else, elif

  • Program akışını belirli koşullara göre değiştirmek için kullanılır.

  • Temel yapı:

    if [ koşul ]; then
        # koşul doğruysa çalışacak komutlar
    fi
  • else ve elif (else if) ile genişletilebilir:

    if [ koşul1 ]; then
        # koşul1 doğruysa
    elif [ koşul2 ]; then
        # koşul1 yanlış, koşul2 doğruysa
    else
        # hiçbir koşul doğru değilse
    fi

    ÖNEMLİ: [ (test komutunun takma adı) ile koşul arasında ve koşul ile ] arasında boşluk olmalıdır! Yani [ $SAYI -eq 10 ] doğru, [$SAYI -eq 10] yanlış.

Test Koşulları

if yapısı içindeki [ koşul ] (veya test koşul) bölümünde çeşitli karşılaştırmalar yapabiliriz:

1. Sayısal Karşılaştırmalar:

  • -eq: Eşit (equal)
  • -ne: Eşit değil (not equal)
  • -gt: Büyük (greater than)
  • -ge: Büyük veya eşit (greater or equal)
  • -lt: Küçük (less than)
  • -le: Küçük veya eşit (less or equal)

Örnek:

SAYI=10
if [ $SAYI -eq 10 ]; then
    echo "Sayı 10'a eşittir."
fi

if [ $SAYI -gt 5 ]; then
    echo "Sayı 5'ten büyüktür."
fi

2. String Karşılaştırmalar:

  • =: Eşit (Bazı kabuklarda == de çalışır, = daha taşınabilirdir)
  • !=: Eşit değil
  • -z string: String boş mu? (zero length)
  • -n string: String boş değil mi? (non-zero length)

Örnek:

AD="Ali"
if [ "$AD" = "Ali" ]; then # Değişkenleri tırnak içinde kullanmak iyi bir pratiktir
    echo "Merhaba Ali!"
fi

PAROLA=""
if [ -z "$PAROLA" ]; then
    echo "Parola boş bırakılamaz."
fi

3. Dosya Testleri:

  • -e dosya_yolu: Dosya/Dizin var mı? (exists)
  • -f dosya_yolu: Dosya var ve normal bir dosya mı? (file)
  • -d dosya_yolu: Dizin var mı? (directory)
  • -r dosya_yolu: Okuma izni var mı? (readable)
  • -w dosya_yolu: Yazma izni var mı? (writable)
  • -x dosya_yolu: Çalıştırma izni var mı? (executable)
  • -s dosya_yolu: Dosya var ve boyutu sıfırdan büyük mü? (size)

Örnek:

DOSYA="notlar.txt"
if [ -f "$DOSYA" ]; then
    echo "$DOSYA bir dosyadır."
else
    echo "$DOSYA bulunamadı veya bir dosya değil."
fi

Koşullu İfadeler - Örnek

#!/bin/bash

read -p "Lütfen bir sayı giriniz: " SAYI

if [ -z "$SAYI" ]; then
    echo "Bir sayı girmediniz!"
elif ! [[ "$SAYI" =~ ^[0-9]+$ ]]; then # Sadece sayı girildiğinden emin olalım (regex)
    echo "Lütfen geçerli bir sayı giriniz."
elif [ $SAYI -lt 0 ]; then
    echo "Girdiğiniz sayı negatif."
elif [ $SAYI -eq 0 ]; then
    echo "Girdiğiniz sayı sıfır."
elif [ $SAYI -gt 0 ] && [ $SAYI -le 100 ]; then # && (VE) operatörü
    echo "Girdiğiniz sayı 0 ile 100 arasında (0 hariç)."
else
    echo "Girdiğiniz sayı 100'den büyük veya negatif olmayan bir sayı."
fi

Mantıksal Operatörler

  • &&: VE (AND) operatörü. [ koşul1 ] && [ koşul2 ] veya [ koşul1 -a koşul2 ] (eski stil)
  • ||: VEYA (OR) operatörü. [ koşul1 ] || [ koşul2 ] veya [ koşul1 -o koşul2 ] (eski stil)
  • !: DEĞİL (NOT) operatörü. ! [ koşul ]

Alıştırma 6:

Komut satırından bir dosya veya dizin adı alan bir betik (dosya_kontrol.sh) yazın.

  • Eğer argüman verilmemişse “Lütfen bir dosya/dizin adı girin.” mesajı versin.
  • Eğer verilen isim bir dosyaysa “Bu bir dosyadır.” ve okuma/yazma izinlerini kontrol edip bilgi versin.
  • Eğer verilen isim bir dizinse “Bu bir dizindir.” mesajı versin.
  • Eğer verilen isim ne dosya ne de dizinse “Bulunamadı.” mesajı versin.

Döngüler: for Döngüsü

  • Bir listedeki her bir öğe için veya belirli bir sayıda komutları tekrarlamak için kullanılır.

1. Liste Üzerinde Gezinme:

#!/bin/bash

echo "Meyveler:"
for MEYVE in elma armut çilek muz; do
    echo " - $MEYVE"
done

echo
echo "Sayılar:"
for SAYI in 1 2 3 4 5; do
    echo "Sayı: $SAYI"
done

# Komut çıktısı üzerinde gezinme
echo
echo "Geçerli dizindeki .txt dosyaları:"
for DOSYA in $(ls *.txt); do # veya for DOSYA in *.txt (daha iyi)
    echo "Bulunan dosya: $DOSYA"
done

2. C-Stili for Döngüsü (bash’e özgü):

#!/bin/bash

echo "C-stili for döngüsü:"
for (( i=1; i<=5; i++ )); do
    echo "Döngü adım: $i"
done

Döngüler: while Döngüsü

  • Belirli bir koşul doğru olduğu sürece komutları tekrarlar.
  • Koşul döngünün başında kontrol edilir.
#!/bin/bash

SAYAC=1
LIMIT=5

echo "While döngüsü:"
while [ $SAYAC -le $LIMIT ]; do
    echo "Sayaç: $SAYAC"
    SAYAC=$((SAYAC + 1)) # Sayacı artırmayı unutmayın, yoksa sonsuz döngü!
done

# Kullanıcı 'çıkış' yazana kadar girdi alma
GIRIS=""
while [ "$GIRIS" != "çıkış" ]; do
    read -p "Bir komut girin (çıkmak için 'çıkış' yazın): " GIRIS
    echo "Girdiğiniz: $GIRIS"
done
echo "Program sonlandırıldı."

Alıştırma 7:

  1. 1’den 10’a kadar olan sayıların karelerini ekrana yazdıran bir for döngüsü yazın (kareler_for.sh).
  2. Kullanıcıdan pozitif bir sayı girmesini isteyen bir betik yazın (pozitif_sayi_while.sh). Kullanıcı negatif bir sayı veya sıfır girdiği sürece tekrar sayı istesin. Pozitif sayı girildiğinde “Teşekkürler, pozitif bir sayı girdiniz: [SAYI]” mesajını versin. (Bir while döngüsü kullanın).

Fonksiyonlar (Basit Giriş)

  • Kod tekrarını önlemek ve programı modüler hale getirmek için kullanılır.
  • Belirli bir işi yapan komut gruplarını bir isim altında toplar.

Tanımlama:

fonksiyon_adi() {
    # komutlar
    # yerel değişkenler tanımlanabilir: local degisken_adi="deger"
    # parametrelere $1, $2, ... ile erişilir (fonksiyona özel)
    # return ile sayısal bir çıkış durumu döndürülebilir (0-255)
}

veya

function fonksiyon_adi {
    # komutlar
}

Çağırma:

fonksiyon_adi arg1 arg2

Örnek

#!/bin/bash

selamla() {
    local ISIM=$1 # Fonksiyona gelen ilk parametre
    if [ -z "$ISIM" ]; then
        ISIM="Misafir"
    fi
    echo "Merhaba, $ISIM!"
}

# Fonksiyonu çağırma
selamla "Ayşe"
selamla "Mehmet"
selamla # Parametresiz çağrı

Alıştırma 8:

İki sayıyı parametre olarak alan ve bu sayıların toplamını ekrana yazdıran topla adında bir fonksiyon yazın. Betiğinizde bu fonksiyonu farklı sayılarla birkaç kez çağırın (fonksiyonlu_toplama.sh).

Çıkış Durumu (Exit Status): $?

  • Her komut çalıştıktan sonra bir “çıkış durumu” (exit status veya exit code) döndürür.
  • Bu durum, komutun başarılı olup olmadığını belirtir.
    • 0: Başarılı.
    • 0 dışında bir değer (genellikle 1-255): Hata oluştu.
  • Son çalıştırılan komutun çıkış durumu $? özel değişkeninde saklanır.

Örnek

#!/bin/bash

ls /root # Bu komut normal kullanıcı için hata verecektir (izin yok)
echo "ls komutunun çıkış durumu: $?" # Muhtemelen 1 veya 2

mkdir test_dizini
echo "mkdir komutunun çıkış durumu: $?" # Başarılıysa 0

rmdir test_dizini
echo "rmdir komutunun çıkış durumu: $?" # Başarılıysa 0

# Betik sonunda çıkış durumu belirleme: exit komutu
if [ -f "onemli_dosya.txt" ]; then
    echo "Dosya bulundu."
    exit 0 # Başarılı çıkış
else
    echo "HATA: onemli_dosya.txt bulunamadı!"
    exit 1 # Hatalı çıkış
fi

Çıkış durumları, betiklerin birbirleriyle veya sistemle iletişim kurması için önemlidir.

Alıştırma Soruları (Genel)

  1. Basit Yedekleme Betiği (yedekle.sh):
    • Komut satırından bir kaynak klasör ve bir hedef klasör alsın.
    • Kaynak klasörün var olup olmadığını kontrol etsin. Yoksa hata mesajı verip çıksın.
    • Hedef klasör yoksa oluştursun.
    • Kaynak klasörün içeriğini tar komutu ile sıkıştırarak (örn: kaynak_YYYY-AA-GG.tar.gz formatında bir isimle) hedef klasöre kopyalasın.
    • İşlem başarılı olursa “Yedekleme tamamlandı.” mesajı vers