Bash

bash (bourne-again shell) sistemi kurulu olduğu sistem üzerindeki programları çağırabileceğiniz yetenekli bir arayüzdür, kabuktur.

shell (kabuk) ise temelde kullanıcıdan aldığı komutları işletim sistemi komutlarına dönüştüren bir programdır aslında.

bash en çok kullanılan komut satırı arayüzüdür. Linux, Unix, MacOS, Windows (subsystem olarak) işletim sistemlerinde genellikle bash  varsayılan komut satırı arayüzüdür. (Not: MacOS’un Catalina isimli sürümüyle birlikte MAC’de varsayılan olarak artık bash yerine zsh kullanılmaya başlanmıştır)

bash 1989 yılında Brian Fox tarafından geliştirildi ve yoluna bir GNU projesi olarak devam ediyor. Geliştirmeler uzun bir süredir Chet Ramey tarafından sürdürülüyor. Özellikle gelişmeler için kendisinin sayfası takip edilebilir.

Bir programlama dilinde olması gereken bir çok özelliği de desteklediği için bash script bazen programlama dili olarakta lanse edilir.

Bash, string based output üretir. bash çok temiz ve basit kullanıma sahiptir. Basit olması onu kullanmayı kolaylaştırır. Bu sebeple çok popülerdir.

Bu yazıyı yazdığım tarih itibariyle yayında olan en son bash sürümü 5.0. Ancak her üretici farklı sürümler kullanıyor.

Bu yazı da Linux/Unix ‘i anlatmıyorum. Bash ve özelliklerinden bahsediyor olacağım.

# WSL

WSL (Windows Subystem for Linux) Windows 10’larda artık bir özellik olarak geliyor. Dilerseniz Control Panel özelliklerinden bunu kolaylıkla aktif edebilirsiniz.

Aktif ettikten sonra Microsoft Store’dan Ubuntu’tu indirerek bir alt sistem olarak Ubuntu’yu kullanabilirsiniz. Haliyle bash’i de kullanabilirsiniz.

# Shell, Bash Version

Kullandığınız kabuğun ne olduğunu görmek için genellikle $SHELL değişkenine bakabilirsiniz.

echo $SHELL

Kullandığınız bash sürümünü görmek için ise

bash --version

# Interactive & Non-Interactive Shell

Terminalinizi açtığınızda ekranınıza düşen ekran interactive shell, bu çalışma şekline ise interactive mode denilir.

Bash’in arkaplanda çalışma durumuna ise non-interactive shell, bu çalışma şekline ise non-interactive mode denilir.

Terminali açtığınızda bash , mevcut login session’ınızla açılır. exit komutunu girerek session’ı, prosesi sonlandırabilirsiniz.

# Prompt

bash’de interactive mode’da iken promptda göreceğiniz aşağıdaki karakterlerin anlamı:

$ :  Komut girebileceğinizi belirtir, Yetkisiz bir kullanıcı ile komut çalıştırabilirsiniz.

# : Komut girilebileceğinizi belirtir. root yetkisiyle komut çalıştırabilirsiniz.

# Type of Command

Bash’de girilen ifade/komut bir keyword, bash built-in komutu ya da bir dosya olabilir.

Bash builtin girilen komutun bash’in içinde bir komut olduğunu belirtir.

Bir komutun ya da bir kelimenin bash built-in olup olmadığını type komutu ile görebilirsiniz.

type type
type if
type -t vim

compgen aracı bir bash builtin’dir. Bu araçtan yararlanarak tüm builtin’ler, keywordler, aliaslar, diğer komutları listelenebilir.

compgen -b
comgen -k
compgen -a
compgen -c

#Help, –help, man

Help bir bash builtin komutudur. Bash builtin komutları hakkında bilgi verir.

help cd

Aşağıdaki komut ise tamamen farklıdır. Çünkü zip bir builtin komut değildir. Kendisine ait help sayfası vardır.

zip --help

Help komutunu, –help parametresini man page’lerle karıştırmamak gerekiyor. man page çok daha detaylı dokümanlardır.

man komutu builtin bir komut değildir. bash ‘in kendisinden bağımsızdır.

Her komutun help’i her komutunda man page’i olmak zorunluluğu yoktur.

Man page’lerle ilgili bilinmesi gereken temel şey bir komut, fonksiyon için man sayfasına baktığınızda sayfaların bölümlerden oluşuyor olmasıdır.

# Standard Input / Output / Error

Bash ‘i çalıştırdığınızda aşağıdaki 3 file description’ı otomatik olarak açılır.

file2 - Bash

Standard input (stdin) file descriptor 0 olarak tanımlanır.

Standard output (stdout) file descriptor 1 olarak tanımlanır.

Standard error (stderr) file descriptor 2 olarak tanımlanır.

Varsayılan olarak bu üç file descriptor terminala varsayılanda yönlendirilir.

# Redirectors

stdout için > ve >> operatörlerini kullanarak başka bir file description’a çıktıyı yönlendirebilirsiniz. stdout için 1 ifadesini kullanmanıza gerek yoktur.

> operatörü aynı isimde bir dosya varsa üzerine yazarken, >> operatörü yönlendiriliği dosyanın sonuna ekleme yapar.

echo "hello" > test.txt
echo "world" >> test.txt

stderr için 2> ve 2>> operatörlerini kullanarak çıktıyı yönlendirebilirsiniz.

asdsad 2> hata.txt
dasd 2>> hata.txt

stdout ve stderr ‘e aynı anda yönlendirmek için &> ve &>> operatörlerini kullanabilirsiniz. Ya da ard arda yönlendirerek kullanabilirsiniz.

ls &> liste.txt
ls > liste.txt 2>hata.txt

stdout ve stderr çıktılarına /dev/null‘a yönlendirerek tamamen kurtulabilirsiniz.

cat test.txt > /dev/null
cat test.txt 2> /dev/null

Dilerseniz çıktıyı yine stdout veya stderr’e yönlendirebilirsiniz.

cat test.txt >&1
cat test.txt >&2

stdin için < operatörünü kullanarak girdiyi ilgili komuta verebilirsiniz.

