Shell Script #1 – (Yorumlar, Here Document ve Debugging)

Script’imizi yazdık. Çalıştırdık. Sorun yok. Pekala…

Script’imizi yazdık. Çalıştırdık, çalışmadı. Neden? Nerede hata aldık? Ya da. Script’imizi yazdık. 3 ay sonra hata aldık. Açtık, baktık. Hiçbir şey anlamadık. Klasik programlama sıkıntıları…

Yorum Satırları

Bash script’lerinde yorum satırlarını “#” işaretiyle gösteririz. Hatırlarsanız, shebang de bu karakterle başlıyordu. Değil işte. Shebang “#!” ile başlıyor. Bu ikisi birbirinden farklı yorumlanır.

Çok satırlı yorumlarda ise “here document” kullanacağız. Aşağıdaki script’te yorum satırlarını görebilirsiniz. Dikkat edin, ilk satır her zaman shebang‘tir.

#! /bin/bash
#Basit bir listeleme script'i
#Listeleme işini yaparken gizli dosyaları, izinleri ve inode numaralarını gösterir.
#Dosya boyutlarını "human-readable" formatta (K, M, G gibi) gösterir.

#Kullanıcıya bir mesaj göster:
echo "Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:"
ls -alhi

Script’imizin çıktısı:

[root@localhost bashscript]# ./yorumlar.sh
Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:
toplam 12K
16797781 drwxr-xr-x. 2 root root   47 Eki 28 18:30 .
16797761 dr-xr-x---. 8 root root 4,0K Eki 28 14:53 ..
16802432 -rwxr--r--. 1 root root  126 Eki 28 15:07 echo_ornegi.sh
16802435 -rwxr--r--. 1 root root  541 Eki 28 18:30 yorumlar.sh
[root@localhost bashscript]#

Here Document

Here document, özel bir amaca hizmet eden kod bloğudur. Bir çeşit I/O yönlendirmesi yapar. Bu sayede interaktif bir komutu besleyebilmenizi sağlar. Bir örnekle incelemeye çalışalım.

wc programı ile girdi olarak verilen bir dosyanın satır, kelime ve boyut bilgilerini görebiliriz. Yalnızca satır sayısını görmek için ise “-l” flag’ini kullanabiliriz.

[root@localhost bashscript]# wc -l yorumlar.sh
17 yorumlar.sh

Yukarıda görüldüğü üzere, “yorumlar.sh” dosyasında 17 satır bulunmakta. Peki önceden hazırlanmış, script içerisinde yazılmış bir veriyi, bir programa girdi olarak vermek istersek ne yapacağız? Burada “here document” yardımımıza koşuyor. Aşağıdaki örnek script’i inceleyelim:

#! /bin/bash

wc -l <<girdi
Test
Örnek veri
Deneme
girdi

wc -l programına “girdi” bloğu içerisinde tanımladığım içeriği gönderdik. Bu işi “mail” için düşünelim. Script içerisinde hazırlanan bir metni, e-posta gövdesine doğrudan iliştirebilmek için bu arkadaştan faydalanabiliriz.

[root@localhost bashscript]# cat word_count.sh
#! /bin/bash

wc -l <<girdi
Test
Örnek veri
Deneme
girdi
[root@localhost bashscript]# ./word_count.sh
3
[root@localhost bashscript]#

Benzer şekilde, bu bloğu koca bir boşluğa gönderirsek, bir bakıma çok satırlı yorum elde edebiliriz.

[root@localhost bashscript]# cat yorumlar.sh
#! /bin/bash
#Basit bir listeleme script'i
#Listeleme işini yaparken gizli dosyaları, izinleri ve inode numaralarını gösterir.
#Dosya boyutlarını "human-readable" formatta (K, M, G gibi) gösterir.

<<yorumlar
 Bu blokta kalan içerik, tamamen yorum olarak kalacaktır.
 Bu sayede birden fazla satıra yayılmış yorumlar oluşturabilirsiniz.
 Bunu yaparken her satırın başına # eklemenize de gerek olmaz.
yorumlar


#Kullanıcıya bir mesaj göster:
echo "Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:"
ls -alhi


