Kabuk Betiği (Shell Scripting)

Cem Topkaya
8 min readMay 12, 2020

--

  • sh, bash, dash, csh, zsh … Ne ola ki?
  • Shell
  • bash
  • dash
  • If Else ile Execute Shell Üstünde Şartlı Çalıştırma
[...] && ... || ...
if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi
  • Ternary Operatör Kullanmak

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")

  • Switch Case

Öğrendiklerimi not alıyorum ki, yaşlanınca kaldığım yeri hatırlayayım diye. Yoksa Türkçe ve yüce Türk milletime hizmet olsun diye düşünerek yazıyorsam taş olayım. Siz de sakın özünüzü korumak için Türkçe makale yazıp kendinizi ve binlerce yıldır bu topraklarda yüzlerce büyük medeniyetin mirasçısı olan yükselmesi tüm galaksi için iyiliklere neden olacak bu millete bir toz zerresi kadar bile yardım etmeyebilirsiniz.

sh, bash, dash, csh, zsh … Ne ola ki?

Bir docker container içinde sh, bash, dash ile bağlanmak istediğimizde.

Not: Yukarıdaki docker container bir linux sürümünün üstüne kurulmuş ancak içinde sadece bash, sh, dash mevcutken, csh, zsh… gibi kabuk uygulamaları yüklü değil.

Öncelikle işletim sisteminizi (linux tabi) varsayılan SHELL’i nedir diye kontrol etmek için:

$ echo "$SHELL ($0)"
VEYA
$ echo $0
VEYA
$ ps -p $$

Yüklü SHELL listesini görmek için:

$ cat /etc/shells

$ cat /etc/shells

Ref: cyberciti.biz

Şimdi shell nedir öğrenelim…

Shell, işletim sistemlerinde işlerimizi kolaylaştırmak/otomatikleştirmek için standartlaştırılmış komutlar şartnamesidir (specification).

Shell (kabuk) işletim sistemleri için bir komut dili yorumlayıcısıdır. Bir komut dilinin nasıl olması gerektiğini tarif eder (POSIX -Portable Operating System Interface- şartnameleri bunun içindir).

ksh88, dash, csh, zsh, ksh, bash … Gibi birçok uygulaması vardır.

bash, Shell’in bir uygulaması olarak düşünülebiliriz. Stephen R. Bourne tarafından geliştirilmiştir.

Stephen R. Bourne (ingiliz üstat)

dash, Tıpkı bash gibi ama daha az yer kaplayan, daha az sayıda kütüphane içeren, hızlı kabuk uygulamasıdır(POSIX 1003.2 şartnamesine göre düzenlenmiştir). Bu yüzden Debian ve Ubuntu bash yerine artık dash kullanırlar.

Özetle; sh bir uygulama değil, bir şartname (özellikler tanımlaması — spesifikasyon) olduğundan, /bin/sh çoğu POSIX sisteminde gerçek bir uygulamaya yönelik sembolik bağlantıdır. Yani /bin/sh komutunu çalıştırdığımızda sistemde yüklü ve sh’ın uygulamalarından “bash, dash, ksh88, csh, zsh, …” gibi bir kabuk uygulamasını çalıştırır.

Karakterlerin nelere karşılık geleceğini, rezerve kelimeleri vs. tayin etmiştir.

Bir işletim sisteminde çeşitli işlerimizi kolayca kodlayabileceğimiz bir scripting kullanırız. Bu script dosyalarını program gibi çalıştırdığımız için yorum satırı olduğunu gösteren # ile başlayan satırlar ekleriz. # Karakterini gören yorumlayıcı hemen altındaki satırdan devam eder. İlk satıra she-bang diye adlandırılan #! karakterleri istisnadır ve ardından hangi kabuk uygulamasına denk geldiğini gösteren dosya yolu gelir. Böylece scripti doğrudan ilgili shell uygulamasına yönlendirerek derlemesini sağlar.

#!/bin/bash 
# kodlamalar hoşluklar vs.

Veya

#!/bin/sh
--------
#!/bin/csh -f
# C shell veya uyumlu bir shell uygulamasını çağırır
--------
#!/usr/bin/env python
--------
#!/usr/bin/perl -T
--------

gibi gibi

Refs:

Değişkenler

Bir kabuk betiğini komut satırından çağırırken parametreler geçebiliriz. Bu parametreleri betik içinde $1, $2, $3, …$9, ${10}, ${11}… ile alabiliriz (iki karaktere geçince süslü paranteze sarıyoruz!).