cat < liste.txt

# Pipe

| operatörüyle ifade edilir, unnamed pipe olarakta bilinir. Output’u alır ve standart input’a yönlendiren bir i/o redirector’dur.

ls | grep "test.txt"

Pipe’daki önemli nokta kendinden önceki komutun hata almadan output göndermesidir.

Pipe dediğimiz mekanizma kendinden önceki ve sonraki processlerin haberleşebilmesi için bir boru hattıyla kanal oluşturur.

# echo, printf

bash’de standart output’a argümanlarımızı yazdırmak için için echo ve printf built-in komutlarını kullanabiliriz.

Bu iki komut arasındaki fark; echo ekrana yazdırırken sonuna \n karakterini koyarken, printf \n karakterini koymaz. echo komutu eğerki -n parametresini alırsa printf gibi \n karakterini eklemez.

echo "hello"
echo -n "hello"
printf "hello\n"

printf komutunu echo komutuna neden tercih etmeniz gerektiği sorusuna, printf komutu içerisindeki format yapısı argüman alabilmektedir diyebiliriz. Örneğin

printf "%s %s\n" gokhan kesici
printf "%s kesici\n" gokhan

printf komutu bir çok formatı desteklerken, aşağıdaki format’lar en önemlileri diyebiliriz:

  • %c : ASCII Chracter
  • %d : decimal integer
  • %i : integer
  • %f : float point
  • %s : string
  • %x : unsigned hex

Bir diğer format şeklide çıktının genişliğinin ayarlanabilir olmasıdır. – ise sola, + ise sağa yatık demektir.

printf "%+10s\n" gokhan

# Variable

Değişken tanımlama oldukça basit.

a=2
b='merhaba dunya'

Değişken değerini $degiskenadi şekinde alabiliyoruz.

echo $a
echo $b

Önemli bir konu da değişken, = ve data arasında boşluğun olmaması gerektiğidir. Aksi durumda komut olarak algılanır ve hata alırsınız. Örneğin aşağıdaki ifadeler hatalıdır.

a = 3
b= 2
c =3

Değişkenleri  string içinde kullanabiliriz. Bunun için çift tırnak kullanırız. Tek tırnak ile bu gerçekleşemez.

a=7
b="ugurlu sayim $a"
echo $b

Bir diğer değişken çağırma yöntemi ise ${degiskenadi}. Aşağıdaki örnekteki kullanımlar için bu gösterim faydalı olacaktır. Bu kullanım ayrıca array başlığında bahsettiğimiz dizilerin çağrılması içinde kullanılmaktadır.

il=34
echo ${il}TR

# Environment Variables

Çevresel değişkenler kendi oluşturduğumuz tanımalar dışında, birden çok programın işletim sistemi üzerinde tanımlı sistem veya kullanıcı tabanlı değişkenleri kullanabilmesi için oluşturulmuştur.

Çevresel değişkenleri görebilmek için env veya printenv komutlarından birini kullanabilirsiniz.

printenv
env

Interactive mod’da tanımladığınız bir değişkenin ortam değişkeni olarak listelendiğini yukarıdaki komutlardan da anlayabilirsiniz.

export ad=gokhan
printenv | grep ad

Bir değişkeni kaldırmak için onu unset etmeniz yeterlidir.

unset ad
printenv | grep ad

# export variable

Export komutu builtin bir komuttur. export komutuyla değişkeni child process’in görmesi sağlanabilir.

Export edilmiş değişkenleri görmek için aşağıdaki iki seçeneği de kullanabilirsiniz.

export
export -p

Değişkeni export ederseniz child process değişken tanımını görecektir.

test="test"
export test
bash
echo $test

export işlemini ayrıca declare komutu ile gerçekleştirebiliriz. declare başlığında konuya değindik.

# Prompt Variables 

Prompt, linux da terminalizde size nerede, hangi dizinde hangi yetkilerle olduğunuzu gösterebilen kullanıcı dostu bir bilgidir.

Prompt’da nelerin gözükeceğini kendiniz belirleyebilirsiniz.

Bash ‘de PS1, PS2, PS3, PS4 olmak üzere 4 değişkende bu promptlar tutulur.

echo $PS1

PS2 ikincil promptur. Bu satır bitmediği devam ettiği durumlarda çıkan promptur.  Varsayılan olarak > karakteridir.

echo "Hello
>World

PS3 select komut gibi seçim yapılma durumlarında çıkan promptur.

select name in gokhan kesici; do echo $name; done
1)gokhan
2)kesici
#?:1

PS4 ise debug durumlarda görüntülenen promptur. Defaultta + işaretidir.

set -x
ls
+ ls
dosya1.sh dosya2.sh

# Path Variable

Komutlar birer dosyadır ve bu komutlar dosyaların olduğu dizinlerde bulunurlar. Sırasıyla belirtilen komutların hangi dizinlerde araması gerektiğini PATH değişkeninde tanımlarız.

echo $PATH

Örneğin bir komutun çağrıldığında hangi dizinden çağrıldığını görmek için type veya which komutlarını kullanabilirsiniz.

which grep
type grep

# Command Substitution

Bir komutun çıktısını kullanmak için iki farklı form bulunmaktadır.

Bunlardan ilki $(komut) formudur.

liste=$(ls)
echo $liste

Diğer form ise `komut` formudur.

liste=`ls`
echo $liste

# Here Document (heredoc)

Heredoc aslında bir çok programlama dilinde olan bir özelliktir.

<<Label şeklinde ifade edilen özel bir input yönlendirme şeklidir.  Çok satırlı text girdisini solundaki komuta iletir.

cat << EOF
merhaba
dunya
EOF

Text kısmı aslında ” “ içeren bir stringdir. Bu yüzden de değişken değerlerini gösterebiliriz.

degisken="dunya"
cat << EOF
merhaba
$degisken
EOF

Değişkeni göstermek istemiyorsak eğer <<‘label’ ifadesini kullanabiliriz. Aşağıdaki örnekte  ” ” ifadesini ‘ ‘ dönüştürdüğümüz için değişken değeri gözükmeyecektir.

