Çoğu zaman yazılımcılar bash script yazmak ile bir yazılım dilinde program yazmayı eş değer tutarlar. Ancak bu yanlış bir düşünce tarzıdır. Programlama dillerinde bulunan koruma katmanları script’lerde varsayılan olarak bulunmamaktadır. Bir örnek vermek gerekirseİ go dili varsayılan olarak atanan herhangi bir değişken kullanılmadığında derleme aşamasında hata verecektir ancak script’ler bununla ilgili bir hata üretmez ve çalışmaya devam ederler.
Bunu tanımlamak için bash bazı builtin kalıpları kullanabilir. Bunların arasında hataları önlemek için set -euxo pipefail
öne çıkmaktadır.
Set -e
-e
parametresi acil bir durumda script’in durmasını ve çıkış(exit) yapılmasını sağlar. Bu komut koşullu ifadelerdeki “false” çıktısının bir sorun olmadığını anlayacak kadar gelişmiştir. Eğer ekstra bir şeyler eklemek istenirse || true
ile farklı bir komut tetiklenebilir.
$ cat sete1.sh
#!/bin/bash
foo
echo "bar"
$ ./sete1.sh
./sete1.sh: line 3: foo: command not found
bar
$ cat sete1.sh
#!/bin/bash
set -e
foo
echo "bar"
$ ./sete2.sh
./sete2.sh: line 5: foo: command not found
set -e
komutunu eklediğimizde hatayı aldığımızda program çalışmayı bıraktı ve echo komutu işlemedi.
$ cat sete3.sh
#!/bin/bash
set -e
$(ls foo)
echo "bar"
$ ./sete3.sh
ls: foo: No such file or directory
$(ls foo)
komutu bir çıktı vermediği için sistem acil duruma düşüyor sonrasında çıkış yapıyor.
$ cat sete4.sh
#!/bin/bash
set -e
foo || true
$(ls foo) || true
echo "bar"
$ ./sete4.sh
./sete4.sh: line 5: foo: command not found
ls: foo: No such file or directory
bar
|| true
etkisi ile birlikte bir hata olsa bile geçmesini sağladık ve programdan çıkmadı.
$ cat sete5.sh
#!/bin/bash
set -e
if ls foobar; then
echo "foo"
else
echo "bar"
fi
$ ./sete5.sh
ls: foobar: No such file or directory
bar
Set -o pipefail
Script normalde bir satırdaki son komuta bakarak çıkış koduna karar verir. Ancak bu bazı durumlarda istenmeyen durumlara neden olabilir. Bir pipe ||
işareti bulunan satırda kontrolleri doğru yapabilmek için -o pipefail
kullanılması gerekmektedir.
$ cat set01.sh
#!/bin/bash
set -e
foo | echo "a"
echo "bar"
$ ./set01.sh
a
./set01.sh: line 5: foo: command not found
bar
Burada pipe
işaretinin solunda foo
komutu bir çıktı vermeyecek haliyle programı bozacak ancak sağ taraftaki echo “a”
çıktı verecek.. bundan ötürü komutların hepsi geçiyor.
$ cat set02.sh
#!/bin/bash
set -eo pipefail
foo | echo "a"
echo "bar"
$ ./set02.sh
a
./set02.sh: line 5: foo: command not found
-o pipefail
eklentisi ile sadece o satır çalışıyor ve sonrasında duruyor.
Set -u
Bir değişken oluşturup kullanmadığınız durumda hata üreterek çalışmasını engeller.
$ cat setu1.sh
#!/bin/bash
set-eo pipefail
echo $a
echo "bar"
$ ./setu1.sh
./setu1.sh: line 3: set-eo: command not found
bar
Hata verdi ancak diğer yandan script’i sonlandırmadı.
$ cat setu2.sh
#!/bin/bash
set -euo pipefail
echo $a
echo "bar"
$ ./setu2.sh
./setu2.sh: line 5: a: unbound variable
Burada değişkene tanım yapılmadığı ile ilgili hata alıyoruz.
Eğer ${a:-$b}
durumunu kullanarak bir tanım yapmak isterseniz bu durumda -u
parametresi durumu algılayıp tanımsız değişken için hata vermeyecektir.
$ cat setu3.sh
#!/bin/bash
set -euo pipefail
DEFAULT=5
RESULT=${VAR:-$DEFAULT}
echo "$RESULT"
$ ./setu3.sh
5
Koşullu soruların içerisindeki değişkenler eğer atanmadıysa onu algılayabilir.
$ cat setu4.sh
#!/bin/bash
set -euo pipefail
if [ -z "${MY_VAR:-}" ]; then
echo "MY_VAR was not set"
fi
$ ./setu4.sh
MY_VAR was not set
Set -x
Bash script içerisindeki satırların çalışma zamanında set parametrelerinden geçip geçmediğini kontrol etmemizi sağlar. Satırların uygunluğunu ve çıktıları ekrana yansıtır.
$ cat setx1.sh
#!/bin/bash
set -euxo pipefail
a=5
echo $a
echo "bar"
$ ./setx1.sh
+ a=5
+ echo 5
5
+ echo bar
bar
Set -E
Script içerisinde trap’ler ile çalışıldığı durumda çıktıyı ona göre şekillendirmeyi sağlar.
$ cat sete01.sh
#!/bin/bash
set -euo pipefail
trap "echo ERR trap fired!" ERR
myfunc()
{
# 'foo' is a non-existing command
foo
}
myfunc
echo "bar"
$ ./sete01.sh
./sete01.sh: line 9: foo: command not found
$ cat sete02.sh
#!/bin/bash
set -Eeuo pipefail
trap "echo ERR trap fired!" ERR
myfunc()
{
# 'foo' is a non-existing command
foo
}
myfunc
echo "bar"
$ ./sete02.sh
./sete02.sh: line 9: foo: command not found
ERR trap fired!
-E
ile birlikte hata durumunda kendi çıktımızın ekrana çıkmasını sağladık.
Çözüm
Bu blog yazısı set -euxo pipefail
veya set -Eeuxo pipefail
komutlarını neden kullandığımız hakkında iyi bir fikir vermiştir.