Kabuk Betiği (Shell Scripting)
- 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:
- https://standards.ieee.org/content/ieee-standards/en/standard/1003_1-2017.html
- https://pubs.opengroup.org/onlinepubs/009695399/
- http://www.opengroup.org/austin/papers/posix_faq.html
- https://medium.com/@codingmaths/bin-bash-what-exactly-is-this-95fc8db817bf
- https://www.quora.com/What-is-the-difference-between-Shell-scripting-and-Bash-Shell-scripting
- https://stackoverflow.com/a/5725402/104085
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.
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: $PARAM1if [[ $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=1; i<=5; i++ )); do echo "$i. defa"; done
ile tek satırda
for (( i=1; i<=5; i++ ))
do
echo "$i. kez"
doneSonsuz döngü için i<=5 yerine 1==1 yazabilirsiniz. Eğer i gibi bir sayaca ihtiyacınız yoksa for (( ; ; )) diyebilirsiniz
Bir dizi içinde dönebilir. Diziyi gösterirken ${arr[*]}
veya ${arr[@]}
kullan:
dizi=(1 2 3)for eleman in ${dizi[*]}
do
echo "$eleman"
donefor 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
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:
grep
Komutuyla 3 haneli sayıların arasında dönün:
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