degisken="dunya" 
cat << 'EOF' 
merhaba 
$degisken 
EOF

# Comment 

Özellike bash script yazarken kullanılan yorum satırlarını aşağıdaki gibi kullanabilirsiniz

#list root directory
ls /root/

Çok satırlı yorum yazmak için bir tanım yok. Ancak farklı yöntemlerle çoklı satır oluşturulabiliyor.

Bunlar içerisinden ilki : ‘ ‘ ifadesi arasına yorum yazmaktır. : aslında bir komut olduğu için : ‘dan sonra boşluk bırakmanız gerekir.

: 'list root 
directory'
ls /root/

Diğer bir seçenek ise marker kullanmaktır.

<<yorum1
list root
directory
yorum1
ls /root/

# Parent working directory, Change directory

Bulunduğunuz çalışma dizinini görüntülemek için:

pwd

Çalışma dizinini değiştirmek için:

cd /root/

Bir önceki çalışma dizinine dönmek için aşağıdaki kısayolu kullanabilirsiniz.

cd -

Bir üst dizinine çıkmak için:

cd ../

Mevcut dizini işaret etmek için:

cd ./liste.txt

# Tilde 

~ karakterinin özel bir anlamı bulunur.  Kullanıcının home dizinini işaret eder.

cd ~
cd ~gokhan

# pushd, popd

cd – komutunu bir önceki dizine gittiğinde bahsetmiştik. Ancak bu bir seviye ilerleyebiliyorduk.

pushd komutlarıyla çalışmak istediğimiz dizini DIR stack’e atıp, popd dediğimizde ise stack’den çıkarabiliriz.

Bilinemsi gereken en önemli şey pwd’nizin stack 0 olmasıdır.

pushd ‘de +1 -1 gibi argümanlarla stack’dekilerin konumlarını yukarı aşağı taşıyabilirsiniz. popd’de benzer argümanlarla ilgili argümanda sırasındaki dizin çıkartılabilir. Bu taşıma sırasında stack 0’daki dizin pwd’niz olacaktır.

pushd dir1
pushd dir2
pushd dir3
pushd +1
popd
popd -1

dirs komutu ile stack’i görebilirsiniz. Stack 0’dan başlar ve dilerseniz +2 -2 gibi argümanlarla direkt stack’deki dizin görülebilir.

dirs -v
dirs +2 # soldan sağa 3. eleman
dirs -2 # sağdan sola 3. eleman

Stack kullanırken cd – komutunuzu kullanırsanız stack 0’ın değiştiğini göreceksiniz.

# Escaping

Bir çok programlama dilinde olduğu gibi kaçış operatörümüz \ ifadesidir.

echo 3 \> 2

# jobs

Bir komutu arka planda çalıştırmak için & işaretini kullanırız.

tail -f /var/log/messages &

Arkaplanda çalışan jobları listelemek jobs komutunu kullanırız.

jobs
[1]+  Running                 tail -f /var/log/messages &

Çıktıdaki 1 ifadesi bash tarafından atanmış bir job numarasıdır. + ifadesi ise son çağrılan background job olduğunu gösterir.  – ifadesi ise sondan 2. çağrılan job olduğunu gösterir.

Job’lar çeşitli statüleri bulunur. Örneğin bir üst örneğimizdeki 1 numaralı job hala çalışıyor durumdadır. Örneğin aşağıdaki komut ise arkaplanda atıldıktan sonra tamamlanan bir job’dır.

ls -l &
[2]+ Done

Job bir şekilde hata alıp sonlanırsa exit statüsünde job’ımız görüntülenir.

lsl &
[3]+ Exit 127

fg komutuyla arkaplanda çalışan bir job’ı arkaplandan çıkararak  ön plana (foreground) getirebilirsiniz.

fg komutuna parametre vermezseniz jobs listesindeki son job’ı getirir. Job id’sini % ile belirterek direk ilgili background job çağrılabilir. % ifadesi yanında numara yerine string yer alıyorsa ilgili string’le başlayan komut öne getirilir. %? ‘de ise ilgili string’i içeren komut ön plana getirilir. %string ve %?string’ler birden fazla job ile eşleşirse belirsizlik söz konusu olduğu için öne hiç bir eşleşen job getirilmez. %+ son job’ı , %- ise sondan bir önceki job’ı öne getirir.

fg 
fg %1
fg %tail
fg %?messages
fg %+
fg %-

background’daki job’ların process id’sini görüntülemek için aşağıdaki iki parametreyi de kullanabilirsiniz.

jobs -l
jobs -p

Ön planda çalışan bir prosesi suspend edip arka plana atayabiliriz. Bunun için CTRL+Z tuş kombinasyonuyla yapabilirsiniz. Suspend edilmiş proses jobs listesinde gözükür ve statüsü Stopped’dir.

tail -f /var/log/messages
CTRL + Z
[2]+ Stopped tail -f /var/log/messages

Stopped statüsündeki job’ı tekrar Running statüsüne getirmek için fg komutuyla ön plana getirebiliriz. Ya da arkaplanda running statüsünde devam etmesi için bg komutunu kullanabiliriz.

fg %2
bg %2

Bir job’ı öldürmek için kill komutunu kullanabiliriz. Kill olan proses terminated statüsündedir.

kill %2
[2]+ Terminated tail -f /var/log/messages

# History

Terminalde bash kabuğuna girdiğiniz komutlar kayıt altına alınır.

Kayıt yeri kullanıcının home dizinindeki .bash_history dosyasıdır. Aşağıdaki değişken ile de görme şansınız var.

echo $HISTFILE

.bash_history’de tutulan komutlar değerli olabilir. Çünkü parolanızı girmiş olabilirsiniz.

History ‘de ne kadar komutun tutulduğunu görmek için aşağıdaki değişken görüntlenir. Geçmişi history komutuyla silebilirsiniz.

echo $HISTFILESIZE
history -c

Aslında tüm yapılandırmalar bashrc denilen initial script tarafından yapılandırılır.

Özellikle bir linux ile bir mac’deki bashrc dosyalarını incelerseniz yapılandırmaların farklı olduğunu göreceksiniz.

