Shell Script #6 – Matematiksel İşlemler

Shell scripting serisine matematiksel işlemler ile devam ediyoruz. Konu, programlama dillerine aşina arkadaşlar için oldukça basit kalacaktır. Ancak yine de bash’e özel bazı noktalara değinmiş olacağız.

Kod örneklerini GitHub reposunda bulabilirsiniz.

Bash ortamında "başarısız" bir toplama işlemi
Bash ortamında “başarısız” bir toplama işlemi

let İfadesi

Aşağıdaki kodu ve çıktıyı incelediğinizde, değişkenlere atanan değerlerin matematiksel ifade olarak görülmediğini, aksine, string literal olarak işlendiğini göreceksiniz:

#! /bin/bash

#İki farklı değişken tanımlıyoruz:
sayi1=35
sayi2=42


#Bu değişkenleri toplamayı denersek, sonuç pek de tatmin edici olmuyor:
toplam=$sayi1+$sayi2
echo $toplam

Çıktı:

[root@localhost ShellScripting101]# ./7-matematiksel_islemler.sh
35+42

İstenen şey verilmiş gibi görünüyor. sayi1 (35)+sayi2 (42) ifadesi ekrana yazdırılmış. Ancak bizim derdimiz bu değildi.

Burada yardımımıza koşan ifade “let“. let’in ne olduğunu shell’imize sorduğumuzda gayet açıklayıcı bir yanıt alıyoruz:

[root@localhost ShellScripting101]# type let
let bir kabuk yerleşiğidir
[root@localhost ShellScripting101]# help let
let: let arg [arg ...]
    Evaluate arithmetic expressions.

    Evaluate each ARG as an arithmetic expression.  Evaluation is done in
    fixed-width integers with no check for overflow, though division by 0
    is trapped and flagged as an error.  The following list of operators is
    grouped into levels of equal-precedence operators.  The levels are listed
    in order of decreasing precedence.

        id++, id--      variable post-increment, post-decrement
        ++id, --id      variable pre-increment, pre-decrement
        -, +            unary minus, plus
        !, ~            logical and bitwise negation
        **              exponentiation
        *, /, %         multiplication, division, remainder
        +, -            addition, subtraction
        <<, >>          left and right bitwise shifts
        <=, >=, <, >    comparison
        ==, !=          equality, inequality
        &               bitwise AND
        ^               bitwise XOR
        |               bitwise OR
        &&              logical AND
        ||              logical OR
        expr ? expr : expr
                        conditional operator
        =, *=, /=, %=,
        +=, -=, <<=, >>=,
        &=, ^=, |=      assignment

    Shell variables are allowed as operands.  The name of the variable
    is replaced by its value (coerced to a fixed-width integer) within
    an expression.  The variable need not have its integer attribute
    turned on to be used in an expression.

    Operators are evaluated in order of precedence.  Sub-expressions in
    parentheses are evaluated first and may override the precedence
    rules above.

    Exit Status:
    If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.

Şimdi aşağıdaki örneği inceleyelim:

#! /bin/bash

#İki farklı değişken tanımlıyoruz:
sayi1=35
sayi2=42

#İfadelerin bir matematiksel ifade olarak görülmesini sağlamak için let kullanırız:

let toplam=$sayi1+$sayi2
echo $toplam
#Ekrana 77 yazacaktır.

Yukarıda görültüğü üzere, “toplam” değişkenine atanacak olan değerin bir matematiksel ifade olması gerektiğini göstermek için “let” kullandık. Değişkenlerimizi tanımlarken ise let’ten faydalanmadık. Ancak bu değişkenleri de matematiksel işlemler için kullanmak üzere oluşturmuştuk. Hem kodumuzun okunabilirliği açısından, hem de henüz bilmediğimiz ama ortalıkta gezmesi muhtemel bazı ihtimallerden korktuğumuz için; matematiksel ifadelerde kullanacağımız her şeyi “let” ile tanımlamakta fayda var.

Aşağıdaki kod örneğinde, bazı temel matematiksel işlemlerin örneğini görebilirsiniz:

#! /bin/bash
#Değişken tanımları:
let sayi1=35
let sayi2=42

