Bash Kabuk Programlama Dersleri 1 (Giriş)
Merhaba.
Bash Kabuk programlama derslerine başlıyoruz.Dersin hedef kitlesi Linux hakkında temel düzeyde bilgisi olan programlama bilen kişilerdir.Bu yüzden Kabuk,Kernel,Donanım gibi konuları anlatmayacağım,ayrıca kabuk çeşitlerinden de bahsetmeyeceğim.Bu konularda bilgi eksiği olanlar kısa bir araştırmayla yeterli bilgiye ulaşacaklardır.Bizim burada temel alacağımız Bash (Bourne Again Shell) kabuğudur fakat kabuklar birbirine benzemektedir.Özellikle Bash ve sh kabukları oldukça yakındır birbirine.Burada öğreneceğimiz bilgileri başka kabuklarda da kullanmak zor olmayacaktır.
Bash Kabuk programlama derslerine başlıyoruz.Dersin hedef kitlesi Linux hakkında temel düzeyde bilgisi olan programlama bilen kişilerdir.Bu yüzden Kabuk,Kernel,Donanım gibi konuları anlatmayacağım,ayrıca kabuk çeşitlerinden de bahsetmeyeceğim.Bu konularda bilgi eksiği olanlar kısa bir araştırmayla yeterli bilgiye ulaşacaklardır.Bizim burada temel alacağımız Bash (Bourne Again Shell) kabuğudur fakat kabuklar birbirine benzemektedir.Özellikle Bash ve sh kabukları oldukça yakındır birbirine.Burada öğreneceğimiz bilgileri başka kabuklarda da kullanmak zor olmayacaktır.
Bash Kabuk programlama, bir programlama dili kadar kuvvetli olmasa da oldukça yeteneklidir.Sistemin ayağa kaldırılmasından,dosya,disk işlemleri, bir çok programın setup'ına kadar geniş bir kullanımı vardır.Üstelik bir programlama dilinde olan değişkenler,diziler,karar yapıları,döngüler,fonksiyonlar gibi bir çok yapı mevcuttur.
Bash kabuğu herhangi bir Linux dağıtımı altında kullanılabildiği gibi openssh temelinde mobil aygıtlarda,putty programıyla windowsta kullanılabilmektedir.Ayrıca Windows 10 öncesinde cygwin kütüphanesiyle windows 10'da Ubuntu on Windows özelliği aktifleştirilerek windows üzerinde de kullanılabilmektedir.Üstelik Bash kabuğu dos kabuğuna göre çok daha yeteneklidir.
Biz bu derslerde Bash Betiğine (shell script) bash programı diyeceğiz.Bir bash programının tepesinde #!/bin/bash ifadesi yer almalıdır.Bu çalışacak programın bash kabuğunda çalışacağını gösterir.Varsayılan kabuk bash ise bu ifade eksik olsa da sorun olmayacaktır ama varsayılan bash olmadığında sorun olacağı için bash programının en üstünde yer almalıdır çünkü başka bir kabuk altında bu ibare görüldüğünde sistemde bash kabuğu varsa program(script) bash kabuğu altında çalıştırılacaktır.Terminele "which bash" yazdığınızda aynı çıktıyı alırsıınız.
Linux'ta "touch <dosya_adi>" komutu dosya oluşurmak için kullanılır.touch komutuyla dosya oluşturup herhangi bir text editörüyle dosyayı açabilceğiniz gbi bir çok yöntemle script dosyası oluşturabilirsiniz.Terminal üzerinde vi yada nano editörü kullanılabilir ki ben nano'yu kullanacağım.nano <dosya_adi> diyerek daha sonra ctrl x ve E tuşuna basarak dosyayı kaydederek de aynı sonucu alabileceğiniz gibi aşağıdaki yöntemleri de kullanabilirsiniz.
which bash > dosya_adi.sh
. echo '#!/bin/bash' > dosya_adi.sh
cat > dosya_adi.sh #enter tuşuna bastıktan sonra
'#!/bin/bash'
vs. bir çok yöntemle yazmaya başlanabilir
.
Bash kabuğunda # başlayan satırlar yorum satırıdır dikkate alınmaz.
Bash kabuğunda # başlayan satırlar yorum satırıdır dikkate alınmaz.
Bir bash programı iki şekilde çalıştırılabilir.Birincisi terminalde dosyanın bulunduğu dizinde "chmod +x dosya_adi.sh" komutundan sonra ./dosya_adi.sh şeklinde çalışırılır ki C'de yada başka bir dilde yazdığımız programı da aynı şekilde çalıştırırız.Ayrıca dosyamızın bulunduğu dizin PATH değişkeni içerisinde tanımlıysa doğrudan dosya_adi.sh şeklinde de çalıştırabiliriz.İkincisi terminalde yine dosyanın bulunduğu dizinde "bash dosya_adi.sh" şeklinde de çalıştırılabiliriz.
Başlamadan birkaç hatırlatma
1. Terminalde dosya adlarının arasında boşluk olmasını istiyorsanız "\" işaretini boşluktan önce kullanınız.
2. Dosya yada komut adlarının kabuk tarafından tamamlanması için tab tuşuna basınız.
3. Her komut aslında bir programdır./bin,/sbin/,/usr/bin/ dizinlerini incelerseniz görebilirsiniz.
4. Linuxta dosyanın uzantısı olmak zorunda değildir.Uzantısız olarak oluşturduğunuz dosya varsayılan olarak text tabanlıdır.
5 ."~" ifadesi Kullanıcının home dizinini gösterir. ~/.bashrc dosyası, kullanıcının açtığı kabuk öncelikle bu dosyayı okur.Bu dosya üzerinde kendimizce değişiklikler yapabiliriz.PS1 değişşkeni üzerinde oynama yaparak giriş satırımızı değiştirebiliriz."alias" komutuyla kendimizce komutlar oluşturabiliriz.Örneğin alias cls="clear" tanımlamasıyla terminalde dosdaki "cls " komutu gibi ekranı temizleyebiliriz.Yada bu tanımlamaların hepsini ayrı bir dosyada yapıp .bashrc içine "source dosya_adi.sh" ifadesini yazabiliriz
6. ">", ">>" ifadeleri ekrana verilen çıktıyı bir dosyaya yönlendirmemizi sağlar ">" ifadesi dosyayı sıfırdan yazar," >>" ifadesi
dosyanın sonuna ekleme yapmamızı sağlar.
7.Giriş satırındaki "$" işareti normal kullanıcıda olduğunuzu,"#" işareti root kullanıcıda olduğunuzu gösterir..Mecbur kalmadıkça root kullanıcıya geçmeyiniz,ihtiyaç duyduğunuzda komutunuzun başına "sudo" ifadesini getirerek komutunuzu root hakkıyla kullanabilirsiniz.
8.Linux'un "cd,ls,cat,touch,mkdir,pwd,chmod,cp,mv,rm..." gibi temel komutları bildiğiniz varsayılmaktadır.Eğer bilmiyorsanız.Google'da Temel Linux komutları diyerek yapacağınız basit bir aramayla yeterli bilgiye ulaşabilirsiniz.
GİRİŞ
echo Komutu:1.Ekrana yada istediğimiz bir dosyaya çıktı vermemizi sağlar.
örnek 1:
ders1Ornek1.sh
#!/bin/bash echo "Bash Programlıyorum"
2. echo komutunu "" ifadesi ile çağırırsak içindeki değişkenleri dikkate alır,'' ifadesi ile çağırırsak tamamını text olarak kabul eder.
örnek 2:
ders1Ornek2.sh
#!/bin/bash degisken1=55 echo " Bu değişken=$degisken1" #değişkenlerin değeri $ ifadesiyle alınır. echo 'Bu değişken= $degisken1'
3. İlerde fonksiyonları göreceğiz ama programlama bilgisine dayanarak şunu belirtebiliriz.Bash'ın fonksiyonlar için bir return bildirimi olmasına rağmen , onunla belirtebileceğiniz tek şey,
fonksiyonun kendi çıkış durumudur (0 ile 255 arasında bir değer, 0 "başarı" anlamına gelir).Bu yüzden dönüş değerini echo ile döndürebiliriz.
örnek 3:
ders1Ornek3.sh
#!/bin/bash carp() { a=$1 #birinci parametre b=$2 #ikinci parametre let c=a*b echo $c } echo $(carp $1 $2)
./ders1Ornek3.sh 25 3 şeklinde çağırdığınızda 75 sonucunu görürsünüz.
Gelecek ders "Değişkenler" konusunda görüşmek üzere...
DERS 2
Başlamadan birkaç önbilgi
1) “. “ ifadesi başka bir bash dosyasını import etmemizi sağlar
2) “export” ifadesi değişkenin çalıştığı kabuk ve alt kabuklarda tanınmasını sağlar,bir nevi değişkeni globalleştirir.Bash’te bir scripti çalıştırdığımızda yeni bir kabuk açılır program çalışır ve kabuk kapanır.Dolayısıyla programı çalıştırdığımız kabuk, komutu başlattığımız kabuğun alt kabuğudur, ayrıntı için fork fonksiyonunu inceleyebilirsiniz.Ama unutmayın,export ifadesi ana kabukta kullanılmış ise alt kabuklar değişkeni tanır; ama tersi durumda alt kabukta kullanıldığı bir değişkeni ana kabuk yada diğer kardeş alt kabuklar tanımaz.
3) ”eval” komutu verilen string bir ifadenin kabukta çalışmasını sağlar.Ör: eval “a=5 && echo $a” ekranda 5 yazar.Kabukta tekrar echo $a yazdığımızda değişkenimiz ordadır ve yine 5 yazar.
Değişkenler
Bildiğimiz gibi değişkenler veri tutmaya yarar, dolayısıyla da her değişkenin bir veri tipi vardır.
Bütün programlama dillerindeki veri tiplerini sınıflayacak olursak 5 ana başlık altında toplayabiliriz.
1.Karakter Tabanlı Veri Tipleri: String ,char vs.
2.Sayı Tabanlı Veri Tipleri: int,float,double,vs.
3.Karar Tabanlı Veri Tipleri: bool vb.
4.Dizi ve Pointer Tabanlı Veri Tipleri
5.Kullanıcı Tabanlı Veri Tipleri:Kendi oluşturduğumuz nesnelerle oluşturduğumuz değişkenler.
Bu sınıflamada karakter tabanlı veri tiplerinin diğerlerinden farklı bir özelliği vardır.Bilindiği gibi bütün veri tipleri bu veri tiplerine dönüşebilir, yada bu veri tiplerini diğer tiplere dönüştürebiliriz.Daha özeline inerek diyebiliriz ki string(karakter dizisi) bütün veri tiplerini içinde taşıyabilmektedir.Özellikle ekrana bir şey yazdıracağımızda yada ekrandan okuma yapacağımızda başvuracağımız veri tipi stringtir.Dolayısıyla biz de bash kabuğunda (siyah ekranda ) çalıştığımız için aksi belirtilmedikçe (declare – i,typeset -i gibi) -dizileri dışarda tutarak diyebiliriz ki -bütün değişkenlerin veri tipleri stringtir.
Bash ‘ta değişkeni veri tipi belirtmeden doğrudan belirtiriz ki zaten yukarda anlattığımız gbi gerek de yoktur.Bir değişkenin değerini okurken $ işaretini kullanırız.
Ders2Ornek1.sh
#!/bin/bash a=5 b=ahmet c=”mehmet” d=’ali’ echo $a $b $c $d b=5 a=ahmet c=3.4 echo $a $b $c
Örnekte de görüldüğü gibi aynı değişkene farklı değerleri atayabiliyoruz ama bu değerlerin hepsinin veri tipi stringtir.Bunu anlamak için aşağıdaki örneği yapabiliriz.
Örnek:
Ders2Ornek2.sh
#!/bin/bash a=5 b=5 echo Toplam=$a+$bEğer bu değişkenler string değil de integer ise sonucun 9 olması lazım,ama sonuc “5+4”dür yani string bir ifadedir.Fakat bu değerler istendiği zaman çeşitli komutlar ya da anahtar kelimelerle istenen veri tiplerine dönüştürülebilir.
typeset,declare,local
typeset ve declare aynıdır local ise sadece fonksiyonlar içinde kullanılabilir.Aldıkları parametreler şunlardır:
a Dizi tanımlama.
-f Fonksiyon adları için kullanılır.
-i İnteger değişken tanımlama.
-p Değişkenlerin niteliklerini, tanımlanma şeklini,kriterlerini gösterme.
-r Değişkenin salt okunur (readonly) yani değerini sabit kılma.
-t Değişkenlere trace(iz) niteliği verme.
-x Her değişkeni diğer komutlar için işaretleme.
Örnek:
Ders2Ornek3.sh
#!/bin/bash declare -i a=25 echo $a #25 yazar a=”string” # integer bir değişkene string bir değer atanıyor echo $a # 0 yazar çünkü değişkenin veri tipi integer’dır declare -p a # declare -i a="0" yazacaktır.Yani değişkenin açıklmasını yaparak,değişken integerdır ve değeri şudur demektedir.local ile de aynı parametreleri fonksiyon içinde kullanabiliriz.local tanımlaması,başka dillerden de hatırlayacağımız gibi değişkenin fonksiyon dışına çıkmamasını sağlar.Fakat declare ve typeset ile de fonksiyon içinde tanımladığımız değişken dışarı çıkamaz.
Örnek:
ders2Ornek4.sh
#!/bin/bash function f1() { echo “Fonksiyon içindeyiz” tiptanimsiz=2 typeset -i a=25 declare -i b=35 local -i c=45 echo $tiptanimsiz echo $a echo $b echo $c } f1 echo “Dışardayız” if [ -z $tiptanimsiz ]; then echo tiptanimsiz tanımlı değil else echo $tiptanimsiz fi if [ -z $a ]; then echo a tanımlı değil else echo $a fi if [ -z $b ]; then echo b tanımlı değil else echo $b fi if [ -z $c ]; then echo c tanımlı değil else echo $c fi
let, expr,(( ... ))
Yukardaki ifadelerin hepsi aritmetik işlem yapmamızı sağlar.expr komutunun temel işlevi artitmetik işlemler olmasa da böyle bir işlevi de vardır. (( ...)) ifadesi aritmetik işlemler yanında C tarzı ifadelerin kullanılmasını sağlar.Örneğin bu ifade sayesinde for((i=0;i<10;i++)) şeklinde for döngüsünü kullanabiliriz.Bu ifadeyi string içerikli bir değerle kullanırken içeriği sayısal olarak değerlendireceği için hata alınabilir, bu yüzden kullanırken dikkatli kullanmalıyız.Örneğin “if((“ali”==”veli”))” ifadesi içeriklerini sayısal olarak 0 olarak değerlendireceği için true sonuç döndürür ama “if((5==7)) “ ifadesi sağlıklı bir şekilde false sonuc verir.Bu ( let, expr,(( ... )) ) ifadeler aritmetik işlemler için aşağıdaki gibi kullanılır.
Örnek:
Ders2Ornek4sh
#!/bin/bash a=9 b=8 c=7 d=6 e=5 f=4 let x=a*b y=$( expr $c + $d ) z=$(( e - f)) echo a*b=$x echo c+d=$y echo e-f=$z
Ondalık Sayıların Kullanımı
Yukarda da belirttiğimiz gibi a=10.55 ifadesi string bir ifadedir ama bu ifadeyi başka bir komuta parametre olarak verip ondalık bir sayı olarak işlem yapıp sonucu tekrar string olarak alabiliriz.
Örneğin şu şekilde bir C programımız olsun:
//flt.c
int main(int argc,char* argv[]) { if( argv[1]==NULL)return 0; float f1 = atof(argv[1]); float f2= atof(argv[2]); printf("%0.2f\n",(f1*f2)); //bash için dönüş noktası return 0; }gcc -o flt flt.c şeklinde programı derledikten sonra aşağıdaki kodu çalıştırabiliriz.Program parametreleri alıp floata dönüştürüp çarpıp bize sonucu geri döndürür.
Ders2Ornek5sh
#!/bin/bash a=10.7 b=4.9 echo $(./flt1 $a $b) # $(komut) şeklinde bir komutun dönüşünü alabiliriz.
Tabi ki bu konuda bizim ilkel programımıza göre çok daha yetenekli bir hesap makinesi vardır bash da kullanılabilen.bc komutuyla ondalık(kesirli) sayılarla her türlü matematiksel işlemleri yapabiliriz.
Ders2Ornek6sh
#!/bin/bash a=10.7 b=4.9 echo “$a* $b”|bc # “|” pipe işaretidir. Solundaki komutun çıktısını sağındaki komut satır satır okur. c=$(echo “$a* $b”|bc ) echo $c
Değişken değeri içerisinde baştan ve sondan silme
${degisken#kelime} ${degisken##kelime} baştan siler
${degisken%kelime} ${degisken%%kelime} sondan siler
Yukardaki ifadelerden biri ile (1 tane kısa ifadeler,2 tane uzun ifadeler için) değişkeninin içindeki değer içinde silme yapabiliriz.Ama biz sadece çıktıda silme yaparız değişkenin değeri değişmez.Değişkenin değerini değiştirmek için tekrar çıktıyı değişkene atamalıyız.
Ders2Ornek7sh
Değişken içinden istenen kısmı alma
${degisken%kelime} ${degisken%%kelime} sondan siler
Yukardaki ifadelerden biri ile (1 tane kısa ifadeler,2 tane uzun ifadeler için) değişkeninin içindeki değer içinde silme yapabiliriz.Ama biz sadece çıktıda silme yaparız değişkenin değeri değişmez.Değişkenin değerini değiştirmek için tekrar çıktıyı değişkene atamalıyız.
Ders2Ornek7sh
#!/bin/bash a=armut b=armut echo ${a#ar} # mut yazar echo $a #armut yazar a=${a#ar} echo $a #mut yazar echo ${b%mut} # ar yazar echo $b #armut yazar b=${b%mut} echo $b #ar yazar
Değişken içinden istenen kısmı alma
${degisken:index:lenght}
Yukardaki ifade ile değişkenin indexin belirttiği harften itibaren lenght kadar kısmını almamızı sağlar.(index sıfırdan başlar)
Ders2Ornek8sh
Yukardaki ifade ile değişkenin indexin belirttiği harften itibaren lenght kadar kısmını almamızı sağlar.(index sıfırdan başlar)
Ders2Ornek8sh
#!/bin/bash a=birikiüç echo ${a:3:3} # iki yazar
İndirect expansion !
Değişken içindeki değeri bir değişken gibi kullanmamızı sağlar, ama unutmayın bu bir pointer değil sadece değişkenin içeriğini verir ve bir aşama daha ileri gitmez.Yani ${!a} çalışır ama ${!!a} yada ${!${a}} çalışmaz.Aynı işlemi eval komutuyla \$$ şeklinde kullanarak da yapabiliriz.
Ders2Ornek9sh
Yukardaki sınıflamamıza dönersek bahsetmediğimiz 3 sınıflama kaldı dizileri(dizi tabanlı) gelecek ders göreceğiz, karar tabanlı olanları 0(false) 1(true) şeklinde ifade edebiliriz.Ama unutmayın linuxta sistem fonksiyonları başarı durumunda sıfır döndürür.Peki son maddeye gelirsek,kendi nesnelerimizi oluşturabilir miyiz?bash nesne yönelimli programlamayı (OOP) desteklemez.Ama biraz ilüzyonla bash’ı kandırabiliriz.
Örnek:
mystruct.sh
main.sh
Çıktı şu şekilde olur.
ahmet kara 23
mehmet kara 24
Unutmayın yukardaki kod diğer programlama dillerindeki gibi bir nesne oluşturmaz,sadece öyle algılamamızı sağlar yada belki de diğer programlam dillerinde de nesne oluşmuyordur da biz öyle zannediyoruzdur.
#!/bin/bash a=5 b=a echo ${!b} # 5 yazar eval "echo \$$b" # yine 5 yazar
Yukardaki sınıflamamıza dönersek bahsetmediğimiz 3 sınıflama kaldı dizileri(dizi tabanlı) gelecek ders göreceğiz, karar tabanlı olanları 0(false) 1(true) şeklinde ifade edebiliriz.Ama unutmayın linuxta sistem fonksiyonları başarı durumunda sıfır döndürür.Peki son maddeye gelirsek,kendi nesnelerimizi oluşturabilir miyiz?bash nesne yönelimli programlamayı (OOP) desteklemez.Ama biraz ilüzyonla bash’ı kandırabiliriz.
Örnek:
mystruct.sh
#!/bin/bash function mystruct() #kurucu fonksiyon { this=$1 export ${this}_ad=$2 #nokta yerine yapı elemanına ulaşmak için _ (alt tireyi) kullanacağız. export ${this}_soyad=$3 #fieldleri tanımlıyoruz export ${this}_yas=$4 }
main.sh
]#!/bin/bash . mystruct.sh #mystruct.sh dosyasını import ediyoruz main(){ mystruct ilk ahmet kara 23 # nesnemizi oluşturuyoruz. mystruct iki mehmet kara 24 echo $ilk_ad $ilk_soyad $ilk_yas #çıktıyı alıyoruz. echo $iki_ad $iki_soyad $iki_yas } main
Çıktı şu şekilde olur.
ahmet kara 23
mehmet kara 24
Unutmayın yukardaki kod diğer programlama dillerindeki gibi bir nesne oluşturmaz,sadece öyle algılamamızı sağlar yada belki de diğer programlam dillerinde de nesne oluşmuyordur da biz öyle zannediyoruzdur.
Herhangi bir basit problem, hakkında yeterince toplantı yapılarak, çözümsüz hale getirilebilir.
https://play.google.com/store/apps/developer?id=ONGUN
https://play.google.com/store/apps/developer?id=ONGUN