bashrc de bu değerleri değiştirerek veya bu değişkenleri değiştirerek istediğiniz değişikliği yapabilirsiniz.

export HISTFILESIZE=4000

History’deki bir komutu ! operatörüyle çağırabilirsiniz.

!! : son komut

!2 : ikinci komut

!-2 : sondan ikinci komut

!string : son kullanılan ve string ile başlayan komutu çalıştırır.

# profile, bash_profile, bashrc, bash_logout

  • /etc/profile dosyası sistem tabanlı bir profil dosyasıdır. Home dizininizde bash_profile yoksa sistem tabanlı bu dosya aktif olur.
  • .bash_profile  / .bash_login / .profile home dizininizde bulunur. Bu dosyalardan biri ve /etc/profile içindeki yapılandırmalar siz shell’e login olduğunuzda çalışan script’dir.
  • .bashrc siz bash’deyken aynı terminalde bash yazıp tekrar bir kabuk açtınız. Bu durumda çalışan script .bashrc’dir.
  • .bash_logout  bash’den çıkıldığında çalışan script’dir.

# Alias

Girdiğiniz komutlar için kendi belirlediğiniz isimlerle alias ile değiştirebilirsiniz. alias diyerekte mevcut aliasları görebilirsiniz.

alias rcrsv='grep -R'
alias

alias içinde alias tanımlanabilir.

alias myrc=rcrsv

# set, shopt

bash’deki özellikleri açıp kapayabilirsiniz.

Builtin olarak set komutuyla bunu gerçekleştirebilirsiniz. – işareti on + işareti ise off anlamına gelmektedir.

set -x
set +x
set -o noglob
set +o noglob

bash 2.0 ile birlikte shopt komutu gelmiştir. Bu komut ile set ile benzer işlevi gören daha fazla ayar yapabileceğiniz builtin bir komuttur.

shopt -s globstar
shopt -u globstar

# Hash commands

Komutlar sırasıyla PATH değişkeninde tanımlı dizinlere bakılarak aranır. Tekrar girdiğiniz bir komutu gidip tekrar aramamak için default da bash hash özelliği aktif gelir.

hash komutunu kullanarak hashlenmiş komutları görebilirsiniz. Ayrıca o komutu kaç kere çalıştırdığınızı da sayar.

hash table’i aşağıdaki komutla temizleyebilrsiniz.

hash -r

set komutunda hashall seçeneğini ile hash özelliğini açıp kapatabilirsiniz.

# Terminals

Terminal denilen şey kabuğa girdi gönderip çıktı alabildiğimiz bir arayüz.

Kernel içerisinde TTY (teletype) driver bulunmaktadır. Klavyeden girilenler kernel terminal emülatörle -ki biz buna console diyoruz- TTY driver’a iletilir. Buradan da ilgili process’e aktarılır.

ps komutunun çıktısında prosesisn tty’si ‘de belirtilir.

tty - Bash

Diyelim ki gui’li kali gibi bir linux dağıtımı kullanıyoruz ve terminal programını kullanmak istiyoruz. Bu aslında console tipi değil user space terminal emülatör oluyor.  Bunlara pseduo tty (PTY) deniliyor.

PTY , master ve slave adında iki kanaldan oluşuyor. Master, user space’deki terminal programınız, slave  ise yine user space’deki prosesiniz.

Örneğin terminali ilk açtığınızda bash kabuğu aktif geliyor. Bu durumda ilk açılışta master echo $TERM komutununda çıktısındaki terminaliz (örneğin xterm), slave ise bash ‘dir.

terminal - Bash

ps komutunun çıktısında da görebileceğiniz pts ifadesi slave’i temsil eder. Aşağıdakine ek yeni bir terminal daha açarsanız pts/1 ifadesini göreceksiniz.

ps 
PID TTY TIME CMD
1253 pts/0 00:00:00 bash
3519 pts/0 00:00:00 tail
4550 pts/0 00:00:00 ps

Bağlı olunan tty character device’ı görmek için tty komutunu kullanabilirsiniz. Terminal ayar değişkliği için stty komutunu kullanabilirsiniz.

# Shell Command  Precedence

Shell’e girilen komutlar belirli öncelik sırasına göre aranır. Sırasıyla aşağıdaki gibidir.

  1. Alias
  2. Keyword
  3. Function
  4. Builtin command
  5. Scripts and executable programs

Executable programlar bahsettiğimiz üzere $PATH değişkeninde arar.

# builtin

Bir üstteki başlıkta öncelikte fonksiyonun builtin komutlardan önce geldiğini görebilirsiniz.

Diyelimki cd isimli komut bir bash builtin ve siz kendi cd isimli fonksiyonunuzu yazmak istiyorsunuz ve içerisinde yine cd komutunu kullanmak istiyorsunuz.

Bu gibi durumlarda komutun öncesine builtin ifadesini kullanmak yeterli olacaktır.

# Operators

assigment ( = )

  • = ifadesi hem string hem de bir sayıyı atamak için kullanılır.
  • Öncesinde ve  sonrasında boşluk bırakılmaz.
  • Örnekler:
    • sayi=20
    • ad=gokhan

arithmetic ( +, -, *, /, **, %, ++, –, +=, -=, *=, /=, %= )

  • Toplama, çıkarma, çarpma, bölme, üs, mod,  artırma/azaltma gibi bir çok aritmek işlemi gerçekleştirebilirsiniz.
  • bash’de integer değerler bulunur, float değerler bulunmaz.
  • Aritmetik değerler $((işlem )) şeklinde kullanılır.
  • Bazı Örnekler:
    • echo $(( 2 + 3 ))
    • echo $(( -2 – 3 ))
    • echo $(( 4 * 2 ));
    • echo $(( 4 / -2 ))
    • echo $(( 2** 3))
    • a=2; echo $(( ++a )); echo $(( a– ))
    • echo $(( 4++ ))
    • b= 3; echo $(( b+=1 ))
    • echo $(( 3– ))

