Kabuk Programlama

ArticleCategory: [Es gibt verschiedene Artikel Kategorien]

UNIX Basics

AuthorImage:[Ein Bild von Dir]

[Photo of the Authors]

TranslationInfo:[Autor und Übersetzer]

original in en Katja and Guido Socher 

en to tr Ceyhun ELMAS ve Erdal MUTLU 

AboutTheAuthor:[Eine kleine Biographie über den Autor]

Katja LinuxFocus'un Alman editörüdür. Tux, film, fotografçılığı ve denizi sever. Ana sayfasına buradan ulaşabiliriz.

Guido uzun zamandan beri bir Linux hayranı ve Linux'u dürüst ve açık insanlar tarafından tasarlandığı için seviyor. İşte bu ona açık kodlu dememizin bir nedeni... Guido'nun sanaldoku sayfası: linuxfocus.org/~guido

Abstract:[Hier sollte eine kleine Zusammenfassung stehen]

Bu yazıda küçük bir kabuk betiğinin nasıl yazılacağı pek çok örnekle veriliyor.

ArticleIllustration:[Das Titelbild des Artikels]

[Illustration]

ArticleBody:[Der eigentliche Artikel. Überschriften innerhalb des Artikels sollten h2 oder h3 sein.]

Neden kabuk programlama ?

Linux için çok çeşitli grafiksel arayüzler sağlansa bile kabuk henüz en düzgün araçtır. Kabuk yalnızca bir komutlar yığını değil aynı zamanda çok iyi bir programlama dilidir. Pek çok işlemi bununla otomatik olarak yapabilirsiniz, özellikle sistem yönetimiyle ilgili konularda çok iyidir, Eğer tasarımınız doğruysa hemen deneyebilir ve kullanışlı basit prototipler oluşturabilirsiniz. Verimliliğin yapılandırmanın, bakımın ve uyumun kolaylığından daha az önemli olduğu basit işlemli küçük uygulamalar için çok kullanışlıdır.
Haydi nasıl çalıştığına bakalım:

Betiğin oluşturulması

Linux için pek çok kabuk bulunmaktadır fakat genellikle kabuk programlama için, ücretsiz ve kulanımı kolay olan bash (bourne again shell) kullanılır. Dolayısıyla bu yazıdaki tüm betikleri bash kullanarak yazdık (ancak zamanın çoğunda bu yaşlı kızkardeş bourne kabuğu ile koşacak).
Kabuk programlarımızı yazmak için her çeşit metin editörü kullanabiliriz, örneğin, nedit, kedit, emacs, vi... diğer programlama dillerinde olduğu gibi.
Program aşağıdaki satırla başlamalıdır.(bu , dosyadaki ilk satır olmalıdır):
    #!/bin/sh 
   
The #! karakterleri sisteme, bu karakterleri takip eden argumanın bu dosyayı çalıştırmakta kullanacağını söyler. Burada kullandığımız kabuk /bin/sh 'dır.
Bir betik yazdığınızda ve bunu kayıt ettiğinizde bu betiği kullanabilmek için çalıştırılabilir yapmalısınız.
Betiği bu şekilde çalıştırılabilir yapın:
chmod +x filename
Sonra betiği ./filename komutuyla çalıştırabilirsiniz.

Yorumlar

Betik programlarında yorumlar # ile başlar ve satırın sonuna kadar gider. Biz gerçekten yorum kullanmanızı öneriyoruz. Eğer betik içine yorumlar koyarsanız bu betiğe gereksinim duyduğunuz zamanlarda ne yaptığını ve nasıl çalıştığını zaman kaybetmeden anlamış olacaksınız.

Değişkenler

Diğer programlama dillerinde değişkenler olmadan yaşayamazsınız. Betik programlarında tüm değişkenler veritipi dizgisine sahiptir ve bunları yayınlama. Değişkene değer atamak şöyle olur:
degisken=deger
Değeri görmek için değişkenin önüne dolar işareti koymak yeterlidir:
#!/bin/sh
# Bir değer ata:
a="Merhaba Dünya"
# Şimdi "a" nın değerini yaz:
echo "a nın değeri:"
echo $a
Bu satırları metin editörünüzde yazın ve "first" ismiyle kayıt edin. Sonra chmod +x komutuyla betiği çalıştırılabilir yapın ve ./first komutuyla çalıştırın.
Betiğin çıktısı böyle olacaktır :
a nın değeri:
Merhaba Dünya
Bazen bu durum yazının diğer kısımlarıyla değişken isimlerinin karışmasına yol açabilir.
num=2
echo "this is the $numnd"
Bu "this is the 2nd" yazmayacaktır.Çıktı : "this is the " olacaktır. Çünkü betik numnd adı verilen değişkeni arayacak ve bir değere sahip olmadığını bulacaktır. Burada parantezleri kullanırsak :
num=2
echo "this is the ${num}nd"
Bu istediğimizi yazacaktır: this is the 2nd