#Toplama işlemi:
let toplama=$sayi1+$sayi2

#Çıkarma işlemi:
let cikarma=$sayi1-$sayi2

#Çarpma işlemi:
let carpma=$sayi1*$sayi2

#Bölme işlemi (Buraya dikkat. Bash tam bölme işlemi yapar, virgüllü sayılarla ilgilenmez):
let bolme=$sayi1/$sayi2

#Kalan bulma (modulus) işlemi:
let modulus=$sayi1%$sayi2

#Üs alma (Buraya dikkat. Bu işleç, bash'te çalışıyor olabilir ancak her shell'de çalışacağının garantisi yok):
let us=$sayi1**3


echo "$sayi1+$sayi2 işleminin sonucu: $toplama"
echo "$sayi1-$sayi2 işleminin sonucu: $cikarma"
echo "$sayi1*$sayi2 işleminin sonucu: $carpma"
echo "$sayi1/$sayi2 işleminin sonucu: $bolme"
echo "$sayi1%$sayi2 işleminin sonucu: $modulus"
echo "$sayi1^3 işleminin sonucu: $us"

Kodun çıktısını aşağıda görebilirsiniz:


[root@localhost ShellScripting101]# ./7-matematiksel_islemler.sh
35+42 işleminin sonucu: 77
35-42 işleminin sonucu: -7
35*42 işleminin sonucu: 1470
35/42 işleminin sonucu: 0
35%42 işleminin sonucu: 35
35^3 işleminin sonucu: 42875

Bölme işlemi biraz can sıkıcı oldu değil mi? Bash, ondalıklı sayılarla işlem yapamaz. Bu nedenle farklı bir araçtan yardım almamız gerekecek.

Shell Script’lerde Ondalıklı Sayılarla İşlem

Bash’in kendisi, ondalıklı sayılarla işlem yapamıyor. Dolayısıyla farklı araçlardan yardım almamız gerekiyor. Burada kullanacağınız araçlar, sisteminize ve sizin yaratıcılığınıza kalmış. Ben bu yazıda “bc” aracından faydalanacağım.

bc Programı

bc, bir hesap makinesi dili. Blitzcrank gelmesin hemen aklınıza 🙂 İfadelerinizi hassaslıkla hesaplayıp size sonuç dönebiliyor. Yüksek ihtimalle sisteminizde hâli hazırda kurulu bir araçtır:

[root@localhost ShellScripting101]# whatis bc
bc (1)               - An arbitrary precision calculator language
[root@localhost ShellScripting101]# which bc
/usr/bin/bc

bc‘yi kullanırken, matematik kütüphanelerini de beraberinde getirmek için “-l” parametresiyle kullanacağız. Kullanımı ise şu şekilde:

#! /bin/bash

#Değişken tanımları:
#Buraya dikkat. let kullanamam.
#Çünkü bash, let'i matematiksel ifadelerde kullanılıyor
#ve ondalıklı sayıları tanımadığı için hata veriyor:
sayi1=29.2
sayi2=13.75

#Toplama işlemi:
#Burada, daha önceki örneklerde de karşımıza çıkan "command substitution" kullanılıyor.
#$() içerisinde yapılan işlemin sonucu, dışarıdaki değişkene atılıyor.
#$sayi1+$sayi2 ifadesi echo'lanıyor ve pipe üzerinden bc programına gönderiliyor.
#Dikkat edin, yine let yok. Çünkü bash'e göre, ortada matematiksel bir ifade yok:
toplama=$(echo "$sayi1+$sayi2" | bc -l)

#Çıkarma işlemi:
cikarma=$(echo "$sayi1-$sayi2" | bc -l)

#Çarpma işlemi:
carpma=$(echo "$sayi1*$sayi2" | bc -l)

#Bölme işlemi:
bolme=$(echo "$sayi1/$sayi2" | bc -l)

#Kalan bulma (modulus) işlemi:
modulus=$(echo "$sayi1%$sayi2" | bc -l)

#Üs alma (Buraya dikkat. İşlemi bc programına yaptıracağımız için, üs alma operatörü olarak ^ kullanıyoruz:
us=$(echo "$sayi1^3" | bc -l)