logical ( !, &&, || )

  • ! , NOT anlamına gemektedir.
  • &&, AND anlamına gelmektedir.
  • ||, OR anlamına gelmektedir.
  • Örnekler:
    • if [ ! -f $dosya ]
    • if [ $condition1 ] && [ $condition2 ]
    • if [ $condition1 ] || [ $condition2 ]

bitwise (<<, >>, &, |, ~,^,&= )

  • <<, shift left bit işlemini gerçekleştirir.
  • >>, shift right bit işlemini gerçekleştirir.
  • &, and işlemini gerçekleştirir .
  • |, or işlemini gerçekleştirir.
  • ~, not işlemini gerçekleştirir.
  • ^, xor işlemini gerçekleştirir.
  • &=, xor-equal işlemini gerçekleştirir. Diğer bitwise operatörlerinde de = ifadesi kullanılabilir.
  • Örnekler:
    • echo $((101 >> 2))
    • echo $((101 << 3))
    • echo $((111 & 101))
    • echo $((111 & 101))
    • echo $((~101))
    • echo $((111 ^ 101))

comma (,)

  • , operatörü aritmetik işlemlerde kullanılır.
  • Örneğin aşağıdaki çıktı ilk olarak 8 daha sonra 5 ifadesini çıktı olarak üretecektir. Bunun sebebi aritmetik işlemin yapıldığı yerde son girilen komut çıktı olarak üretilir.
    • a=3; echo $((a+=2, 10-2)); echo $a

# expr

bash’de aritmetik işlemlerinizi $((işlem )) şeklinde kullanmak yerine bash ‘de builtin olarak bulunmayan ancak linux’da var olan expr komutuyla da yapabilirsiniz.

expr  2 + 3
expr 2 \* 3
expr 2 \< 3
expre 4\/2

expr komutuyla bir string’in uzunluğunu öğrenebilirsiniz.  Bunun yanı sıra help’inden görebileceğiniz bazı farklı opsiyonlarda kullanılabilmektedir.

expr length "gokhan"

# String Operators

String operatörleri ile bash’de diğer linux/unix komutlarını kullanmadan bizlere stringlerle işlemler yapmamızı sağlar.

String operatörleri { } içerisinde kullanılır.