Burada değişkenlerin sayısı herzaman otomatik olarak belirlenir Bunları ilk kullanışımızda tekrar ele alacağız.

Eğer matematiksel ifadelere gereksiniminiz varsa expr gibi programlar kullanmalısınız (aşagıdaki tabloda görünüyor).
Ayrıca betikte geçerli olan değişkenler aynı zamanda çevre değişkenleridir. Değişken açkısözcük tarafından önce alınır , çevre değişkenleri ihraç edilir. Bunlardan burada daha fazla sözetmeyeceğiz. Bunlar normalde yalnızca sistemgiriş betiklerinde kullanılır.

Kabuk komutları ve denetim yapıları

Kabuk betiklerinde kullanılacak üç sınıf komut vardır:

1)Unix komutları:
Kabuk betiklerinde her Unix komutunun kullanılabilmesine karşın, diğerlerine göre daha fazla kullanımı olan komutlar vardır. Bu komutlar genellkle dosya ve metin işlemleri ile ilgilidir.
Standart çıktı aygıtına (sizin ekranınız) veri yazmak. Genellikle şu şekilde kullanılır:
herhangikomut | tee çıktıdosyası
Komutun çıktısını ekrana ve dosyaya yazar.
Komut kullanımı (syntax) Amaç
echo "metin kısmı" Metin kısmını ekrana yazar.
ls Dosyaları listeler.
wc -l dosya
wc -w dosya
wc -c dosya
Dosyadaki stırları sayar veya
sözcükleri veya
karatkterleri sayar.
cp kaynakdosya hedefdosya Kaynakdosya'yı hedefdosya olarak kopyalar.
mv eskiad yeniad Dosya adını değiştirir veya dosyayı taşır.
rm dosya Dosya siler.
grep 'katar' dosya Dosya içerisinde katar arar.
Örnek: grep 'aranacak katar' dosya.txt
cut -b sütün dosya Sabit uzunluklu metin sütünlarından veri elde etme.
Örnek: 5 ile 9 arasındaki karakterleri elde eder.
cut -b5-9 dosya.txt
Bu komutu bam başka bir komut olan "cat" ile karıştırmayın.
cat dosya.txt dosya.txt'yi standart çıktı aygıtına yazar (sizin ekranınız).
file dosya Dosyanın tipi hakkında bilgi vermektedir.
read değişken Kullanıcıdan aldığı değeri değişken'e yazar.
sort dosya.txt dosya.txt'daki satırları sıralar(dizer).
uniq Tekrarlanan satırları siler. Sadece arka arkaya gelen ve tekrarlanan satırları sildiği için sort ile birlikte kullanılabilir.
Örnek: sort dosya.txt | uniq
expr Kabuk içerisinde matematiksel işlemler yapar.
Örnek: 2 ile 3 toplamak için
expr 2 "+" 3
find Dosya araması yapar.
Örnek: dosya ismine göre arama:
find . -name dosyaismi -print
Aslında bu komutun birçok kullanımı vardır ve hepsini bu yazıda anlatmak ne yazık ki olası değildir.
tee
basename dosya Yoltanımı(dizin) kısmını atarak sadece dosyanın adını üretir.
Örnek: basename /bin/tux
Sadece tux elde edilir.
dirname dosya Dosya adınını atarak sadece yoltanımını (dizin) üretir.
Örnek: dirname /bin/tux
Sadece /bin kısmı elde edilir.
head dosya Dosya başından itibaren birkaç satır yazar.
tail dosya Dosya sonundan itibaren birkaç satır yazar.
sed sed basit olarak bul ve değiştir programıdır. Ekrandan (veya borudan) okuduğu metinleri standart çıktı aygıtına (genelde ekran) yazar. Aranan metin düzenli ifadedir (kaynaklar kısmına bakınız). linuxfocus katarını LinuxFocus ile değiştirmek için :
cat metin.dosyası | sed 's/linuxfocus/LinuxFocus/' > yenimetin.dosyası
kullanabilirsiniz. Bu her satırdaki linuxfocus'ları LinuxFocus ile değiştirir. Eğer, aynı satırda birden fazla linuxfocus varsa, sadece ilkini değiştirir. Bir satırdan birden fazla aynı ifadeden varsa ve siz bunu değiştirmek istiyorsanız:
cat metin.dosyası | sed 's/linuxfocus/LinuxFocus/g' > yenimetin.dosyası
awk Genellikle awk bir satırdaki alanları elde etmek için kullanılmaktadır. Benimsenmiş değer olarak, alan ayracı, boşluk karakteridir. Başka bir alan ayracı belirtmek için -F seçeneği kullanılabilir.
 cat dosya.txt | awk -F, '{print $1 "," $3 }' 