[root@localhost bashscript]# ./yorumlar.sh
Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:
toplam 24K
16797781 drwxr-xr-x. 2 root root   88 Eki 28 23:53 .
16797761 dr-xr-x---. 8 root root 4,0K Eki 28 14:53 ..
17514307 -rw-r--r--. 1 root root    2 Eki 28 22:33 a
16821756 -rw-r--r--. 1 root root    5 Eki 28 22:28 ali
16802432 -rwxr--r--. 1 root root  126 Eki 28 15:07 echo_ornegi.sh
16802431 -rwxr--r--. 1 root root   58 Eki 28 23:53 word_count.sh
16802435 -rwxr--r--. 1 root root  541 Eki 28 18:30 yorumlar.sh
[root@localhost bashscript]#

Script’lerde Hata Ayıklama

Bash programının man sayfasından 3 opsiyonu paylaşayım öncelikle. Sonra bunların ne işe yaradığını konuşalım.

-n: Read commands but do not execute them. This may be used to check a shell script for syntax errors. This is ignored by interactive shells.
-v: Print shell input lines as they are read.
-x: After expanding each simple command, for command, case command, select command, or arithmetic for command, display the expanded value of PS4, followed by the command and its expanded arguments or associated word list.

Hadi başlayalım.

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# cat debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
rm a
[root@localhost bashscript]# bash -n ./debug.sh
[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]#

Yukarıdaki örneği adım adım inceleyelim:

  1. ls ile dizini listeledim. “a” isimli bir dosya var.
  2. cat ile “debug.sh” scriptini okudum. Bu script, ekrana bir mesaj yazıp “a” dosyasını silmeyi deniyor.
  3. bash -n ./debug.sh ile, bulunduğum dizindeki script’i bash programına verdim. -n flag’i sayesinde, sadece komutları okumasını fakat çalıştırmamasını istedim.
  4. ls ile tekrar kontrol ettiğimde, “a” dosyasının silinmediğini gördüm. Üstelik ekrana herhangi bir mesaj da yazılmadı.

Şimdi script’i hatalı hâle getirelim ve tekrar deneyelim.

[root@localhost bashscript]# cat debug.sh
#! /bin/bash