degisken=gokhan
echo ${#degisken} # 6
echo ${degisken:1} # okhan
echo ${degisken:0} # gokhan
echo ${degisken:0:2} # go
echo ${degisken:0:-2} # gokh
echo ${degisken: -2} # an
echo ${degisken#g*} # okhan. eşleşen eğer stringin basıysa en kısa string pozisyonundan öncesini kaldırır.
echo ${degisken##g*} # eşlesen eğer stringin basıysa en uzun string pozisyonundan öncesini kaldırır.
echo ${degisken%n} # gokha . eşlesen eğer stringin sonuysa en kısa eşleşilen pozisyondan sonrasını kaldırır. 
echo ${degisken%%n} # esleşen eğer string sonuysa en uzun eşleşilenden pozisyondan sonrasını kaldırır.
echo ${degisken/k/kk} # gokkhan . ilk eşleşileni değiştirir.
echo ${degisken//k/kk} # gokhan . tüm eşleşilenleri değiştirir.

# Matching Operators

shopt çıktısında eğer extglob özelliği aktifse  ki genelde aktifdir, diğer eşleşme operatörlerini kullanabiliriz.

ls * # wildcard
ls *.sh # wildcard
ls ?.sh # tek karakter
ls [d1] # küme içerisindeki elemanları içerirse listeler.
ls ?(a|b).pdf # Hiç veya bir tane a veya b içeren dosyaları listeler. (extglob)
ls *(a|b).pdf # Hiç ya da çok fazla a veya b içerebilen dosyaları listeler. (extglob)
ls +(a|b).pdf # Bir veya daha fazla a veya b içerebilen dosyaları listeler. (extglob)
ls @(a|b).pdf # Bir a veya b dosyası içerebilen dosyaları listeler. (extglob)
ls !(a|b).pdf # Bir a veya b 'yi içermeyen dosyaları listeler. (extglob)

# Condition

if ile birlikte condition’lar aşağıdaki şekillerde ve örnekler gibi kullanılabilir.

if command

  • komutun exit kodu 0 ise koşul sağlanır.
  • Örneğin:
    • if ls -l

if  (command)

  • komutun exit kodu 0 ise koşul sağlanır.
  • Parantez içinde olması bir subshell de çalışmayı sağlar.
  • Örneğin:
    • if (ls -l)

if  [ condition ]

  • builtin test komutudur aslında. test komutu dosya tiplerini ve değerleri karşılaştırmak için kullanılır.
  • [ ifadesi aslında builtin ‘dir ve which komutuyla kontrol ederseniz bir programdır aslında.
  • test komutu bir diğer değişle [ condition ] çıktı üretmez, başarılıysa 0 yani true, başarısızsa 1 değerini yani false döner.
  • Bu yapıda regular expression, pattern matching karşılaştırması yapılamamaktadır.
  • Örneğin:
    • [ -a dosya ] : Dosya varsa True döner.
    • [ -b dosya ] : Dosya block special ise True döner.
    • [ -c dosya ] : Dosya character special ise True döner.
    • [ -d dizin ] : Dizin varsa True döner.
    • [ -e dosya ] : Dosya varsa True döner.  -a ve -e geriye uyumluluk açısından birlikte bulunmaktadır.
    • [ -f dosya ] : Dosya regular bir dosya ise True döner. Regular dosya demek text veya binary olan; socket,symbolic link olmayan dosyalardır.
    • [ -g dosya ] : Dosya için setgid biti girilmişse True döner.
    • [ -h dosya ] : Dosya sembolink ise True döner.
    • [ -L dosya ] : -h ile aynıdır. Sembolink ise True dönr. -h -ve – geriye uyumluluk için birlikte bulunmaktadır.
    • [ -k dosya ] : sticky bit set edilmişse True döner.
    • [ -p dosya ] : Dosya pipe tipinde ise True döner.
    • [ -r dosya ] : Dosya okunabilirse True döner.
    • [ -s dosya ] : Dosyanın boyutu 0 ‘dan büyükse True döner.
    • [ -S dosya ] : Dosya bir socket ise True döner.
    • [ -u dosya ] : setuid set edilmişse True döner.
    • [ -o option ] : Shell option aktifse True döner.
    • [ -w dosya ] : Dosya yazılabilirse True döner.
    • [ -x dosya ] : Dosya yazılabilirse True döner.
    • [ dosya1 -nt dosya2 ] : Dosya1 dosya2 ‘den daha güncel içeriğe sahipse True döner.
    • [ dosya1 -ot dosya1 ] : Dosya1 dosya2 den daha eski içeriğe sahipse True döner.
    • [ dosya1 -ef dosya2 ] : Aynı inode değerine sahiplerse True döner. Yani hard link ise.
    • [ -z string ] : String boşsa true döner.
    • [ -n string ] : String boş değilse true döner.
    • [ String1 = String2 ] : String1 ve string2’ye eşitse True döner.
    • [ String1 == String2 ] : String1 ve string2’ye eşitse True döner. Bir üstek ki tek = ile aynı şeydir.
    • [ String1 != String2 ] : String1 ve string2’ye eşit değilse True döner.
    • [ String1 \< String2 ] : String2 String1 ‘den alfabetik olarak daha sonra geliyorsa True döner.
    • [ String1 \> String2 ] : String1 String2’den alfabetik olarak daha sonra geliyorsa True döner.
    • [ Integer1 -aritmetikoperatör Integer2 ] : Aritmetik karşılaştırmalar için -eq,ne,lt,le,gt,ge ifadelerini kullanabiliriz.

if  [[ condition ]]

  • [[ , [‘den farklı olarak bir shell keyword’dur. Yani bir program değildir. [‘nin moden hali diye de lanse edilir.  Ksh kabuğundan bash’e gelmiştir.
  • Ek olarak regex ve pattern matching karşılaştırma yapılabilmektedir.
  • \ kaçış ifadesinin kullanılmasına gerek bulunmamaktadır.
  • Örneğin
    • [[ String1 == S* ]] : Pattern matching ifadeleri ile karşılaştırabiliriz.
    • [[ String1 > String2 ]] : \> şeklinde belirtmek zorunda kalmadık.
    • [[ String1 =~ ^S ]] : Regex ifadeleri ile karşılaştırabiliriz.

if  (( condition ))

  • Ksh kabuğundan bash’e gelmiştir.
  • Operatörler başlığında bahsettiğimiz aritmetik işlemler için kullanılabilir.

# if/else

Aşağıda if kullanımına en basit örnektir.

if [ 1 -gt 0 ]
then
echo "Buyuktur"
elif [ 1 -eq 0 ]
then
echo "Esittir"
else
echo "Kucuktur"
fi

if koşulu içerisinde komutta çalıştırılabilmektedir.

if [ $(ls -l 2> /dev/null)  ] 
then 
echo "dogru"
else
echo "yanlis"
fi

# Function

İki formda function oluşturulabilir.

İlk form function ifadesini kullanarak script içerisinde tanım yapmak ve fonksiyonu çağırmak.

function isim { echo gokhan; }
isim

Diğer form ise function ifadesini kullanmadan fonksiyon oluşturmak.

isim() { echo gokhan; }
isim

# return, exit

Bash’de return klasik programlama dillerinden biraz farklı kullanılıyor. Normalde bir fonksiyon return içeriyorsa return değeriyle döner.

Bash’de bir fonksiyon çağrıldığında son komutun return değeriyle döner ki başarılıysa bu 0, değilse 1-255 arasında bir değerdir.

return ifadesiyle bu değeri kendimiz belirleyebiliriz.

Benzer şekilde programdan çıkışımız statüsünü de exit ile belirleyebiliriz.

Aşağıdaki örnekte önce fonksiyondan 2 ile çıkılır, sonra programdan 3 ile çıkılır.

ornek () {
echo "bu bir ornektir"
return 2
}
ornek
echo $?
exit 3

# Positional Parameters

Fonksiyona parametreleri iletebilir bu parametreleri pozisyona bağlı olarak kullanabilirsiniz.

isim() { echo $0; echo $1; echo $2; echo "Toplam $#"; echo "Parametreler [email protected]"; }
isim gokhan kesici
echo $1
  • $# değişkeni toplam parametre sayısını belirtir.
  • $* değişkeni tüm parametreleri tek stringde IFS tanımlı ayraç neyse ona göre gösterir.
  • [email protected] değişkeni tüm parametreleri  tek stringde IFS bakmaksızın boşluklu ifade ile gösterir. Genelde $* ile aynı sonucu dönerler.
  • $0 değişkeni çalıştırılan scriptin adını, komutu ifade eder.
  • Son satırda yazdığımız echo $1 ifadesi fonksiyonun argümanı değil scriptin 1 numaralı değişkenin adıdır.

# Scope

Bash’de script yazıyorsanız değişkenleriniz fonksiyon içerisinde olsa dahi global scope‘dur. Herhangi bir yerden erişilebilir.

isim() { echo gokhan; sayi=2; }
isim
echo $sayi

Yukarıdaki örnekteki sayi değişkenin global değilde local olmasını istiyorsanız local ifadesini kullanmanız gerekir.

isim() { echo gokhan; local sayi=2; }
isim
echo $sayi

Fonksiyon parametreleri localdir. Örneğin gidip bir script’e parametre verseniz bile bunu çağıramazsınız.

Örneğin aşağıdaki scriptimizi çağıralım. Fonksiyon $1 parametresini bilmeyecektir. Tek istisna $0 parametresidir. Bunu bilecektir.

function isim { echo $1; }
isim
./test.sh gokhan

# for

Aşağıdaki for kullanımı syntax ‘ı görülebilir.

for name in list
do
      statements can use $name
done

Örnek kullanımlar:

for i in 1 2 3; do echo $i;done
for i in {1..3}; do echo $i; done
for i in {0..12..2}; do echo $i; done
for i in $(seq 1 2 12); do echo $i; done
for dir in $PATH; do echo $dir; done

C syntax’ına benzer aritmetik artan for dongüsü oluşturmak için aşağıdaki syntax kullanılabilir.

for (( i=1; i<=3; i++)); do echo $i;done

# while / until

while ifadesi koşul sağlanıncaya dek do içerisindeki ifadelerimizi çalıştırır. False durumunda dongüden çıkılır.

while kullanımına örnekler:

i=0; while [[ $i -lt 5 ]]; do echo $i; i=$(($i+1));done
i=0; while [[ i -lt 5 ]]; do echo $i; i=$(($i+1));done
i=0; while [[ $i -lt 5 ]]; do echo $i; i=$[$i+1];done
while true; do tail -1f /var/log/syslog; sleep 3;done

until, while ile benzerdir. Koşul ifadesi doğru sağlanıncaya dek do ‘da tanımlı işlemi gerçekleştirir. True donülmesi durumunda döngü’den çıkılır.

until kullanımına örnek:

i=0; until [[ $i -gt 5 ]]; do echo $i; i=$(($i+1));done

# select

select ile menü yapısı oluşturulabilir. Örneğin:

select file in *;do cat $file; done

Select ifadesi PS3 prompt’unda yer alır. PS3 promptunun değişkenini değerini değiştirerek  menüde istenileni sunabiliriz.

PS3='Seciminiz:'

# case

case ifadesini koşul ifadelerinde kullanabiliriz.

Syntax yapısı aşağıdaki gibidir.

case expression in
 pattern1)
  statements
  ;;
 pattern2)
  statements
  ;;
 *)
  statements
  ;;
 esac

Dikkat edilmesi gereken şey pattern’ler ;;  özel karakteri ile sonlandırılmalıdır. Aksi durumda koşuldan hemen sonra koşulların sonlandırılma işlemi gerçekleşeceğinden diğer yazdıklarınız anlaşılmayarak syntax error alınır.

Örnek case kullanımı:

i=1
case $i in
1) echo "1";;
2) echo "2";;
*) echo "0";;
esac

# shift

Positional parameters bölümünde komutla birlikte yazılan argümanların aslında parametre olduğundan bahsetmiştik.

Bu parametrelerin bir diğer özelliği read-only olmasıdır. Örneğin aşağıdaki kullanım yanlıştır ve hata alınır.

isim() { 1=$2; echo $1; }
isim gokhan kesici

Ancak shift komutuyla argümanlar belirtilen N sayısı kadar sola kaydırılır.

isim() { shift 3; echo $1; }
isim gokhan kesici

# Commandline Options, getopts

Bash’de positional parametreler dışında – ifadesi ile opsiyon belirterek aşağıdaki gibi argümanları kullanabiliriz.

command [-options] args

Seçenekli argümanları shift komutundan yararlanarak case komutunuda kullanarak aşağıdaki gibi gerçekleştirebiliriz.

while [ -n "$(echo $1 | grep '-')" ];do
case $1 in
-h ) echo "help";;
-c ) echo "command";;
-d ) echo "del";;
* ) echo "usage: gokhan [-h] [-c] [-d]";;
esac 
shift
done

Komut satırındakileri parse edebilmenin daha şık yolu getopts kullanmaktır.  – ifadesi ile başlayan argümanlar birer opsiyon olarak otomatik parse edilir. getops her çağrıldığında bu opsiyonlar sırasıyla döner. Bu sebeple while döngüsü ile getopts birlikte aşağıdaki gibi kullanılır.

getopts optstring variable
  • optstring içerisindeki karakterler içerisinde kolon (:) ifadesi varsa bu ifade opsiyonu değil argümanı temsil eder. Başta kullanıldığında ise illegal option hatası gibi bir error mesajını almamak için kullanılır.
  • getops ile birlikte aşağıdaki değişkenler otomatik tanımlanır.
    • $OPTARG : her bir opsiyona ait argüman varsa bunu tutar.
    • $OPTIND : bir sonraki opsiyonun index’ini tutar.
    • $OPTERR : 0/1 ve hata mesajlarını tutar.

Örneğin kendi oluşturduğumuz scanner.sh dosyamızı aşağıdaki gibi çağıralım.

# scanner.sh
while getopts ":t:p:" o; do
case "${o}" in
t) t=${OPTARG}
echo $t
;;
p) p=${OPTARG}
echo $p
;;
?) echo "yanlis opsiyon";;
esac
done
./scanner.sh -t 192.168.1.1 -p 443
  • -t ve -p opsiyonumuz bir argüman almıştır.
  • : ifadesi ile başlamasının sebebi illegal

# Arrays

Bash’de tek boyutlu diziler desteklenmektedir.  Array’lere assign işlemini bir kaç şekilde gerçekleştirebilirsiniz. Aşağıdaki formlarda assigment işlemi yapılınca otomatik array oluşturulmuş olur.

b[2]=2
b[0]=gokhan

Diğer bir assign yapma şekli ise

b=([2]=2 [0]=gokhan)

Sıralı  veya karışık bir şekilde dizileri assign etmek için aşağıdaki yöntemler kullanılabilir.

b=(gokhan kesici)
b=(gokhan [3]=kesici istanbul) # 0,3,4

Boş bir dizi oluşturmak için declare komutu kullanılabilir.

Dizi değişkeni çağırmak için ise süslü parantez ${ } kullanımı gereklidir ve dizinin diğer elemanları boş kalabilir.

echo ${b[1]}
echo ${b} # index verilmezse b[0] demektir.

Array içerisindekileri görüntülemek için:

echo ${b[@]} 
echo ${b[*]}

Array boyutu öğrenmek için:

echo ${#b[@]} # 3

Array indexlerini öğrenmek için:

echo ${!b[@]} # 0 3 4

# declare

Variable’ları tanımlarken tipini deklare etmeden assign edebileceğimizi biliyoruz. declare komutu ile ise değişkenlerin tipini deklare edilebilriz.

declare ile birlikte kullanılan parametrelerin anlamları:

  • -i : değişken değeri integer olarak atanır.
  • -a : değişken dizi olarak atanır
  • -f : fonksiyon isimi belirtilirse fonksiyonun kendisini, belirtilmezse tüm fonksiyonları içindekilerle birlikte listeler.
  • -F: fonksiyon ismi belirtilirse fonksiyonun adını, belirtilmezse tüm fonksiyonların adlarını listeler.
  • -r : değişkeni read-only yapar.
  • -x : değişken export edilebilir yapmaktadır.

Örnekler:

declare -i a=1; a=one; echo $a; # son atanan değer bir integer olmadığından 0 olarak döner. 
declare -a a=3; echo ${a}; a[2]=5; echo ${a[2]}; 
declare -f; 
declare -f fonksiyon1;
declare -F; 
declare -F fonksiyon1;
declare -r a=1; (( a++ )) #hata alınır
declare -x a; # değişkenimizi export edebilmemiz sağlanır.

# read

read komutu standart input veya bir dosyadan aldığı okuduğu her bir satırı değişkenlere atar.

read adiniz; echo $adiniz
read -p "adiniz:" adiniz; # prompt
read -sp "parola:" parola; # silent prompt
read var1 var2 var3; echo $var3 $var2 $var1; # multiple variable

Eğer değişken adı verilmezse otomatik olarak $REPLY değişkene atama yapılır

read; echo $REPLY

Dosyadan satır satır okumak için:

while IFS= read line; do echo "test1 $line"; done <dosya1

# eval

eval yanına argüman olarak girdiğiniz değerleri tek bir string haline getirip tek komut olarak çalıştırır. evaluate’in kısaltması olan eval bir çok dilde bulunur.

list="ls -l | grep dosya"; eval $list
eval "ls -l | grep dosya"

Command Substitution ile eval’i karıştırmamak gerekir. Yani aşağıdaki gibi bir kullanım hatalıdır.

list=$("ls -l | grep dosya");

eval’ın kullanıldığı yerlere dikkat edilmelidir. input validasyon yapılmadığında ve kullanıcıdan input alınan durumlarda kullanımı oldukça tehlikelidir.

# ps

ps bir bash builtin komutu değildir.  Ancak yine de değinmeden olmayacaktır.

ps komutu  System-V(unix), BSD, GNU stil dedikleri formatlarda opsiyonlar almayı destekler.

  • Unix style: Opsiyonlar – işareti ile başlar
  • BSD style: Opsiyonlar – işareti ile başlamaz.
  • GNU style: Opsiyonlar — işareti ile başlar.

ps komutunda hiç bir parametre vermediğinizde çıktımız aşağıdaki gibi mevcut terminaldeki prosesleri listeler.

ps - Bash

En basit çıktı olan bu formda

  • pid: proses numarası
  • tty:  komutun çalıştığı terminali
  • time: basitçe ifade etmek gerekirse kullanılan cpu zamanı. Ama aslında wall clock time olarak ifade edilir.
  • cmd: çalışan komut

ps komutu bash komutu olmadığı için job numaralarıyla karşılaştırma yapamayacağından job id ‘leri göremeyiz.

ps -e veya ps -ax komutu ile tüm prosesleri listeleyebiliriz. Örneğin bu komutların sadece çıktılarında ufak farklılıklar vardır. Tüm prosesleri listelemenek için bir çok opsiyon vardır, ancak yine de en çok kullanılanlardan bazıları:

ps -e
ps -ef
ps ax
ps -ax
ps -A

Tüm proseslerin listelenmesi durumunda TTY kolonumuzda ? ifadesini görebilirsiniz.  Bu bir terminale gerek duymayan bir proses, bir daemon olabilir.

Bir ps komutu çıktısındaki diğer kolonların anlamları şunlardır:

  • UID: proses sahibinin kullanıcı adı
  • PPID: parent prosesin id’si
  • C, %CPU:  CPU kullanımı
  • %MEM: Bellek kullanımı
  • START, STIME: Prosesin başlama zamanı
  • S : Prosesin durumunu gösterir.
    • Z:zombie,
    • S/D: interruptible / uninterruptible sleeping,
    • R: running
    • I: Idle kernel thread
    • X: dead
    • <: high priority
    • N: low priority
    • L: locked
    • +: foreground
    • s: session leader of  a process group
    • l: multi-threaded
  • VSZ/SZ /RSS/PSR: RSS proses tarafından tutulan bellek alanı, PSR atanan processor, VSZ sanal bellek alanı, SZ fiziksel page size demektir.
  • LWP / NLWP / SPID :  LWP ve SPID thread id’sini, NLWP ise proses içerisindeki toplam thread sayısını gösterir.
  • PRI : Proses önceliği. Düşük değerli daha önceliklidir. Prosesin kernel’daki gerçek önceliğidir.
  • NI: Nice değeri . 19 ile -20 arasındadır. User space’deki öncelik değeridir.
  • WCHAN: Sleep durumdaki prosesin kernel fonksiyonu. Diğer statülerde – veya * ifadelerini görebilirsiniz.

Filtreleme için sık kullanılan opsiyonlar:

  • -C komut : prosesler içinden komut kolonunda ilgili komutu seçer ve listeler.
  • -u kullanıcı : kullanıcıya ait prosesleri listeler.
  • -p pid : pid’li prosesi seçer ve listeler.
  • -t terminal : terminali seçer ve listeler

Çıktıyı kontrol etmek için sık kullanılan opsiyonlar:

  • -f : Tüm kolonları gösterir.
  • -F : Tüm kolonlar gösterir  ayrıca SZ, RSS, PSR alanlarını da dahil eder.
  • -o: Sadece belirtilen kolon adlarını belirtebiliriz. Örneğin:
    • ps -o pid
  • c : komut kolonundaki ifadenin full path’i yerine base’ini gösterir.
  • f : Prosesleri ağaç formatında olarak gösterilir.
  • -H: Prosesleri ağaç formatına benzer şekilde hiyerarşik olarak gösterir.

Thread’leri göstermek için sık kullanılan opsiyonlar:

  • -L : Threadleri görürüz.
  • -T : Threadleri görürüz. -L ‘den farklı olarak toplan thread sayusu gösterilmez.