Burada ayraç olarak virgül (,) karakteri kullanıldı ve ilk ve üçüncü ($1 $3) alanlar (sütünlar) elde edildi. Eğer, dosya.txt'nin satırları şu şekilde ise:
Adam Bor, 34, India
Kerry Miller, 22, USA

o zaman sonuç:
Adam Bor, India
Kerry Miller, USA

olur. Awk ile bundan çok daha fazlasını yapmak olasıdır. Bu sadece en çok kullanılan şeklidir.


2) Kavramlar: Borular, yönlendirme ve ters tırnak
Bunlar gerçekte komut, ancak aynı zamanda da çok önemli kavramlardır.

Borular (|) bir programın çıktısını (stdout) başka bir programın girdisi olarak vermektedir (stdin).
    grep "merhaba" dosya.txt | wc -l
dosya.txt dosyasındaki merhaba kelimesini içeren satırları bulmakta ve onları saymaktadır.
Grep komutunun çıktısı wc komutuna girdi olarak kullanılmıştır. Belli mantık sınırları içerisinde bu şekilde istediğiniz kadar komutu, borular ile arka arkaya kullanabilirsiniz.

Yönlendirme: bir komutun çıktısını bir dosyaya yazmaktadır veya dosyanın arkasına eklemektedir.
> çıktıyı bir dosyaya yazmaktadır. Eğer, dosya varsa, dosyanın üzerine yazmaktadır.
>> çıktıyı dosyanın arkasına eklemektedir (Dosya yoksa, yeni bir dosya yaratarak içerisine yazmaktadır, ancak hiçbir zaman dosya üzerine yazmamaktadır.).

Ters tırnak
Bir komutun çıktısı başka bir komuta parametre olarak (Bu yukarıdaki standart girdi (stdin) aygıtı gibi değildir, program isminden sonra verilen katarlar parametreleri oluşturmaktadır.) verilebilir. Ayrıca, bir programın çıktısını bir değişkene atamak için de kullanılabilir.
find . -mtime -1 -type f -print
Komutu, 24 saat içerisinde değiştirilmiş tüm dosyaları bulmaktadır. (-mtime -2 ise, 48 saat olura). Eğer, bütün bu dosyaları bir tar dosyası (dosya.tar) haline getirmek istiyorsanız :
tar xvf dosya.tar dosya1 dosya2 ...
Tüm dosyaları tek tek yazmaktansa, iki (find ve tar) komutu ters tırnak kullanarak birleştirebiliriz. Böylece, tar komutu, find komutunun bulduğu tüm dosyaları paketleyecektir:
#!/bin/sh
# Tırnaklar ters tırnaktır (`),  normal tırnak değil ('):
tar -zcvf ensondegisen.tar.gz `find . -mtime -1 -type f -print`

3) Denetim yapıları
"if" ifadesi bir ifadenin doğru olup olmadığını denetlemektedir (Çıkış durum bilgisi 0 ise, başarılı demektir). Eğer, doğru ise "then" kısmı çalıştırılmaktadır:
if ....; then
   ....
elif ....; then
   ....
else
   ....
fi
Genelde, if ifadesi, içerisinde test adı verilen bir komut kullanılmaktadır. Bu komut katar karşılaştırılmasında, dosyanın var olup olmadığının denetlenmesinde, dosyanın okuma hakkı olup olmadığını denetlenmesinde ... kullanılmaktadır.
"test" komutu " [ ] " köşeli parantezler olarak yazılmaktadır. Burada kullanılan boşluklar önemlidir : Parantezlerden önce ve sonra boşluk olmasına dikkat ediniz. Örnek:
[ -f "dosya" ]     : "dosya" nın dosya olup olmadığını denetler.
[ -x "/bin/ls" ]   : /bin/ls çalıştırılabilir bir dosya olup olmadığını denetler.
[ -n "$değisken" ] : $degisken'in herhangi bir değer taşıyıp taşımadığını 
                     denetlemektedir.
[ "$a" = "$b" ]    : "$a" nın  "$b" ye eşit olup olmadığını denetlemektedir.
"man test" komutunu çalıştırarak, tüm karşılaştırma ve dosya denetleme seçeneklerini elde edeceksiniz.
Bu kabuk programını kullanmak oldukça basittir:
#!/bin/sh
if [ "$SHELL" = "/bin/bash" ]; then
  echo "Giriş kabuğunuz bash (bourne again shell) dır."
else
  echo "Giriş kabuğunuz bash değil $SHELL dir"
fi
$SHELL değişkeni kullanılan kabuğun ismini içermektedir ve biz onu "/bin/bash" ile karşılaştırıyoruz.

Kısayol işlemcileri (operatörleri)
C ile tanışık olan kişiler aşağıdaki yapıyı hemen benimseyeceklerdir:
[ -f "/etc/shadow" ] && echo "Bu bilgisayar gölge şifrelemesi 
                                      (shadow passwords) kullanıyor."
&& ifadesi if-ifadesine kısayol gibi kullanılabilir. İfadenin sağ tarafı ancak sol tarafı doğru olduğunda çalıştırılmaktadır. Bunu VE (AND) gibi okuyabilirsiniz. Buna göre : "/etc/shadow dosyası var VE echo çalıştırılmaktadır". VEYA (||) işlemcisi de vardır. Örnek:
#!/bin/sh
eiletidizini=/var/spool/mail/james
[ -r "$eiletidizini" ] || { echo "$eiletidizini dizinini okuyamıyorum" ; exit 1; }
echo "$eiletidizini de eiletiniz var.:"
grep "^From " $eiletidizini
Bu kabuk betiği e-ileti dizinini okuyup okuyamadığna bakmaktadır. Eğer, okuyabiliyorsa, dosyadan "From" içeren satırları ekrana yazmaktadır. Eğer $eiletidizini dosyasını okuyamıyorsa, VEYA işlemcisi devreye girmektedir. Normal İngilizce'de program parçasını şu şekilde okuyoruz: "E-ileti dizini okunabilir veya çık.". Buradaki sorun, VEYA'dan sonra sadece bir komuta yer olmasıdır, ancak bizim birden fazla komuta gereksinimiz vardır:
-hata mesajı görüntüle
-programdan çık
Bu ikisini bir arada kullanabilmek için, bunları adı olmayan bir fonksiyon olarak grupluyoruz. Fonksiyonlar hakkında genel bilgi aşağıda verilmektedir.
Herşeyi VE veya VEYA'lar kullanmadan, sadece if-ifadelerinden yararlanarak da gerçekleştirebilirsiniz, VE ve VEYA'lar biraz daha kullanışlıdır.

case ifadesi bir katarı (kabuktaki * ve ? de kullanılabilir) birden fazla seçenekten birine uygun düşürmede kullanılmaktadır.
case ... in
...) burada birşeyler yap;;
esac
Bir örnek ele alalım. Bir dosyanın tipini fıle komutunu kullanarak öğrenebiliriz:
file lf.gz

sonuç:

lf.gz: gzip compressed data, deflated, original filename, 
last modified: Mon Aug 27 23:09:18 2001, os: Unix
Şimdi biz bu komutu smartzip adında, otomatik olarak sıkıştırılmış (uncompress, bzip2, gzip ve zip) dosyaları açan bir kabuk programı yazımında kullanacağız:
#!/bin/sh
ftype=`file "$1"`
case "$ftype" in
"$1: Zip archive"*)
    unzip "$1" ;;
"$1: gzip compressed"*)
    gunzip "$1" ;;
"$1: bzip2 compressed"*)
    bunzip2 "$1" ;;
*) error "File $1 dosyası smartzip ile açılamıyor.";;
esac

Eğer, dikkat ettiyseniz, burada özel bir değişken kullandık $1. Bu değişken, programa komut satırından verilen ilk parametreyi içermektedir. Programı :
smartzip yazilar.zip
gibi çalıştırırsak, $1'in alacağı değer yazilar.zip olacaktır.

select ifadesi bash kabuğuna özgüdür ve etkileşimli kullanımlarda oldukça yarar sağlamaktadır. Kullanıcı, kendisine verilen bir listeden birini seçebilmektedir:
select var in ... ; do
  break
done
.... şimdi $var kullanılabilir ....
İşte örnek:
#!/bin/sh
echo "En favori işletim sisteminiz hangisidir?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Diğer"; do
        break
done
echo "Seçiminiz $var dur."
Kabuk programı şunları gerçekleştirmektedir:
En favori işletim sisteminiz hangisidir?
1) Linux
2) Gnu Hurd
3) Free BSD
4) Diğer 
#? 1
Seçiminiz Linux dur."
Kabukta kullanabileceğiniz döngü ifadeleri şunlardır:
while ...; do
 ....
done
while-döngüsü denetlenen ifade doğru olduğu sürece çalışılacaktır. Döngünün herhangi bir noktasından çıkmak için "break" kullanılabilir. Döngünün herhangi bir yerinde "continue" kullanılırsa, döngünün geriye kalanı çalıştırılmamakta ve onun yerine döngünün başına dönülmektedir.

for-döngüsü boşluklar ile ayrılmış katar listesini parametre olarak almaktadır ve bunları sırasıyla ilgili değişkene atamaktadır:
for değişken in ....; do
  ....
done
Aşağıdaki örnekte A dan C'ye kadar olan karakterler ekrana yazmaktadır:
#!/bin/sh
for degisken in A B C ; do
  echo "değişkenin değeri $degisken"
done
Aşağıda daha kullanışlı bir örnek yer almaktdadır. Örnek, rpmgoster adını taşımakta ve parametre olarak verilen rpm paketleri hakkında bilgi elde etmekte ve ekranda göstermektedir:
#!/bin/sh
# RPM paketlerinin içeriğini listeler
# KULLANIM: rpmgoster rpmdosya1 rpmdosya2 ...
# ÖRNEK: rpmgoster /cdrom/RedHat/RPMS/*.rpm
for rpmpaketi in $*; do
  if [ -r "$rpmpaketi" ];then
    echo "=============== $rpmpaketi =============="
    rpm -qi -p $rpmpaketi
  else
    echo "HATA: $rpmpaketi dosyası okunamadı."
  fi
done
Yukarıdaki örnekte komut satırından verilen ve tüm parametreleri içeren $* değişkenini görüyorsunuz. Programı,
rpmgoster openssh.rpm w3m.rpm webgrep.rpm
olarak çalıştırırsak, $* değişkeni 3 parametreyi birlikte içerecektir: openssh.rpm, w3m.rpm ve webgrep.rpm.

GNU bash ayrıca until-döngüsüne de sahiptir, ancak while ve for döngüleri genellikle yeterli olmaktadır.

Tırnak içine alma
Programa herhangi bir parametre göndermeden önce, kabuk joker (*, ?) karakterleri ve değişkenleri kullanmaya ve yerleştirmeye çalışmaktadır. Kullanmak ve yerleştirmeden kasıt, joker (*, ? gibi) karakterlerin yerine uygun dosya adları konulması ve değişkenlerin yerine taşıdıkları değerler ile değiştirilmesidir. Bu uygulamayı değiştirmek için tırnak kullanabilirsiniz: Sözgelimi, bulunduğumuz dizinde yer alan dosyalar arasında eileti.jpg ve tux.jpg jpg dosyaları da olsun.
#!/bin/sh
echo *.jpg
Bu ekrana "eileti.jpg tux.jpg" yazacaktır.
Bunu önlemek için tek veya çift tırnak kullanabiliriz:
#!/bin/sh
echo "*.jpg"
echo '*.jpg'
Bu ise, ekrana "*.jpg" ifadesini iki kez yazmaktadır.
Tek tırnaklar daha kısıtlayıcıdır. Tek tırnak kullanıldığında, değikenlerin yerine değerlerinin konulması bile engellenmektedir:
#!/bin/sh
echo $SHELL
echo "$SHELL"
echo '$SHELL'
Sonuç aşağıdaki gibi olacaktır:
/bin/bash
/bin/bash
$SHELL
Son olarak, özel anlam taşıyan herhangi bir karakteri yazmak için önüne ters bölü çizgisi kullanabiliriz:
echo \*.jpg
echo \$SHELL
dolayısıyla:
*.jpg
$SHELL
elde edilir.

Bilgilendirme yeri
Bilgilendirme yeri, birden fazla satırdan oluşan metni bir komuta göndermek istediğinizde kullanılabilir. Kabuk programlarınızda yardım metni yazarken her satırın başına echo komutunu kullanmadan yazabilmek oldukça kullanışlı olmaktadır. "Bilgilendirme yeri" << ve metnin sonunda da yazılması gereken bir başlık ile başlamaktadır. ren adındaki örnek kabuk programı, yardım metni için bilgilendirme yerinden faydalanmaktadır ve yaptığı iş ise, birden fazla dosya ismini değiştirmmektir:
#!/bin/sh
# Eğer, 3'ten daha az paramtre verilmişse, yardım mesajı görüntülenecektir:
if [ $# -lt 3 ] ; then
cat <<YARDIM
ren -- birden fazla dosyanın adını sıradan ifadeler kullanarak değiştirmektedir.

KULLANIMI: ren 'sıradanifade' 'yerinekonulacakifade' dosyalar...

ÖRNEK: tüm *.HTM dosyalarını *.html olarak değiştirmek için:
  ren 'HTM$' 'html' *.HTM

YARDIM
  exit 0
fi
ESKI="$1"
YENI="$2"
# shift komutu, komut satırından verilen parametre listesinden 
# bir adet parametreyi silmektedir
shift
shift
# Şimdi $* tüm dosyaları içermektedir:
for dosya in $*; do
    if [ -f "$dosya" ] ; then
      yenidosya=`echo "$dosya" | sed "s/${ESKI}/${YENI}/g"`
      if [ -f "$yenidosya" ]; then
        echo "HATA: $yenidosya zaten var"
      else
        echo "$dosya sı $yenidosya olarak değiştirilmektedir ..."
        mv "$dosya" "$yenidosya"
      fi
    fi
done
Bu şu ana kadar incelediğimiz en karmaşık betiktir. Bunun üzerinde biraz konuşalım. İlk if-ifadesi, komut satırından verilen parametre sayısının en azından 3 adet olup olmadığını denetlemektedir. (Özel bir değişken olan $# de parametre sayısını tutmaktadır.) Eğer, parametre sayısı yeterli değil ise, yardım mesajı cat komutuna, o da ekrana göndermektedir. Yardım mesajı görüntülendikten sonra programdan çıkılmaktadır. Eğer, 3 veya daha fazla parametre verilmiş ise, ilki ESKI, ikincisi de YENI değişkenlerine atanmaktadır. Daha sonra, shift komutu iki defa kullanılarak 3. paramtreyi ilk parametre olarak elde etmek için kaydırılmaktadır. $* daki her parametre tek tek $dosya değişkenine atanmaktadır. İlk önce dosyanın var olup olmadığı denetlenmektedir ve ondan sonra yeni dosya adı eskisi üzerinde sed komutuyla yapılan değiştirme ile elde edilmektedir. Ters tırnaklar, yeni dosya isminin yenidosya değişkenine atamak için kullanılmıştır. Artık gereksinim duyduğumuz herşeye sahibiz: eski dosya adına ve yenisine. Bu ikisi mv komutuna parametre olarak verilerek, eski dosya ismi yenisi ile değiştirilmektedir.

Fonksiyonlar
Daha karışık programlar yazmaya başlar başlamaz, aynı program parçalarını birden fazla yerde kullandığınızı fark ediyorsunuz. Bunun için fonksiyonlardan yararlanabiliriz. Bir fonksiyon aşağıdaki gibi gözükmektedir:
fonksiyonismi()
{
 # Fonksiyon gövdesinde (içerisnde) $1, fonksiyona verilen ilk parametre
 # $2 ikinci vs.
 fonksiyonun gövdesi
}
Fonksiyonu kullanmadan önce, programın başında fonksiyonu "tanımlamak" gerekmektedir.

Konsole penceresinin başlığını değiştirmek için xtitlebar adlı program kullanılabilir. Eğer, aynı anda birden fazla konsole kullanıyorsanız, başlıklarından hangisinin ne olduğunu kolayca anlayabilirsiniz. Konsole'un ismi, kendisine gönderilen kaçış kodu (escape sequence) ile değiştirilmektedir. Betik yardim adinda bir fonksiyon kullanmaktadır. Görüldüğü üzere, yardim fonksiyonu bir defa tanımlanmış ve iki yerde kullanılmıştır:
#!/bin/sh
# vim: set sw=4 ts=4 et:

yardim()
{
    cat <<YARDIM
xtitlebar -- xterm, gnome-terminal veya kde konsole un adı değiştirir.

KULLANIMI: xtitlebar [-h] "başlık"

SEÇENEKLER: -h yardim metin

ÖRNEK: xtitlebar "cvs"

YARDIM
    exit 0
}

# Hata durumunda veya -h parametresi verildiğinde yardim 
# fonksiyonunu çağırmaktayız:
[ -z "$1" ] && yardim
[ "$1" = "-h" ] && yardim

# konsole'un başlığını değiştirmek için kaçış kodu gönder:
echo -e "\033]0;$1\007"
#
Yazdığınız programlarda geniş yardım bilgilerine yer vermeniz iyi bir alışkanlıktır. Bu size ve başkalarına programı kullanırken oldukça faydalı olmaktadır.

Komut satırı parametreleri
$* ve $1, $2 ... $9 komut satırından verilen parametreleri (programın adından sonra gelen veriler) içerdiklerini görmüştük. Şu ana kadar az sayıda ve oldukça basit yapıda parametre kullandık (Bir iki gerekli parametre ve -h yardım için). Ancak, çok geçmeden, komut satırından verilen parametreleri işleyecek bir işlemciye gereksinim duyacaksınız. Genelde, seçime bağlı tüm parametrelerin başı eksi işaretini koymak ve bunların diğer parametrelerden (dosya adı vs.) önce gelmesi alışılagelmiş bir uygulamadır.

Parametre işlemcisi oluşturmanın birçok yolu vardır. Aşağıdaki while döngüsü ve case ifadesi genel bir parametre işlemci için çok uygun bir çözümdür:
#!/bin/sh
yardim()
{
  cat <<YARDIM
Bu genel bir parametre işlemcisidir. 
ÖRNEK KULLANIM: cmdparser -l merhaba -f -- -dosya1 dosya2
YARDIM
  exit 0
}

while [ -n "$1" ]; do
case $1 in
    -h) yardim;shift 1;; # yardım fonksiyonu çağrıldı
    -f) opt_f=1;shift 1;; # opt_f değişkenine değer atandı
    -l) opt_l=$2;shift 2;; # -l parametresi değer almaktadır,  
       -> shift ile 2 kaydırıldıktan sonar
    --) shift;break;; # seçenekler bölümünün sonu
    -*) echo "hata: $1 seçeneği tanımlı değildir. yardım için -h";exit 1;;
    *)  break;;
esac
done