ehco "Ben bu dosyayı silecek miyim sence?
remove a
[root@localhost bashscript]# bash -n ./debug.sh
./debug.sh: line 3: `"' için eşleşme aranırken beklenmedik dosya sonu
./debug.sh: line 5: sözdizimi hatası: beklenmeyen dosya sonu
[root@localhost bashscript]#
  1. cat ile script’imi okudum. “echo” yerine “ehco”, “rm” yerine “remove” yazılmış. Bu script’te yazım hataları var.
  2. bash -n ile script’i kontrol ettim.
  3. Çıktı olarak hatalar önüme döküldü. Burada uzun bir iş yaptığımı varsayalım. Birçok adım doğru tamamlandıktan sonra script bir yerde hata verseydi, işler birbirine girebilirdi. Neyse ki “bash -n” sayesinde, işe hiç başlamadan sonunun gelemeyeceğini görmüş olduk.

Gelelim başka bir örneğe:

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# cat debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
rm a
[root@localhost bashscript]# bash -x ./debug.sh
+ echo 'Ben bu dosyayı silecek miyim sence?'
Ben bu dosyayı silecek miyim sence?
+ rm a
[root@localhost bashscript]# ls
debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]#
  1. ls ile baktım, “a” dosyası orada.
  2. cat ile okudum. Bir yazım hatası yok gibi.
  3. xtrace özelliğini kullanarak, script’in “benim yazdıklarımdan ne anladığını” göstermesini istedim. Her bir satır, önce bize gösterildi, sonra çalıştırıldı.
  4. ls ile tekrar baktığımda, script’in “a” dosyasını sildiğini de görüyoruz.

“Benim yazdıklarımdan ne anladığını…”. Burası biraz havada kalmadı mı sizce de? Başka bir örnek:

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# cat debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
echo "Bu işlemin yapıldığı tarih: $(date)"
rm a
[root@localhost bashscript]# bash -x ./debug.sh
+ echo 'Ben bu dosyayı silecek miyim sence?'
Ben bu dosyayı silecek miyim sence?
++ date
+ echo 'Bu işlemin yapıldığı tarih: Sal Kas  3 16:50:02 +03 2020'
Bu işlemin yapıldığı tarih: Sal Kas  3 16:50:02 +03 2020
+ rm a
[root@localhost bashscript]# ls
debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]#
  1. ls ile baktım. “a” dosyasını gördüm.
  2. cat ile okudum. Script’in içerisinde bir yerlerde “echo” ile “date” iç içe kullanılmış. “Benim yazdığım” echo içinde bir date. Fakat “onun anladığı” ne?
  3. bash -x ile script’i çalıştırdım.
  4. İlk echo’yu olduğu gibi anlamış. Gösterdi, çalıştırdı, sonucunu gördük.
  5. “++date” satırında ise, “önce date komutunu çalıştırıp tarihi öğrenmem lazım” dediğini görüyoruz. Sonrasında “date”in çıktısını “echo” içerisine yerleştirmiş ve o hâliyle çalıştırmış.
  6. ls ile kontrol edip dosyayı sildiğini de teyit edebiliriz.

Yetti mi? Hayır:

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# bash -v ./debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
Ben bu dosyayı silecek miyim sence?
echo "Bu işlemin yapıldığı tarih: $(date)"
Bu işlemin yapıldığı tarih: Sal Kas  3 16:54:09 +03 2020
rm a
[root@localhost bashscript]#
  1. ls ile baktım. “a” dosyasını gördüm.
  2. bash -v sayesinde, script çalıştırılırken ilgili satırın önümüze getirilmesini istedim.
  3. Çalıştırma sırasında gördüm ki; script’imin kodları, çalıştırılmadan önce ekrana dökülüyor. Bu sayede hangi satırın hangi çıktıyı ürettiğini görüyoruz.

Peki bu arkadaşlar bir arada çalışamaz mı? Tabii ki. Daha iyi anlatabilmek için burayı numaralandırıyorum:

1- [root@localhost bashscript]# bash -xv debug.sh
2- #! /bin/bash

3- echo "Ben bu dosyayı silecek miyim sence?"
4- + echo 'Ben bu dosyayı silecek miyim sence?'
5- Ben bu dosyayı silecek miyim sence?
6- echo "Bu işlemin yapıldığı tarih: $(date)"
7- ++ date
8- + echo 'Bu işlemin yapıldığı tarih: Sal Kas  3 16:56:40 +03 2020'
9- Bu işlemin yapıldığı tarih: Sal Kas  3 16:56:40 +03 2020
10- rm a
11- + rm a
[root@localhost bashscript]#
  1. bash -xv ile, hem xtrace hem de verbose istedim. Ne yazmışım, ne anlamışsın, ne çalıştırdın?
  2. 2 numara shebang.
  3. 3 numara benim yazdığım echo.
  4. 4 numara, + ile başlayan, bash’in anladığı hâli.
  5. 5 numara, yukarıdaki echo’nun çıktısı.
  6. 6 numara, benim yazdığım echo.
  7. 7 numara, ++ ile başlayan, “önce date’i çalıştırmam lazım” dediği yer.
  8. 8 numara, “date” çalıştıktan sonra oluşan yeni “echo”. Ben böyle bir şey yazmamıştım. Bash bu hâle getirdi.
  9. 9 numara; benim anlatmaya çalıştığım hâlinden, bash’in anladığı hâle gelen echo’nun çıktısı.
  10. 10 numara, benim yazdığım “rm”.
  11. 11 numara, + ile başlayan, bash’in anladığı hâli.

Sonuç Olarak

Shell script’ler, özellikle root yetkileri ile çalıştırıldığında, ölümcül etkilere sahip olabilir. Fakat tekrarlı işlerinizi otomatize etmeniz için de inanılmaz güçlü aletler oluşturmanızı sağlayabilir. Bu nedenle yazdığınız script’lerin, her ortamda her durumda çalışacağından emin olmalısınız.

Böyle bir şey mümkün mü? Sanmam. O yüzden yapabileceğimiz şey şuna indirgenebilir: Şu dağıtımın şu sürümünde, şu çekirdek versiyonuyla, internet bağlantısı olduğu takdirde, SSH servisi varsa çalışır. Ya da buna benzer şeyler söyleyebilirsiniz.

Sonra da bu gereksinimlerin olup olmadığı test eden bir shell script yazar, ön gereksinimler sağlandıysa esas script’inizi çalıştırırsınız 🙂

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir