Bash ile Programlama Cem Ahmet Mercan Ulusal Yüksek Başarımlı Hesaplama Merkezi (UHeM) 13 Temmuz 2017
Neden, Niye, Ne gerek var? Python daha iyi! C daha hızlı! Java her yerde... 1) Zaten yapmayı bildiğimiz komutlar, 2) Bash her yerde mevcut, 3) API (Uygulama Program Arayüzü) ye gerek yok, 4) CLI (Komut Satırı Arayüzü) ünde de geçerli, 5) Derleme vb. Gerekmez, çok kolay. 2
Betik Hazırlama (Script) Betik derlemeden doğrudan çalıştırılabilir, karakterlerden oluşmuş program dosyasıdır. Herhangi bir yazı editörü ile program hazırlayabiliriz: nano ilkbetik.sh yada vim ilkbetik.sh sh uzantısı zorunlu değildir, kendimizin bu dosyanın bir shell betiği olduğunu kolayca anlamamız için sadece. 3
nano Editörü 4
ilkbetik.sh echo "Merhaba dünya!" mkdir AHMET ls -alh AHMET 5
Source Komutu $ source ilkbetik.sh Merhaba dünya! Yada kısa şekli $. ilkbetik.sh Merhaba dünya! Bu komut sanki dosyadaki komutları kopyalayıp, shell komut satırı arayüzüne yapıştırmışız gibi komutları zaten açık olan bash de çalıştırır. 6
ilkbetik.sh #!/bin/bash echo "Merhaba dünya!" mkdir AHMET ls -alh AHMET Bir betiği doğrudan çalıştırmaya çalıştırdığımızda, dosyanın ilk iki karakteri #! ise bu karakterlerden sonra gelen komut bu dosyadaki satırları çalıştırmak için kullanılır. 7
Doğrudan Çalıştırılabilir Betik $./ilkbetik.sh bash:./ilkbetik.sh Permision denied şeklinde çalıştırmak için betik dosyasına çalıştırma hakkı vermeliyiz: $ chmod +x./ilkbetik.sh $./ilkbetik.sh Merhaba dünya! 8
Yorum (Comment) Eklemek Bash, # işaretinden satır sonuna kadar olan bölümü yorum olarak değerlendirip çalıştırmaz: #!/bin/bash # Bu program Kullanıcı haklarını düzeltir # Hazırlayan: Cem Ahmet Mercan # echo "Bu yazı yazılmayacak" mkdir AHMET #AHMET isimli bir dizin açılıyor 9
Değişkenler
Değişken Tanımlama Değişken isimleri harfle başlayıp; harf, rakam yada _ karakteri ile devam edebilir. Türkçeye özel karakterler isim için kullanılamaz. Harflerin büyük yada küçük olmasına duyarlıdır. Yani $PATH ile $Path yada $path değişkenleri tamamen farklıdır. Değişkenlerin değerine başına $ işareti konularak erişilir. Değişkenlere yazı yada sayı atanabilir, dizi oluşturulabilir. DEGISKEN="Merhaba Dünya!" SAYAC=0 11
Öntanımlı Değişkenler Bash de tanımlı bir çok değişken mevcuttur. Bu değişkenleri programımızda kullanabiliriz. Şu an tanımlı değişkenleri set komutu ile görebiliriz. Başlatacağımız bir programın hangi değişkenlerle başlayacağını ise, env komutu ile görebiliriz. 12
Öntanımlı Değişkenler Bash de tanımlı değişkenlere örnekler: USER kullanıcı adı HOSTNAME bilgisayarın adı PWD şu anda bulunulan dizin HOME kullanıcının ev dizini RANDOM her seferinde rastgele bir sayı verir. LANG programın çalışacağı dil bilgisi. IFS alanları ayırmak için kullanılan karakter listesi. LINENO betiğin hangi satırı çalışıyor. PPID betiği çağıran programın process id değeri. $$ betiğin process id değeri. 13
Örnek #!/bin/bash echo "Merhaba $USER," echo "Şu anda $HOSTNAME bilgisayarındasın" echo "ve $PWD dizinindesin." echo "Burada merhaba-$user isimli bir dizin açacağım:" mkdir "merhaba-$user" ls -alh "merhaba-$user" 14
Değişken Tanımlama #!/bin/bash echo "Merhaba $USER," echo "Şu anda $HOSTNAME bilgisayarındasın" echo "ve $PWD dizinindesin." DIZIN="merhaba-$USER" echo "Burada $DIZIN isimli bir dizin açacağım:" mkdir "$DIZIN" ls -alh "$DIZIN" 15
Değişken Tanımlama Değişken adı, = işareti ve Değişken değeri arasında BOŞLUK OLMAMALI! # Doğrusu: DIZIN="merhaba-$USER" #Hatalı: DIZIN ="merhaba-$user" DIZIN= "merhaba-$user" DIZIN = "merhaba-$user" 16
Betik Parametreleri $ ilkbetik.sh gel zaman git zaman -h -l $0 $1 $2 $3 $4 $5 $6 $# parametre sayısı, yukarıdaki örnekte 6. $* tüm parametreler birlikte tek metin "gel zaman git zaman -h -l" $@ tüm parametreler ama ayrı ayrı metinler şeklinde "gel" "zaman" "git" "zaman" "-h" "-l" $$ Betiğin işlem numarası (process id) 17
Örnek #!/bin/bash echo "Merhaba $USER," echo "Şu anda $HOSTNAME bilgisayarındasın" echo "ve $PWD dizinindesin." echo "Burada $1 isimli bir dizin açacağım:" mkdir "$1" ls -alh "$1" 18
Özel Karekterler ve İfadeler
Metin Gruplama Karakterleri " " İki tırnak arasındaki ifade birlikte tek bir ifade olur. Özel karakterler çalıştırılır. mkdir "cem ahmet mercan" Tek bir dizin açar. mkdir cem ahmet mercan Üç ayrı dizin açar. echo "$USER" kullanıcı adı, $USER değil ' ' İki tırnak arasındaki ifade birlikte tek bir ifade olur. Özel karakterler ÇALIŞTIRILMAZ. echo '$USER' $USER, kullanıcı adı değil 20
Dosya / Dizin için Özel Karakterler * Her hangi bir şey, hiç bir şey dahil *.gif sonu.gif olan herşey. A* başı A olan herşey. Sadece A da kabul. *Ahmet* Ahmet geçen herşey, Sadece Ahmet de olur.? her hangi bir karakter????.txt 4 karakter sonra.txt {,, } bu metinlerin her biri, ayrı ayrı cp dosya{001,005,-listesi} arsiv/ cp dosya001 dosya005 dosya-listesi arsiv/ 21
Dosya / Dizin için Özel Karakterler [ ] Bu karakterlerden harhangi biri, ls [klm]* - Karakter aralığı belirtir. k, l veya m ile başlayan herşey rm dosya00[0-9].txt rm dosya000 dosya001 dosya003... dosya008 dosya009 ls [A-Da-d]* A,B,C,D,a,b,c,d harflerinden biri ile başlayan. [[:digit:]] rakamlardan herhangi biri [[:upper:]] rm [[:digit:]]*.txt rakamla başlayan txt dosyalarını sil. büyük harflerden herhangi biri alnum, alpha, ascii, blank, cntrl, digit, graph, lower, print, punct, space, upper, word, xdigit 22
Özel Karakterler \ Özel Karakterleri özel anlamı olmadan kullanmak için \? Gerçek soru işareti? anlamında \\ Gerçekten \ yazdırmak için \BOŞLUK IFS değişkeninden kaçarak boşluk yazmak için \ Görünmez yada klavye dışı karakterleri yazmak için \t TAB karakteri \n ENTER karakteri, new line \e ESC karakteri \0?? ASCII karşılığını vererek yazdırmak için, \033 ESC 23
Yazdırma echo Verilen ifadeyi biçimlendirmeden yazdırır. -n yazdıktan sonra yeni satıra geçmez. -e \ ile özel karakterleri de yazmayı açar. echo -e "${SIRANO}\t${ISIM}\t${SOYAD}" echo -e "\033[1mBu yazı bold yazılacak\033[0m" printf C deki printf fonksiyonunun aynısı, formatlı çıktı verir. %d, %f, %s tüm veri tipleri var, - sola yaslar, rakamla genişlik verilir, söylemedikce ENTER basmaz. printf "%4d \t %-12s \t %-12s \n ${SIRANO} ${ISIM} ${SOYAD} 24
Komut Gruplama ; Aynı satıra birden fazla komut yazmak için cd ; ls önce cd komutu sonra ls çalıştırılır. ( ) Bir grup komutu birlikte, yeni bir bash de çalıştırır. ( ls ; echo $DENEME; mkdir $DENEME ) && AND (ve) işlemi, Önceki komut doğru sonlandı ise sonrakini çalıştırır. mkdir $DIZIN && cd $DIZIN OR (yada) işlemi, Önceki komut hatalı sonlandı ise sonrakini çalıştırır. mkdir $DIZIN echo "Dizin Açılamadı!" 25
Komut Çıktısını Kullanmak ` ` Eğik tırnaklar arasındaki komutu çalıştırılıp, çıktısı ifadenin bulunduğu yere konur. DOSYALAR=`ls /usr/bin` rm `cat dosya-listesi.txt` $( ) Yukarıdaki Eğik tırnaklar ile aynı şey: DOSYALAR=$(ls /usr/bin) Bir komutun çıktısını başka bir komuta gönderir. ls grep ali ls komutunun çıktısında ali geçenler. rm `ls grep ali` adında ali geçen dosyaları siler. 26
Döngüler
for Döngüsü for i in ahmet mehmet mustafa do echo "Şimdi $i isimli dizin oluşturuluyor." mkdir $i done Şimdi ahmet isimli dizin oluşturuluyor. Şimdi mehmet isimli dizin oluşturuluyor. Şimdi mustafa isimli dizin oluşturuluyor. 28
for Döngüsü for isim in *.mp3 do YENIAD="Sezen-Aksu-${isim}" mv "$i" "$YENIAD" done gülümse.mp3 Sezen-Aksu-gülümse.mp3 yağmur.mp3 Sezen-Aksu-yağmur.mp3 29
for Döngüsü for (( BAŞLANGIÇ; TEST; ARTIŞ )) do KOMUTLAR done for (( i=0; i<5 ; i++ )) do echo "$i. satir yaziliyor." done 30
while / until Döngüleri while TEST do KOMUTLAR done until TEST do KOMUTLAR done 31
while Döngüsü while read DOSYA do cp $DOSYA /yedek-diski/ done < dosyalistesi.txt Deneme.c cp Deneme.c /yedek-diski/ Hesaplamalar.zip cp Hesaplamalar.zip /yedek-diski/ calismalar.tgz cp calismalar.tgz /yedek-diski/ 32
While Döngüsü cat liste.txt while read a b c d do # Her satırdaki 3. kelimeyi yazacak echo "$c" done Satırdaki kelimeler sırasıyla değişkenlere dağıtılır. Kalanların hepsi son değişkene konur. read komutu girilen verileri değişkenlere aktarır. read -p "Lütfen bir sayı giriniz" SAYI 33
While Döngüsü SIMDI=`date '+%s'` while read Proje Tarih do PROJ_TARIH=`date -d "${Tarih}" '+%s'` if [ $SIMDI -gt $PROJ_TARIH ] ; then echo "$Proje süresi dolmuş!" fi done << PROJE_LISTESI hesaplama 02/06/2016 yedekleme 28/12/2018 PROJE_LISTESI 34
Geri Dönüş Değeri Her komut bir değer geri döndürür. Sorunsuz çalıştı ise 0 döndürür. 0 bu sebeble TRUE. Her hangi bir başka durumda, 0 dışında bir değer FALSE. mkdir $DIZIN && echo "Dizin Açıldı" mkdir $DIZIN echo "Dizin Açılamadı" $? değişkeni geri dönüş değerini tutar: mkdir $DIZIN echo $? 35
if Komutu
if Komutu if [ İFADE ] then KOMUTLAR else KOMUTLAR fi if [ $SAYI -gt 12 ] then echo "12 den Büyük" else echo "Büyük Değil" fi Man Sayfası için: man test [ ] Parantezlerin iki tarafında da boşluk şart! 37
Tamsayı Testleri A -eq B Eşitse, Equal A -ne B Eşit Değilse, Not equal A -gt B Büyükse, Greater than A -ge B Büyük veya eşitse, Greater or equal A -lt B Küçükse, Less than A -le B Küçük veya eşitse, Less or equal S=1.1 if [ $S -gt 2 ] ; then echo "BUYUK"; fi -bash: [: 1.1: integer expression expected 38
Metin Testleri -z A Metnin uzunluğu 0 ise, Zero length -n A Metnin uzunluğu 0 değilse, Not zero length A Metnin uzunluğu 0 değilse, Not zero length A == B İki metin aynı ise, A = B İki metin aynı ise, = iki yanı da boşluk olmalı! A!= B iki metin farklı ise, S="Ahmet Nerede?" if [ "$S"!= "" ] ; then echo "Yazı var!"; fi 39
Dosya / Dizin Testleri -e İSİM Bu dosya mevcutsa, -f İSİM Bu dosya mevcut ve normal dosya ise, -d İSİM Bu mevcut bir dizin ise, -r İSİM Bu dosya mevcut ve okuma hakkı varsa, -w İSİM Bu dosya mevcut ve yazma hakkı varsa, -x İSİM Bu dosya mevcut ve çalıştırma hakkı varsa, S="/RS/users/mercan" if [! -d "$S" ] ; then echo "Böyle bir dizin yok!"; fi 40
if Örneği if [ "$MA" == "kara" ] && [ "$USER"!= "root" ] then... if [[ "$MA" == "kara" && "$USER"!= "root" ]] then... if [[ "$MA" == "ege" "$MA" == "kara" ]] && [ "$USER"!= "root" ] then 41
case Komutu case $ISIM in "cem" "ahmet" ) echo "Ahmet gelmiş";; "mercan" ) echo "Ahmet Soyadını yazmış" echo "Bir dahakine adını yaz!";; esac *) echo "Kimsin sen, tanımıyorum seni $ISIM";; 42
Komut Çıktısı Yönlendirme
Komut Çıktısı Yönlendirme > Komutun normal çıktısını bir dosyaya gönderir. ls > dosya-listesi.txt #ls çıktısını dosyaya kaydeder. >> Komutun normal çıktısını bir dosyaya ekler. ls >> dosya-listesi.txt #ls çıktısını dosyaya ekler. < Komuta girdi olarak bir dosyanın içindekileri verir. read ad < liste.txt #liste.txt deki yazıları komuta verir. 44
Komut Çıktısı Yönlendirme 2> Komutun Hata çıktısını bir dosyaya gönderir. mkdir $DIZIN 2>/dev/null 2>&1 hata çıktısını normal çıktı ile birleştirir. ls >dosya-listesi.txt 2>&1 &> hem hata hem de normal çıktıyı dosyaya gönderir. ls &>dosya-listesi.txt 45
Gerekli Komutlar
tr Komutu tr Karakterleri başka bir karakterlere değiştirir. -s ile tekrar eden karakterleri siler. # : işaretlerini TAB a çevir cat /etc/passwd tr ":" "\t" # BOŞLUK, TAB ve ENTER tekrarlarını temizle cat index.html tr -s " \t\n" 47
seq Komutu seq Verilen aralık ve adımla sayı dizisini altalta yazar. -w ile küçük sayıları 0 ile sabit genişlikte yazar. seq -w 001 100 001 002 003 004 005 097 098 099 100 seq 23 5 47 23 28 33 38 43 48
sort Komutu sort Verilen satırları sıraya dizer. -n ile sayısal sıra, yoksa sözlük sırası -r reverse, ters sırala -k kaçıncı sütuna göre, normali 1. sütun # Ev dizinindeki dosyaları büyükten küçüğe sırala du -m ~/ sort -n -r 49
head ve tail komutları head verilen dosyanın başından satırları gösterir -n ile satır sayısı verilir. head -n 3 /etc/passwd # en büyük 10 dosyayı listeler du -m ~/ sort -n -r head -n 10 tail verilen dosyanın sonundan satırları gösterir -n ile satır sayısı verilir. tail -n 3 /etc/passwd 50
grep Komutu grep Satırlardan verilen ifadeye uyanları listeler. -n ile bulunan satırların numaralarını gösterir -r ile dizindeki tüm dosyalarda çalışır. --color ifadenin geçtiği yerleri renkli yazar -e normal regex ifadesi vermek için -E extended regex vermek için -v verilen ifadeye uymayanları listeletmek için # /etc deki dosyalarda 160.75.90.50 ara grep -nr --color 160.75.90.50 /etc 51
awk Komutu awk Satırlardan verilen ifadeye uyanları işler -F ile sütun ayıracı verilir. Çok gelişmiş bir programlama dilidir. # passwd dosyasından kullanıcı ve kabuk bilgisi awk -F: '{print $1, $7}' /etc/passwd root daemon apache /bin/sh /usr/bin/false /bin/bash 52
sed Komutu sed Satırlardan verilen ifadeye uyanları işler -i ile dosyanın kendisini değiştirir. -e normal regex ifadesi vermek için -E extended regex vermek için # asswd dosyasındaki : işaretlerini TAB yapar sed -e 's/:/\t/g' /etc/passwd 53
Gerçek Bir Örnek for dizinno in `seq -w 01 25` do echo "Simulation${dizinNo} dizini isleniyor:" cd Simulation${dizinNo}/ for dosya in Sim*.csv do sed -i 's/ /, /g' $dosya done cd.. done 54
Değişken İşleme
Değişkenlerle Metin İşlemleri METIN="ABCDEF_ABCDEFz" echo "${METIN}" ABCDEF_ABCDEFz echo "${#METIN}" 14 echo "${METIN:2}" CDEF_ABCDEFz echo "${METIN:2:6}" CDEF_A echo "${METIN:(-4)}" DEfz echo "${METIN:(-4):2}" DE echo "${METIN:-Bu degisken bos}" 56
Değişkenlerle Metin İşlemleri METIN="ABCDEF_ABCDEFz" echo "${METIN}" ABCDEF_ABCDEFz echo "${METIN//CDE}" ABF_ABFz echo "${METIN//CDE/xy}" AbxyF_AbxyFz echo "${METIN/#CDE/www}" ABwwwF_ABCDEFz echo "${METIN/%CDE/www}" ABCDEF_AbwwwFz echo "${METIN#A*C}" DEF_ABCDEFz echo "${METIN##A*C}" DEFz echo "${METIN%C*Fz}" ABCDEF_AB echo "${METIN%%C*Fz}" AB 57
Sayısal İşlemler SAYI=12 let TOPLAM=SAYI+5 $((TOPLAM=SAYI+5)) # Tamsayı olmalı # $ işareti OLMAMALI # Aynı işlem $((SAYI++)) $((SAYI--)) $((TOPLAM=SAYI-3)) $((TOPLAM=SAYI%7)) # 1 artırır # 1 eksiltir # dört işlem # mod, kalan TAMSAYI=${SAYI//.*} # tamsayıya çevirme 58
Fonksiyon Tanımlama
Fonksiyon Tanımlama function boldecho( ) { echo -e "\033[1m" "$*" "\033[0m" } boldecho "Bu metin bold yazılacak" 60
Fonksiyon Tanımlama Daha karışık bir örnek olarak free komutundan % cinsinden boş bellek miktarını veren bir fonksiyon yazalım: free -g Yani (100*free)/total gerekli total used free shared buffers cached Mem: 15 13 2 0 0 2 -/+ buffers/cache: 9 5 Swap: 7 1 5 61
Fonksiyon Tanımlama free -g grep 'Mem:' Mem: 15 13 2 0 0 2 free -g grep 'Mem:' awk '{print $2}' 15 free -g grep 'Mem:' awk '{print $4}' 2 TOPLAM=`free -g grep 'Mem:' awk '{print $2}'` BOS=`free -g grep 'Mem:' awk '{print $4}'` 62
Örnek function bosyuzde() { TOPLAM=`free -g grep 'Mem:' awk '{print $2}'` BOS=`free -g grep 'Mem:' awk '{print $4}'` $((SONUC=(100*BOS)/TOPLAM)) echo $SONUC } YUZDE=`bosYuzde` echo "Diskin %${YUZDE} 'u boştur." 63
Hata Ayıklama
Hata Ayıklama #!/bin/bash -v Komutları çalıştırmadan önceki halini yazar. Aldığımız hatanın hangi satırda oluştuğu görülür. #!/bin/bash -x Komutları çalıştırırken, işledikten sonra yazar. Alt process lerin komutlarını da yazar. Çok miktarda çıktı oluşturur. Önce -v denenmeli. 65
Son, Teşekkürler.