$0 çağırılan betiğin adını vereceği için betiğe geçirilen parametreler gibi değerlendirmemeniz gerekiyor.

$# ile kaç parametre geçirildiği bilgisini alabilirsiniz.

$* ile tüm parametrelerin listesini alabilirsiniz.

for x in $*
do
echo $x
done

Değişkenler İçin İpuçları

  • Betiğinizin içinde değişkene = ile değer atarken = karakterinin sağında ve solunda boşluk olmadığından emin olun!
  • Değişkenin değerini atarken eşitliğin sağında değeri muhakkak “” karakterleri arasına alın.
  • :- ile varsayılan değerler verebilirsiniz.
  • read ile konsoldan gelen veriyi okuyabilir, değişkene atayabilirsiniz.
case esac ve REPLY

Parametrelere Ön Tanımlı Değer Atamak

Eğer 2. parametre boş geliyorsa varsayılan değer atamak içim :- kullanırız.

Konsola Girilen Değeri Değişkene Atamak

Ekrandan okuduğunuz değeri doğrudan bir değişkene atamak için read komutu -p anahtarı alır. Şifre gibi hassas bilgilerin yazıldığında görünmemesi için ise -s anahtarı kullanılır.

#!/bin/bash
read -p "Kullanıcı adınızı giriniz: " kullanici_adi
read -ps "Şifrenizi giriniz: " sifre

Eğer Beklenen Parametreler Girilmemişse

$# Kaç parametre girildiğini gösteriyordu. Eğer $# -eq 0 ise hiç parametre girilmemiş demektir. echo “${0}: Kullanıcı bilgileriniz eksik” satırıyla 0. argüman olan betiğin adını vererek hata mesajı görüntülüyoruz.

./ornek.sh: Kullanıcı bilgileriniz eksik

Tüm kod:

#!/bin/bashif [ $# -eq 0 ]; then
echo "${0}: Kullanıcı bilgileriniz eksik"
read -p "Kullanıcı adınız: " kullanici_adi
read -sp "Şifreniz: " sifre
else
kullanici_adi="$1"
sifre="$2"
fi

If Else ile Execute Shell Üstünde Şartlı Çalıştırma

#!/bin/bash -xe
echo param1 değeri: $PARAM1
if [[ $PARAM1 == false ]]
then
echo "Doğru"
else
echo "Yanlış"
fi

Ternary Operatör Kullanmak

Ternary operatör [[ $b = 5 ]] && a="$c" || a="$d"
Daha kısa haliyle a=$([ "$b" == 5 ] && echo "$c" || echo "$d")

Then bloğunun hata vermesine karşın else bloğu ekleyebilirsiniz
[ $b == 5 ] && { a=$c; true; } || a=$d
Set edilmemişse a=${VAR:-20}
Tek satırda if ile if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

Burası çokomelli …!…. [...] && ... || ...

Basitçe if … then … else … anlamı var. Bunu komut satırında koşturduğunuzda komutun hata vermesi halinde status code 0'dan farklı gelir ve uygulamanız kırılır. Şimdi if kısmına bir dizin varsa komutunu then kısmına kopyala komutunu yazalım.

[ -d "projects" ] && cp "projects" hedefDizin

Eğer if şartında hata alırsanız (projects dizini yoksa) komut kırılır ve statusCode 0'dan büyük değer alırsınız (en son çalışan komutun sonuç kodunu $? ile öğreniyoruz).

$ [ -d project ] && cp project hedefDizin
$ echo $?
1

Eğer bunu Jenkins gibi bir Sürekli Entegrasyon ortamında kullanıyorsanız kodunuz kırılır. Şimdi parçalayarak bakalım ne yaptığımıza:

$ [[ -d projects ]]
$ echo $?
0
# Olmayan dizinin varlığını kontrol ettiğimizde statusCode 1 gelir
$ [[ -d project ]]
$ echo $?
1

Dizin varsa komut statusCode 0 olarak çıkıyor ama yoksa 1 ile hata vererek çıkıyor. Hatanın sonunda && ile devam eden kod çalışmıyor (bizim örneğimizde (&& cp project hedefDizin).

Hata vermesi muhtemel komutun hata verse de kodun kırılmasını önlemek için || true ile komut satırınızı bitirebilirsiniz.

Olmayan dizini/dosyayı kontrol ettiğimiz if … bloğunda statusCode 1 gelir then … bloğu çalışmaz ama komut statusCode 1 geldiği için kod kırılır. Bu yüzden garanti çalışmayı sağlamak için yani statusCode 0 ile devam edebilmek için || … ile else bloğunu yazarız

$ [[ -d project ]] && echo "hede" || echo "garanti çalışki statusCode 0 olsun"
garanti çalışki statusCode 0 olsun
# Status Code 0 olarak komut çalıştırıldı çünkü else hata vermedi!
$ echo $?
0

Peki ya else bloğu da hata verecek olsaydı?

$ [[ -d project ]] && echo "hede" || cp project illaBuraya
cp: cannot stat 'project': No such file or directory
$ echo $?
1

Kodumuz kırılsın diye her bir şeyi yaparsak bunu emin olun başarırız :)

#!/bin/bash -xe
DIZIN_YOLU="${WORKSPACE}/bosluklu dizin adi"
echo "Dizin Yolu: $DIZIN_YOLU"
# Ternary haliyle > if then else
[ -d "$DIZIN_YOLU" ] && echo "Dizin mevcut" || echo "Oh nöo"
# Normal if then else
if [ ! -d "$DIZIN_YOLU" ]
then
mkdir -p "$DIZIN_YOLU"
else
echo "Dizin var diye bir şey yapmıyoruz"
fi

Ve yarattı:

Switch Case

case "$b" in
5) a=$c ;;
*) a=$d ;;
esac

for Döngüsü

komut satırında for ile başladığımızda yeni satır (return — enter tuşuyla) komutun çalışmasını değil alt satırda yazmaya devam etmemizi sağlar:

for VARIABLE in 1 2 3 4 5 .. N
do
command1
command2
commandN
done

Örnek (sh üstünden gösteriliyor):

Aralık (range) Yaratmak

for i in 1 2 3 4 ile

for i in {1..4} ile {başlangıç..son}

for i in {0..10..2} : her adımda 2 artımla 0, 2, 4, 6, 8, 10

$ for i in {0..10..2}; do echo “Deger: “$i; done;

for (( i=1; i<=5; i++ )); do echo "$i. defa"; done ile tek satırda

for (( i=1; i<=5; i++ )) 
do
echo "$i. kez"
done
Sonsuz döngü için i<=5 yerine 1==1 yazabilirsiniz. Eğer i gibi bir sayaca ihtiyacınız yoksa for (( ; ; )) diyebilirsiniz
for (( i=1; i<=5; i++ )); do echo “$i. defa”; done

Bir dizi içinde dönebilir. Diziyi gösterirken ${arr[*]} veya ${arr[@]} kullan:

dizi=(1 2 3)for eleman in ${dizi[*]}
do
echo "$eleman"
done
for indeks in ${!dizi[@]}
do
echo "$indeks"
done

Tek satır hali:

$ dizi=(1 2 3); for eleman in ${dizi[*]}; do echo "$eleman"; done
$ dizi=(1 2 3); for indeks in ${!dizi[*]}; do echo "$indeks"; done
$ arr=(1 2 3); for i in ${arr[*]}; do echo $i; done
$ arr=(cem can john); for eleman in ${arr[@]}; do echo $eleman; done

Kaynak: https://www.cyberciti.biz/faq/bash-for-loop/

seq Komutuyla aralığı ve artım oranlarını ve hatta yazdırırken kullanabileceğiniz formatı da ayarlayabilirsiniz

Aralık olarak farklı komutların çıktılarını kullanabilirsiniz

ls Komutuyla dosyaların içinde dönün:

cat Komutyla okuduğunuz dosyanın kelimeri içinde dönün:

$ for satir in $(cat Dockerfile.image.a); do echo $satir; done

grep Komutuyla 3 haneli sayıların arasında dönün:

$ grep -Po ‘\d{3}’ known_hosts
$ for sayi in `grep -Po ‘\d{3}’ known_hosts`; do echo “Sonuç: “$sayi; done

Back tick arasında komutu yazmakla $() arasında yazmak aynı şeydir:

`...` == $(...)

Not: seq ve tr (transpose) komutlarını kabuk betiği kodlarınızda yardımcı olarak kullanabilirsiniz. Bu komutlarla ilgili kullanım notlarımı burada bulabilirsiniz.

Döngüyü kırmak için break veya iş yapmadan devam etmek için continue kullanabilirsiniz

https://www.cyberciti.biz/faq/bash-for-loop/

--

--

Cem Topkaya
Cem Topkaya

Written by Cem Topkaya

Evlat, kardeş, ağabey, eş, baba, müzik sever, öğrenmek ister, paylaşmaya can atar, iyi biri olmaya çalışır, hakkı geçenlerden helallik ister vs.

No responses yet