echo "$sayi1+$sayi2 işleminin sonucu: $toplama"
echo "$sayi1-$sayi2 işleminin sonucu: $cikarma"
echo "$sayi1*$sayi2 işleminin sonucu: $carpma"
echo "$sayi1/$sayi2 işleminin sonucu: $bolme"
echo "$sayi1%$sayi2 işleminin sonucu: $modulus"
echo "$sayi1^3 işleminin sonucu: $us"

Şöyle yakışıklı hâlini bırakmakta da fayda görüyorum:

Bash ortamında bc yardımıyla ondalıklı sayılarda işlemler
Bash ortamında bc yardımıyla ondalıklı sayılarda işlemler

Kodun çıktısı ise şu şekilde:

[root@localhost ShellScripting101]# ./8-bc_ornekleri.sh
29.2+13.75 işleminin sonucu: 42.95
29.2-13.75 işleminin sonucu: 15.45
29.2*13.75 işleminin sonucu: 401.500
29.2/13.75 işleminin sonucu: 2.12363636363636363636
29.2%13.75 işleminin sonucu: .0000000000000000000500
29.2^3 işleminin sonucu: 24897.088

Bölme işlemi yine bir tık sıkıntılı sanki, değil mi?

Konuyu çok detaylandırmayacağım ancak anahtar kelimeyi vereceğim: “scale“. bc programını kullanırken scale değerini değiştirerek virgülden sonra kaç basamak görüntüleneceğini belirtebilirsiniz:

#! /bin/bash

#scale örneği.
#Virgülden sonra kaç basamak görüntüleneceğini belirtebilirsiniz:

birinci_sayi=22
ikinci_sayi=7

bolum=$(echo "$birinci_sayi/$ikinci_sayi" | bc -l)
echo $bolum

bolum=$(echo "scale=2; $birinci_sayi/$ikinci_sayi" | bc -l)
echo $bolum

Çıktılar ise şu şekilde olacaktır:

3.14285714285714285714
3.14

expr Programı

expr programı, kendisine verilen ifadeleri yorumlayan bir araçtır. “Expression’ları evaluate eder.” diyeceğim, olmayacak.

expr’nin yorumlama kabiliyeti, matematiksel işleçler için de geçerli.

#! /bin/bash

#expr kullanımı:
#expr programı, çıktıyı ekrana verir.
#Ancak bu davranışı değiştirmek için command substitution kullanılabilir.
#Önemli bir nokta daha. expr programı, işleçler arasında boşluk karakteri görmek ister.
#Hatırlarsanız, bash'te ise tam tersine boşluk görmek istemiyorduk.

#Değişken tanımları:
sayi1=19
sayi2=28

#İki değeri topla ve sonucu yaz:
expr $sayi1+$sayi2
#Ekrana 19+28 yazar.
expr $sayi1 + $sayi2
#Ekrana 47 yazar.


#İfadeyi yorumla ve sonucu değişkene ata:
toplam=$(expr $sayi1 + $sayi2)
#toplam değişkeninin değeri 47 oldu.
#Ekrana toplam = 47 yazar.
echo toplam = $toplam


#Çarpma işlemi biraz sorunlu.
#Command substitution sırasında çarpma işlemi için * karakterini kullanamayız.
#Çünkü * karakteri, bash için bir özel karkater (wildcard olarak kullanıyoruz hatırlarsanız)

#Dolayısıyla bir escape yapmamız gerekecek:

expr $sayi1*$sayi2
#Ekrana 19*28 yazar.

expr $sayi1 * $sayi2
#Syntax hatası

expr $sayi1 \* $sayi2
#Ekrana 532 yazar


carpim=$(expr $sayi1 \* $sayi2)
echo "19 * 28 = $carpim"
#Ekrana 19 * 28 = 532 yazar

Kodun çıktısı ise şu şekilde olacaktır:

[root@localhost ShellScripting101]# ./9-expr_ornekleri.sh
19+28
47
toplam = 47
19*28
expr: sözdizimi hatası
532
19 * 28 = 532

Bir cevap yazın

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