echo "opt_f nin değeri $opt_f"
echo "opt_l nin değeri $opt_l"
echo "ilk parametre $1"
echo "ikinci parametre $2"
Bunu bir deneyin! Programı aşağıdaki gibi çalıştırabilirsiniz:
cmdparser -l merhaba -f -- -dosya1 dosya2
sonuç
opt_f nin değeri 1
opt_l nin değeri merhaba
ilk parametre -dosya1
ikinci parametre dosya2
Bu nasıl çalışmaktadır? Döngü içinde tüm parametreler tek tek case ifadesindekilere uyup uymadığı karşılaştırılmaktadır. Uyuan bir parametre bulunduğunda, değeri ilgili değişkene atanmakta ve bir kaydırılmaktadır. Unix'te, seçimler (- eksi işareti ile başlayanlar) önce gelmektedir. İki eksi (--) çizgisiyle seçimlerin bittiğini belirtebilirsiniz. Buna, eksi ile başlayan bir katarı grep ile ararken gereksiminiz olacaktır:
f.txt dosyasında -xx- arama:
grep -- -xx- f.txt
Bizim parametre işlemcimiz, yukarıda da görüldüğü gibi, -- ile başlayan parametreleri de işleyebilmektedir.

Örnekler

Genel amaçlı program iskeleti

Bir kabuk programı yazmak için gerekli olan tüm bileşenleri inceledik. Tüm iyi programlarda yardım bulunmalıdır ve seçenekleri işlemek, tek bir seçenek olsa bile, için genel amaçlı şeçenek işlemcimizden yararlanabilirsiniz. Dolayısıyla, başka programların yazımında temel alabileceğiniz framework.sh, bir çerçeve programınızın olması, hiç de fena fikir değildir. Yeni bir program yazacağınız zaman, bundan bir kopya çıkartmanız yeterlidir:
cp framework.sh benimprogram
ve bundan sonra "benimprogram" adlı programda gerekli değişiklikler yapılabilir.

Şimdi şu iki programı inceleyelim:

İkili sayı tabanda verilen bir sayısı, onlu tabana çeviren program

b2d programı, ikili sayı tabanında verilen bir sayıyı (1101 gibi) karşılığı olan onlu tabana çevirmektedir. Bu program expr kullanarak basit matematiksel işlemlerin nasıl yapılabileceğiniz göstermektedir:
#!/bin/sh
# vim: set sw=4 ts=4 et:
yardim()
{
  cat <<YARDIM
b2h -- ikili sayı sisteminde verilen bir sayıyı, onlu sayı sisteme çevirir

KULANIM: b2h [-h] ikilisayi

SEÇENEKLER: -h yardım metin

ÖRNEK: b2h 111010
için sonuç 58 olur
YARDIM
  exit 0
}

hata()
{
    # hata mesajını görüntüle ve çık
    echo "$1"
    exit 1
}

sonkarakter()
{
    # $rval değişkeni içerisine, verilen katarın son karakterini yerleştirir
    if [ -z "$1" ]; then
        # boş katar
        rval=""
        return
    fi
    # wc, çıktının sonuna biraz boşluk koymaktadır, 
    # sed'e gereksinim duymamızın nedeni budur:
    karaktersayisi=`echo -n "$1" | wc -c | sed 's/ //g' `
    # şimdi son karakteri elde et
    rval=`echo -n "$1" | cut -b $karaktersayisi`
}

kes()
{
    # verilen bir katarın son karakterini kesip, geriye kalanı 
    # $rval içine yerleştirir
    if [ -z "$1" ]; then
        # boş katar
        rval=""
        return
    fi
    # wc, çıktının sonuna biraz boşluk koymaktadır, sed'e gereksinim 
    # duymamızın nedeni budur:
    karaktersayisi=`echo -n "$1" | wc -c | sed 's/ //g' `
    if [ "$karaktersayisi" = "1" ]; then
        # katarda sadece bir karakter var
        rval=""
        return
    fi
    karaktersayisieksi1=`expr $karaktersayisi "-" 1` 
    # son hariç hepsini al:
    rval=`echo -n "$1" | cut -b 0-${karaktersayisieksi1}`
}
    

while [ -n "$1" ]; do
case $1 in
    -h) yardim;shift 1;; # yardım fonksiyonu çağırıldı
    --) shift;break;; # seçeneklerin sonu
    -*) hata "hata: böyle bir seçenek yok. Yrdım için $1. -h ";;
    *)  break;;
esac
done

# Programın ana kısmı
toplam=0
agirlik=1
# bir parametre mutlaka verilmelidir:
[ -z "$1" ] && yardim
ikilisayi="$1"
orjikilisayi="$1"

while [ -n "$ikilisayi" ]; do
    sonkarakter "$ikilisayi"
    if [ "$rval" = "1" ]; then
        toplam=`expr "$agirlik" "+" "$toplam"`
    fi
    # $ikilisayi'daki son karakteri çıkart
    kes "$ikilisayi"
    ikilisayi="$rval"
    agirlik=`expr "$agirlik" "*" 2`
