Bash skriptləri adətən #! “shebang”-la (sharp+bang) başlayır. Faylın əvvəlindəki #! işarələri, linux sisteminə fayl execute olunduqda hansı interpreterlə işləyəcəyini təmin etmək üçün yararlı olur. Linux bunu magic byteslara görə başa düşür. Magic bytes haqqında daha ətraflı man magic
Bash scriptlərini aşağıdakı iki üsulla çalışdırmaq mümkündür:
bash faylın_adı.sh
./faylın_adı.sh
Həmçinin onu da nəzərinizə çatdırmaq istəyirəm ki, "faylın_adı.sh" faylına execute icazəsi vermədən onu ikinci üsulda olduğu kimi işlətmək mümkün olmayacaq. “faylın_adı.sh” faylına execute icazəsi vermək üçün aşağıdakı əmri daxil etmək kifayətdir:
chmod +x faylin_adi.sh
shell skript faylının içində # işarəsindən sonra yazılan hər şey komment sayılır.
Linuxda müxtəlif shell-lər mövcuddur. Onlara bash, zsh, ksh və s. misal gətirmək mümkündür. Onların hərəsi bir birindən xırda xüsusiyyətlərlə fərqlənirlər.
Skript faylının işə düşməsi halında, komment-lər (yəni # işarəsi ilə başlayan sətirlər) işə düşmür.
Kommentlərdən kod yazarkən həmin hissəni nəyə görə yazdığınızı digər şəxslər başa düşsün deyə, vəya, ümumi proqramın istifadə qaydalarını izah etmək üçün istifadə edə bilərsiniz.
# bu şərh sətridir
echo 'test' # sharp işarəsindən sonra yazılanlar icra edilməyəcək
Xüsusi char-ların icrasından qaçmaq üçün onun qarşısına \ backslash işaarəsi atmaq olar:
Dəyişənlər (variables):
Dəyişən nədir?
İstifadəçi tərəfindən daxil olunan dəyəri(hərf,rəqəm və s.), komputerin RAM yaddaşında boş olan bir əraziyə sizin təyin etdyiniz adla etikətləyir. Yəni, siz dəyişənə bir dəyər təyin etdyinizdə, məsələn:
menimYasim=13
Komputer 13 ədədinin yaddaşda neçə bit yer tutacağını təyin edir, bundan sonra komputerin RAM yaddaşının boş olan bir hissəsində həmin ölçü qədər yaddaş ərazisini rezervləyir. Rezervlədiyi yeri və həmin yerin adresini götürüb, sizin daxil etdiyiniz ədədi (yəni 13-ü) ikilik say sistemində komputerin RAM-ında rezervlədiyi yerdə yadda saxlayır. Bundan sonra siz həmin dəyişəni, məsələn "menimYasim" dəyişənini çağırdıqda, komputer artıq "menimYasim"-a aid olan ədədin RAM-dakı adresi və "menimYasim"-in ölçüsünü bildiyi üçün bildiyi üçün, həmin adresə müraciət edərək, sizin istədiyiniz əməliyyatı edir.
Dəyişənlərin adı hərf və ya _ işarəsi ilə başlaya bilər. Bunlardan sonra isə, hər hansı rəqəm gələ bilər.
Shell, bütün dəyişənləri default olaraq string tipində tanıyır.
Dəyişəni tanımlayarkən, = (bərabərdir) işarəsinin hər iki tərəfində də boşluq buraxmamağa diqqət etmək lazımdır. Bərabərliyin sol tərəfində yazdığımız ad tamamilə bizim istəyimizə bağlı olsa da, linux əmrləri ilə eyni olamasına diqqət yetirmək lazımdır. Kodun oxunması zamanı komputer ilkin olaraq bərabərliyin sağ tərəfini oxuyur, onu hesablayıb cavabını çıxardır bundan sonra isə həmin cavabı bərabərliyin sol tərəfindəki dəyişənə təyin edir.
Dəyişənin tanınmasına nümunə olaraq aşağıdakı misalı göstərmək olar:
menimAdim="Ali"
Beləliklə artıq "menimAdim" dəyişəninin dəyəri "Ali"-dir.
Bir də konstant dəyişənlər mövcuddur. Konstant(sabit) dəyişən, ilkin təyin olunan qiymətini heçvaxt dəyişmir. Ölçü vahidləri ilə bağlı iş görərkən konstant dəyişənlərdən istifadə etmək mümkündür.
Konstant dəyişənləri aşağıdakı formada təyin etmək mümkündür:
declare -r pi=3.14
Gəlin "eded1" və "eded2" dəyişənlərinə(variables) yeni dəyərlər(values) təyin edək:
eded1=6
eded2=2
Sadə toplama, çıxma, vurma və bölmə riyazi hesablamalarını aşağıdakı formada etmək mümkündür:
əmri ilə bitir. "exit 0" əmri skriptin müvəffəqiyyətlə və səhvsiz başa çatdığını göstərir.
sonu "exit 0" ilə bitən hər hansı bir skripti çalışdırdıqdan sonra, terminalda
exit 0
yazdıqda sizə cavab "0" verirsə, deməli həmin kod səhvsiz çalışıb bitib.
Əgər çalışdırıldıqda terminala alt-alta yazılmasını istədiyiniz bir neçə sətir varsa, hər bir sətri ayrıca "echo" komandası ilə ekrana çıxartmaq yerinə, kodun daxilində:
cat << END
hemin setirleri
bu cur
alt-alta
yaza bilersiniz
END
* İstifadəçidən inputun alınma yolları
Skripti işə saldıqda, arqumentlər istifadə oluna bilər. Arqument nədir? Skripti çalışdırarkən faylın adından sonra yazılan istənilən şey arqument sayılır. Məsələn:
menimSkriptim.sh test test2 test3
bu terminal əmrində test, test2 və test3 "menimSkriptim.sh" skriptinin arqumentləri sayılır.
Kodun daxilində, skriptin arqumentlərinə $1 $2 $3 və s. çağıraraq əlçatmaq mümkündür. Məsələn aşağıdakı kodu:
#!/bin/bash
echo $1 $2 $3
aşağıdakı əmr ilə çalışdırıldığını fərz edək:
./test.sh a bb ccc dddd
Bu zaman terminalda aşağıdakı stdout yazısını görəcəyik:
a bb ccc
əmr-dən də məlum olduğu kimi, bizim 4 arqumentimiz var idi: a, bb, ccc, dddd.
ancaq kodun daxilində onlardan üçünü çağırmışdıq: $1, $2, $3
Buna görə də, dddd arqumenti ekrana yazılmadı.
Skript kodun daxilində
$# - skript çalışdırılarkən daxil edilən arqumentlərin sayını ;
$@ - skript çalışdırılarkən daxil edilən arqumentlərin siyahısını özündə daşıyır.
Məsələn, aşağıdakı "skript.sh" adlanmış kod faylını nəzərdə tutaq:
İstifadəçidən daxil olunası yazını arqument yolundan başqa READ əmri vasitəsi ilə də almaq mümkündür:
#!/bin/bash
echo "Istifadeci adi ve sifresinizi daxil edin: "
read -t 3 -p 'Istifadeci adi: ' istifadeciAdi
read -t 3 -sp 'Sifre: ' istifadeciSifresi
# -t timeout ucundur, verilmis saniye erzinde daxiletme olmasa failure qaytarib novbeti setre kecir.
# -p yeni setre kecmeden istifadeciden daxiletme isteyir.
# -s istifadecinin daxiletmesini ekrana yazmamaq ucun istifade olunur. Sifreler ucun elverislidir.
#buradaki `echo ""` emri bir setir asagi dusmek ucun istifade olunub.
echo ""
echo "Ad soyad ata adi: "
# read vasitesi ile eyni vaxtda bir nece meluamati almaq mumkundur:
read istifadeciAdi istifadeciSoyadi istifadeciAtaAdi
echo "Sizin adiniz: $istifadeciAdi"
echo "Sizin soyadiniz: $istifadeciSoyadi"
echo "Sizin atanizin adi: $istifadeciAtaAdi"
exit 0
İstifadəçidən məlumatı almağın digər yolu isə stdin vasitəsi ilə başa gəlir. Linuxda pipe(|) komandası çox istifadə olunduğuna görə, fayllarla işləyərkən bu metod çox köməyə çatır.
adlar.list adında, içində aşağıdakı məlumatlar olan bir fayl olduğunu fərz edək:
Xüsusi bir kod vasitəsi ilə, bütün ad soyad ata adı məlumatlarını oxuyub, onlardan adları əlifba sırasına görə sıralaya bilərik:
#!/bin/bash
# ASA melumatindan Adlari siralayan kod
# Istifade qaydasi:
# Icinde ASA melumatlari olan fayl bu koda pipe olunmalidir.
# Burada cut əmri vasitəsi ilə delimiter(ayırıcı) olaraq boşluq(spacebar) təyin edərək, ad soyad və ata adını bir # # birindən ayırmışıq. -f field deməkdir. ad soyad və ata adı bizə 3 field verir:
# 1) soyad
# 2) ad
# 3) ata adi
# bizə bunların arasından adı sıralamaq lazım olduğuna görə, 2-ci field lazımdır. Adları sıralamaq üçün sort # # əmrindən istifadə etmək mümkündür.
cat /dev/stdin | cut -d" " -f 2 | sort
exit 0
Həmin kodu aşağıdakı şəkildə işlətmək lazımdır:
cat adlar.list | ./test.sh
Kod çalışdırıldıqda aşağıdakı nəticəni əldə edəcəyik:
filankes1filankes2filankes3filankes4filankes5
2 - şərt və döngü operatorları
if, fi, then, else komandaları, verilmiş şərt doğru olarsa, kodun çalışmasını təmin edən kombinasiyalardır. Şərtlər [] işarələri arasında verilməlidir. [ və ] işarələrindən həm əvvəl həm də sonra boşluq buraxılmalıdır.
If statement-ləri özündən sonra testlər qəbul edir:
hər hansı bir dəyişənin dəyərinin bərabərliyinin yoxlanması üçün iki fərqli yol seçmək olar:
[ "$test" = 5 ] dəyişəni cüt dırnaq içərisinə alıb 1 cüt kvadrat mötərizədən istifadə etmək. [[ $test == 10 ]] dəyişəni cüt dırnaqsız yazıb ümumi ifadəni cüt kvadrat mötərizədən istifadə edərək bağlamaqla.
Əgər ifdən sonra yazacağımız şeyi ifadə adlandırası olsaq,
! ifade Əgər ifadə False olarsa True qaytarır. ( $test ) Əgər ifade True olarsa True qaytarır.
ifade1 && ifade2 Hər iki ifadə True olarsa True qaytarır. ifade1 || ifade2 İki ifadədən biri True olduqda True qaytarır.
Fayl tip testləri:
Aşağıdakı testlər yalnız mövcud olan fayllar üçün True dəyəri qaytarırlar:
-d file True - əgər özündən sonra gələn fayl qovluq olarsa.
-e file True - əgər fayl mövcud olarsa
-L file True - əgər fayl symbolic link-dirsə
-O file True - əgər faylın sahibi hal-hazırki effektiv istifadəçi id-sidirsə.
-r file True - əgər fayl readable-dırsa.
-s file True - əgər faylın ölçüsü sıfırdan yuxarıdırsa.
-w file True - əgər fayl writable-dırsa.
-x file True - əgər fayl executable
Faylın modification date-i ilə bağlı şərt operatorları:
file1 -nt file2 True - əgər file1 file2-dən yeni olarsa
file1 -ot file2 True - əgər file1 file2-dən köhnə olarsa
String-lərlə bağlı şərt operatorları
-z String True - əgər stringin ölçüsü sıfır olarsa.
-n String True - əgər stringin ölçüsü sıfır olmazsa.
String1 = String2 True - əgər stringlər bərabərdirsə.
String1 != String2 True - əgər stringlər bərabər deyilsə.
Numeric tests
Numeric tests
ARG1 -eq ARG2 True - əgər ARG1 ARG2-yə bərabər olarsa.
[[ 5 -eq 05 ]] && echo "5 equals 05"
ARG1 -ne ARG2 True - əgər ARG1 ARG2-yə bərabər olmazsa.
[[ 6 -ne 20 ]] && echo "6 is not equal to 20"
ARG1 -lt ARG2 True - əgər ARG1 ARG2-dən kiçik olarsa.
[[ 8 -lt 9 ]] && echo "8 is less than 9"
[[ 3 < 4 ]] && echo "3 is less than 4"
ARG1 -le ARG2 True - əgər ARG1 ARG2-dən kiçik vəya bərabər olarsa.
[[ 3 -le 8 ]] && echo "3 is less than or equal to 8"
ARG1 -gt ARG2 True - əgər ARG1 ARG2-dən böyük olarsa.
[[ 5 -gt 10 ]] || echo "5 is not bigger than 10"
[[ 4 > 2 ]] && echo "4 is greater than 2"
ARG1 -ge ARG2 True - əgər ARG1 ARG2-yə bərabər və ya ondan böyük olarsa.
Bu nümunədə, -z ilə $1-in, yəni birinci arqumentin boş olub olmamasını yoxlayırıq, əgər boş olarsa, komputer then-in içinə girib oxumağa başlayır növbəti kodları. if ilə başlayan kod blokları fi ilə bitməlidir.
#!/bin/bash
if [ -z $1 ]
then
echo "Arqument daxil olunmayib."
fi
exit 0
Aşağıdakı kod isə, çalışdırıldığı zaman daxil edilən arqumentin olub olmamasını yoxlayır, əgər varsa, həmin arqumentin qovluq olub-olmamasını yoxlayır.
#!/bin/bash
if [ -z $1 ]
then
echo "Arqument daxil olunmayib."
elif [ -d $1 ]
then
echo "Daxil etdiyiniz $1 bir qovluqdur."
else
file $1
fi
exit 0
loop - döngü operatorları
sadə sintaks:
for VARIABLE in LIST; do
COMMAND
done
Nümunələr;
for HOST in host1 host2 host3; do echo $HOST; done
for HOST in host{1,2,3}; do echo $HOST; done
for HOST in host{1..3}; do echo $HOST; done
for FILE in file*; do ls $FILE; done
for FILE in file{a..c}; do ls $FILE; done
for EVEN in $(seq 2 2 10); do echo "$EVEN"; done
# loop for displayin installed RPM packages with "kernel" in their name
# displaying the installation date for each package.
for PACKAGE in $(rpm -qa | grep kernel); \
do echo "$PACKAGE was installed on \
$(date -d @$(rpm -q --qf "%{INSTALLTIME}\n" $PACKAGE))"; done
Writing regular expressions
The wordlist and script must be improved in future, since some regex tests is giving empty results.
I've prepared this script for educational purposes:
#!/bin/bash
# Define the wordlist as an array
wordlist=("cat" "concatenate" "dog" "category" "educated" "vindication" "coat" "convert" "cart" "covert" "cypher" "123" "abc123" "a b c" "A1B2C3" "!@#" "newline\n" "tab\t" "space " "CAPITAL" "lowercase" "Mixed123" "12345.67")
# Function to display the wordlist, regex information, and test the regex
test_regex() {
local regex=$1
local description=$2
clear
echo "Wordlist:"
printf '%s\n' "${wordlist[@]}"
echo
# Display the regex and description
echo "Regex Pattern: $regex"
echo "Description: $description"
read -p "Press Enter to see the matches..."
# Test the regex on each word
echo "Matches:"
for word in "${wordlist[@]}"; do
echo "$word" | grep -P "$regex" > /dev/null
if [ $? -eq 0 ]; then
echo " $word"
fi
done
echo
read -p "Press Enter to continue to the next regex..."
}
echo "Regular Expressions Demonstration"
# List of regex patterns and descriptions
declare -A regexes
regexes['^(cat)$']='Matches the word "cat" exactly.'
regexes['^cat']='Matches lines starting with "cat".'
regexes['dog$']='Matches lines ending with "dog".'
regexes['c.t']='Matches "c", any character, then "t".'
regexes['c[aou]t']='Matches "c", either "a", "o", or "u", then "t".'
regexes['c[aou]*t']='Matches "c", zero or more of "a", "o", or "u", then "t".'
regexes['c.{2}t']='Matches "c", exactly two characters, then "t".'
regexes['educat?ed']='Matches "educated", with or without the "t".'
regexes['con*cat*enate']='Matches "concatenate" with any number of "n" and "t".'
regexes['coa+t']='Matches "coat" with one or more "a".'
regexes['c.{2,}t']='Matches "c", at least two characters, then "t".'
regexes['c.{,2}t']='Matches "c", up to two characters, then "t".'
regexes['c.{1,3}t']='Matches "c", between 1 and 3 characters, then "t".'
regexes['.{5}']='Matches exactly 5 characters.'
regexes['.{2,}']='Matches at least 2 characters.'
regexes['.{,3}']='Matches up to 3 characters.'
regexes['.{3,5}']='Matches between 3 and 5 characters.'
regexes['[[:alnum:]]']='Matches alphanumeric characters.'
regexes['[[:alpha:]]']='Matches alphabetic characters.'
regexes['[[:blank:]]']='Matches blank characters: space and tab.'
regexes['[[:cntrl:]]']='Matches control characters.'
regexes['[[:digit:]]']='Matches digits.'
regexes['[[:graph:]]']='Matches graphical characters.'
regexes['[[:lower:]]']='Matches lowercase letters.'
regexes['[[:print:]]']='Matches printable characters.'
regexes['[[:punct:]]']='Matches punctuation characters.'
regexes['[[:space:]]']='Matches space characters.'
regexes['[[:upper:]]']='Matches uppercase letters.'
regexes['[[:xdigit:]]']='Matches hexadecimal digits.'
regexes['\\bword']='Matches "word" at the edge of a word.'
regexes['\\Bword']='Matches "word" not at the edge of a word.'
regexes['\\<word']='Matches "word" at the beginning of a word.'
regexes['\\>word']='Matches "word" at the end of a word.'
regexes['\\w']='Matches word constituents.'
regexes['\\W']='Matches non-word constituents.'
regexes['\\s']='Matches white space.'
regexes['\\S']='Matches non-whitespace.'
# Loop through each regex and test it
for regex in "${!regexes[@]}"; do
test_regex "$regex" "${regexes[$regex]}"
done
# End of Script
echo "Regex demonstration completed."
This script connects to two servers ('servera' and 'serverb') using SSH, and for each server, it retrieves and saves the fully qualified domain name, CPU information, SELinux configuration (excluding empty and comment lines), and logs of failed password attempts to separate output files in the '/home/student/output' directory.
#!/bin/bash
# Define user and output directory
USR='student'
OUT='/home/student/output'
# Loop through the servers
for SRV in servera serverb; do
# Filename for the current server's output
OUTPUT_FILE="${OUT}-${SRV}"
# Get the fully qualified domain name
ssh ${USR}@${SRV} "hostname -f" > "${OUTPUT_FILE}"
echo "#####" >> "${OUTPUT_FILE}"
# Get CPU information
ssh ${USR}@${SRV} "lscpu | grep '^CPU'" >> "${OUTPUT_FILE}"
echo "#####" >> "${OUTPUT_FILE}"
# Get SELinux configuration (excluding empty and comment lines)
ssh ${USR}@${SRV} "grep -v '^$' /etc/selinux/config | grep -v '^#'" >> "${OUTPUT_FILE}"
echo "#####" >> "${OUTPUT_FILE}"
# Get logs of failed password attempts
ssh ${USR}@${SRV} "sudo grep 'Failed password' /var/log/secure" >> "${OUTPUT_FILE}"
echo "#####" >> "${OUTPUT_FILE}"
done