done

echo "İkili sayı sistemindeki $orjikilisayi nın 
      onlu sayı sistemindeki değeri $toplam dır"

Kullanılan algoritma, sağ taraftan başlayarak, her basamaktaki sayının ağırlığını (1,2,4,8,16,..) kullanarak, eğer ilgili basamaktaki sayı 1 ise, toplam değişkenine eklemektedir. Sözgelimi "10", 0 * 1 + 1 * 2 = 2 dir.
Katarın her bir basmak sayısını elde etmek için sonkarakter fonksiyonunu kullanıyoruz. Bu fonksiyon, katardaki toplam karakter sayısını bulabilmek için wc -c komutundan yararlanmaktadır. Daha sonra da, son karakteri elde etmek için cut komutundan yararlanmaktadır. kes fonksiyonu da benzer bir mantıkla çalışmaktadır, ancak yaptığı iç katardaki sona karakterden kurtulup, ona kadar olanı elde etmektir.

Dosya çevirme programı
Belkide siz, göndermiş olduğunuz tüm e-iletilerinizi bir dosyada saklayanlardansınızdır. Birkaç aydan sonra bu dosyanın boyutu oldukça büyümekte ve e-ileti programıyla dosyaya olan erişimi yavaşlatnmaktadır. rotatefile programı size yardımcı olabilir. Program, eileti dizininizin adını, adı gideneileti olsun, gidenelileti.1, gidenelileti.2 vs. olarak değiştirmektedir.
#!/bin/sh
# vim: set sw=4 ts=4 et: 
surum="0.1"
yardim()
{
    cat <<YARDIM
rotatefile -- dosya adını çevirir 

KULLANIM: rotatefile [-h]  dosyaadi

SEÇENEKLER: -h yardim metin

ÖRNEK: rotatefile giden
Bu program giden.2 yi giden.3, giden.1 i giden.2, giden i  giden.1 olarak
değiştirerek boş bir giden dosyası yaratacaktır

En fazla 10'a kadar değiştirmektedir.

sürüm $surum
YARDIM
    exit 0
}

hata()
{
    echo "$1"
    exit 1
}
while [ -n "$1" ]; do
case $1 in
    -h) yardim;shift 1;;
    --) break;;
    -*) echo "hata: böyle bir seçenek yoktur $1. Yardım için -h";exit 1;;
    *)  break;;
esac
done

# girilen verileri denetleme:
if [ -z "$1" ] ; then
 error "HATA: parametre olarak dosya vermeniz gerekmektedir, 
        yardım için -h kullanınız" 
fi
dosyan="$1"
# .1 , .2 vs. dosyalrını adını değiştir:
for n in  9 8 7 6 5 4 3 2 1; do
    if [ -f "$dosyan.$n" ]; then
        p=`expr $n + 1`
        echo "mv $dosyan.$n $dosyan.$p"
        mv $dosyan.$n $dosyan.$p
    fi
done
# orijinal dosyayı:
if [ -f "$dosyan" ]; then
    echo "mv $dosyan $dosyan.1"
    mv $dosyan $dosyan.1
fi
echo touch $ya
touch $dosyan

Bu nasıl çalışmaktadır? Kullanıcının parametre olarak dosya verdiğini denetledikten sonra, program, 9'dan 1'e doğru bir döngü içerisine girmektedir. Dolayısıyla, 9. dosya 10, 8. dosya 9 vs. olmaktadır. Döngü bittikten sonra, orijinal dosyayi 1 olarak adını değiştirdikten sonra orijinal dosya adında boş bir dosya yaratılmaktadır.

Hata ayıklama

Hata ayıklamada kullanılan en basit yöntem echo komutunu kullanmaktır. Bu komutu hatanın olabileceğini düşündüğünüz yerde kullanabilirsiniz. Bu belkide hata bulmada, kabuk programları yazan kişilerin %80'ninin kullandığı yöntemdir. Kabuk programlarının avantajı derlemeye gereksinim duymamaları ve bu yüzden echo komutunu eklemek uzun sürmemektedir.

Kabukta, gerçek hata ayıklama seçeneği de vardır. Eğer, "garipbetik" adlı programda hata varsa, hatayı:
sh -x garipbetik
kullanarak bulabilirsiniz.
Programı bu şekilde çalıştırdığınızda, çalıştırılan tüm komutları, değişkenlerin değerleri ve joker karakterler (*,? gibi) varsa, yerlerine karşılıklarını yerleştirerek görüntülemektedir.

Ayrıca, kabukta, programı çalıştırmadan, yazım hatalarını denetleyen seçeneği de vardır. Bunun için:
sh -n sizin_programiniz
Eğer, sonuçta hiç bir şey görüntülenmiyorsa, yazılım hatası yok demektir.

Bundan sonra kabuk programları yazmaya başlmanız dileği ile. İyi eylenceler!

Kaynaklar