Tarkibga o'tish

Shartlar va sikllar

O'zgaruvchilar va argumentlar maqolasida [ -z "$1" ] testi bir marta ishlatilgan edi — argument bo'sh yoki berilmaganini tekshirish uchun. Bu maqola aynan shunday testlar ortidagi to'liq mantiqni, ularga asoslanib qaror qabul qiladigan if konstruksiyasini va amalni bir necha marta takrorlaydigan for/while sikllarini ochib beradi. Bu uchtasi — testlar, shartlar va sikllar — har qanday jiddiy Bash skriptining skeletini tashkil qiladi.

Nimalarni o'rganasiz

Bu maqola oxirida siz quyidagilarni qila olasiz:

  • [ ] va [[ ]] test sintaksisi bilan fayllar, satrlar va sonlarni tekshirish;
  • if, if/else, if/elif/else konstruksiyalarini tuzish;
  • case bilan ixchamroq ko'p variantli tanlov yozish;
  • ro'yxat elementlari bo'ylab for siklini va shart bajarilguncha ishlaydigan while siklini yozish;
  • testlar va sikllarni real tekshiruv skriptiga birlashtirish.

Testlar: shartli ifodalar

Test — qavslar ichiga olingan va true yoki false natija (exit status) qaytaradigan ifoda:

[ -e /etc/passwd ]

Agar /etc/passwd mavjud bo'lsa, bu test 0 exit kodi (muvaffaqiyat) bilan tugaydi, aks holda 1 bilan. Exit kodlarning umumiy mantiqi Exit kodlar va xatolarni qayta ishlash maqolasida chuqurroq ko'rib chiqiladi. Bu yerda muhimi shuki, testning o'zi aslida oddiy buyruq (test buyrug'ining qisqa shakli), shuning uchun uning natijasini $? orqali ham tekshirish mumkin.

Bash’da ikkita test sintaksisi bor: klassik [ ] (POSIX bilan mos, aynan test buyrug'ining o'zi) va kengaytirilgan [[ ]] (faqat Bash’ga xos). Ochuvchi va yopuvchi qavslar bilan ifoda orasida har doim bo'sh joy bo'lishi kerak — [-e /etc/passwd] xato beradi.

help test    # built-in help for the test command
man test     # full documentation

Fayl testlari

Flag Nimani tekshiradi
-e fayl mavjud (istalgan turdagi)
-f oddiy fayl (regular file) mavjud (symlink emas)
-d katalog mavjud
-r o'qish mumkin
-w yozish mumkin
-x bajarish mumkin
-s fayl mavjud va bo'sh emas
if [ -f /etc/nginx/nginx.conf ]; then
    echo "Configuration file found"
fi

Satr testlari

Ifoda Nimani tekshiradi
-z "$VAR" satr bo'sh
-n "$VAR" satr bo'sh emas
"$A" = "$B" ikki satr teng
"$A" != "$B" ikki satr teng emas

Son testlari

Ifoda Nimani tekshiradi
-eq teng
-ne teng emas
-lt kichik
-le kichik yoki teng
-gt katta
-ge katta yoki teng
FREE_PERCENT=12
if [ "$FREE_PERCENT" -lt 15 ]; then
    echo "Disk space is running low"
fi

Warning

Satrlar uchun =/!=, sonlar uchun esa -eq/-ne/-lt/-gt ishlating — ular bir-birining o'rnini bosa olmaydi. "10" -eq "9" sonlar sifatida, "10" = "9" esa satrlar sifatida taqqoslaydi. Ikkalasi turli natija berishi mumkin: masalan, "010" va "10" son sifatida teng, lekin satr sifatida teng emas.

[[ ]]: kengaytirilgan test

[[ ]] faqat Bash’da (va zsh kabi boshqa zamonaviy shell’larda) ishlaydi va bir nechta qulayliklar beradi:

if [[ -f /etc/nginx/nginx.conf && -r /etc/nginx/nginx.conf ]]; then
    echo "The file exists and is readable"
fi

if [[ "$HOSTNAME" == web-* ]]; then
    echo "This is a web server"
fi

Farqlari: [[ ]] ichida && va || ni to'g'ridan-to'g'ri ishlatish mumkin ([ ] ichida ular uchun -a, -o kerak bo'ladi, bular esa POSIX’da eskirgan hisoblanadi). O'zgaruvchilarni qo'shtirnoqsiz ishlatish ham xavfsizroq (bo'sh satr yoki bo'sh joy muammolari kamroq uchraydi). == esa pattern matching'ni qo'llab-quvvatlaydi. Yangi, Bash’ga xos skriptlarda [[ ]] tavsiya etiladi. Agar skript boshqa shell’da (sh, dash) ham ishlashi kerak bo'lsa, uning o'rniga POSIX bilan mos [ ] ishlatiladi.

if, if/else, if/elif/else

#!/bin/bash

MY_SHELL="bash"

if [ "$MY_SHELL" = "bash" ]; then
    echo "You're using the Bash shell"
fi

if shart bilan boshlanadi, then keyingi qatorga o'tadi, blok esa fi (if so'zi teskari yozilgani) bilan tugaydi.

Shart bajarilmagan holat uchun else:

if [ "$MY_SHELL" = "bash" ]; then
    echo "You're using the Bash shell"
else
    echo "You're not using Bash: $MY_SHELL"
fi

Bir nechta shartni ketma-ket tekshirish uchun elif ("else if" ning qisqartmasi):

if [ "$MY_SHELL" = "bash" ]; then
    echo "Bash"
elif [ "$MY_SHELL" = "zsh" ]; then
    echo "Zsh"
else
    echo "Unknown shell: $MY_SHELL"
fi

case: ko'p variantli tanlov

Agar bitta o'zgaruvchini bir nechta aniq qiymat bilan taqqoslash kerak bo'lsa, case elif zanjiridan ancha ixcham va o'qilishi oson:

#!/bin/bash

read -p "Choose a service (nginx/postgresql/redis): " service

case "$service" in
    nginx)
        systemctl status nginx
        ;;
    postgresql)
        systemctl status postgresql
        ;;
    redis)
        systemctl status redis
        ;;
    *)
        echo "Unknown service: $service"
        ;;
esac

Har bir variant — pattern (nginx)). Undan keyin bajariladigan buyruqlar keladi va variant ;; bilan tugaydi, bu keyingi variantga o'tishni bildiradi. *) — standart holat (default case): u hech qaysi variantga mos kelmagan har qanday qiymatni ushlaydi. Uni har doim yozish tavsiya etiladi, aks holda kutilmagan qiymat jimgina e’tiborsiz qoldiriladi.

for: ro'yxat bo'ylab aylanish

for sikli ro'yxatning har bir elementini navbatma-navbat o'zgaruvchiga beradi va blokni shuncha marta bajaradi:

#!/bin/bash

for color in red green blue; do
    echo "$color"
done
red
green
blue

Ro'yxat ko'pincha o'zgaruvchida saqlanadi:

colors="red green blue"

for color in $colors; do
    echo "$color"
done

Real amaliy misol — joriy katalogdagi har bir .jpg fayl nomi oldiga bugungi sanani prefiks sifatida qo'shish:

#!/bin/bash

today=$(date +%Y-%m-%d)

for file in *.jpg; do
    mv "$file" "${today}-${file}"
done
bear.jpg   ->  2026-07-26-bear.jpg
cat.jpg    ->  2026-07-26-cat.jpg

*.jpg — wildcard (pattern). Bash uni ishga tushirishdan oldin joriy katalogdagi mos fayllar ro'yxatiga kengaytiradi (glob expansion), for esa shu ro'yxat bo'ylab aylanadi.

Skriptning barcha argumentlarini "$@" orqali aylanib chiqish O'zgaruvchilar va argumentlar maqolasida ko'rsatilgan edi — bu ham aynan shu for konstruksiyasining qo'llanishi.

while: shart bajarilayotganda takrorlash

for ro'yxat tugaguncha ishlasa, while shart true bo'lib turgan ekan, blokni takrorlayveradi:

#!/bin/bash

count=1

while [ "$count" -le 5 ]; do
    echo "Counting: $count"
    count=$((count + 1))
done
Counting: 1
Counting: 2
Counting: 3
Counting: 4
Counting: 5

$((...)) — arithmetic expansion. U butun sonlar ustida qo'shish, ayirish va boshqa amallar uchun ishlatiladi.

while faylni qatorma-qator o'qish uchun ham juda qulay:

#!/bin/bash

while read -r line; do
    echo "Line: $line"
done < /etc/hosts

Bu yerda < /etc/hosts faylni while siklining standart kirishiga yo'naltiradi, read -r esa har chaqirilganda keyingi qatorni o'qiydi va sikl fayl tugaguncha davom etadi. -r flag'i teskari slash (\) maxsus belgi sifatida emas, oddiy belgi sifatida o'qilishini ta’minlaydi. U deyarli har safar odat sifatida qo'shiladi.

until — while ning teskarisi: blok shart false bo'lib turgan ekan, bajariladi. Amalda until kam ishlatiladi, chunki shartning inkorini (! [ condition ]) yozish aynan shu natijani beradi. Lekin ba’zi holatlarda, masalan "servis ishga tushguncha kutish"da, until mantiqni tabiiyroq ifodalaydi:

until systemctl is-active --quiet nginx; do
    echo "nginx hasn't started yet, waiting..."
    sleep 1
done
echo "nginx has started"

break va continue: siklni muddatidan oldin boshqarish

Sikl o'zining tabiiy oxirigacha ishlashi shart emas. Uning oqimini ikki kalit so'z o'zgartiradi va ular real skriptlarda doimo uchraydi:

  • break — sikldan darhol chiqish;
  • continue — joriy iteratsiyaning qolgan qismini o'tkazib yuborib, keyingisiga o'tish.
#!/bin/bash

# Find the first writable directory from a preference list and stop looking
for dir in /var/log/myapp /tmp/myapp /tmp; do
    if [ ! -d "$dir" ]; then
        continue                 # not there — try the next candidate
    fi
    if [ -w "$dir" ]; then
        echo "Using: $dir"
        break                    # found one — no need to check the rest
    fi
done

Ichma-ich sikllarda break N/continue N tashqaridagi N-sikl uchun ishlaydi (break 2 birdaniga ikki darajadan chiqadi). Bu, masalan, hostlar × portlar to'rini tekshirayotganda bitta topilma butun qidiruvni to'xtatishi kerak bo'lganda foydali.

Arifmetik sikllar: (( )) va C uslubidagi for

Yuqoridagi while [ "$count" -le 5 ] hisoblagichi ishlaydi, lekin Bash’da sonli sikllar uchun tabiiyroq o'qiladigan maxsus arifmetik sintaksis bor. (( )) ichida oddiy matematik operatorlar (<, >, ==, ++) to'g'ridan-to'g'ri ishlatiladi: -lt/-eq ham, o'zgaruvchi nomlari oldidagi $ ham kerak emas:

# C-style for — the idiomatic numeric counter
for (( i = 1; i <= 5; i++ )); do
    echo "Counting: $i"
done

# the same logic as a while with an arithmetic condition
count=1
while (( count <= 5 )); do
    echo "Counting: $count"
    (( count++ ))
done

(( )) sonlar uchun, [[ ]] esa fayllar/satrlar uchun. Intervyuda aynan shu farq so'raladi: [ "$a" -lt "$b" ] ham, (( a < b )) ham butun sonlarni taqqoslaydi, lekin arifmetika uchun (( )) tozaroq. U ham [[ ]] kabi Bash kengaytmasi (POSIX sh emas).

Warning

Ifoda 0 ga teng chiqsa, (( )) noldan farqli exit status qaytaradi. Shuning uchun count 0 dan boshlansa, (( count++ )) "muvaffaqiyatsiz" tugaydi. Odatda bu zararsiz, lekin set -e yoqilganda (qarang: Exit kodlar va xatolarni qayta ishlash) u skriptni kutilmaganda to'xtatib qo'yishi mumkin. Qat’iy rejim yoqilgan bo'lsa, (( count++ )) || true yoki oldindan oshiruvchi (pre-increment) (( ++count )) dan foydalaning.

Amaliy vaziyat: servis holatini tekshirish va natijaga qarab harakat qilish

Vazifa: berilgan servis ishlayotganini tekshiradigan va ishlamayotgan bo'lsa, uni qayta ishga tushirishga urinadigan skript.

#!/bin/bash

# Usage: ./check_service.sh <service_name>

service="$1"

if [ -z "$service" ]; then
    echo "Error: please provide a service name. Usage: $0 <service_name>"
    exit 1
fi

if systemctl is-active --quiet "$service"; then
    echo "$service is running"
else
    echo "$service is not running, restarting..."
    sudo systemctl restart "$service"

    if systemctl is-active --quiet "$service"; then
        echo "$service restarted successfully"
    else
        echo "$service failed to restart — check the log: journalctl -u $service"
        exit 1
    fi
fi
./check_service.sh nginx
nginx is running

Bu vaziyatda uchta narsa birlashadi: argumentni tekshirish (-z), holatga qarab tanlov (if/else) va natijani qayta tekshirish (ichki if). exit 1 qatorlari Exit kodlar va xatolarni qayta ishlash maqolasida to'liq tushuntiriladi.

Keng tarqalgan xatolar

Qavslar va ifoda orasida bo'sh joy yo'q

if [-f /etc/hosts]; then
bash: [-f: command not found

[ ning o'zi alohida buyruq, shuning uchun undan keyin ham, ] dan oldin ham bo'sh joy bo'lishi shart: [ -f /etc/hosts ].

then ni nuqtali vergulsiz bir qatorda yozish

if [ -f /etc/hosts ]
then

ikki qatorda yozilsa to'g'ri, lekin bitta qatorda ; kerak:

if [ -f /etc/hosts ]; then

Testda o'zgaruvchini qo'shtirnoqsiz ishlatish

if [ -z $1 ]; then

Agar $1 umuman berilmagan bo'lsa, bu [ -z ] ga aylanadi va yo xato beradi, yo kutilmagan natija qaytaradi. To'g'risi: [ -z "$1" ] — qo'shtirnoq bo'sh qiymatni ham bitta argument sifatida to'g'ri uzatadi.

for color in $colors da qo'shtirnoqni unutish (teskari holat)

"$@" dan farqli o'laroq, ro'yxatni so'zlarga ajratish kerak bo'lgan holatlarda (masalan, for color in $colors) o'zgaruvchi ataylab qo'shtirnoqsiz qoldiriladi. Aks holda butun ro'yxat bitta element sifatida o'qiladi. Bu qo'shtirnoq qachon kerak va qachon kerak emasligini bilish nega muhimligini ko'rsatadi: maqsad ro'yxatni bo'lishmi (qo'shtirnoqsiz) yoki bitta butun qiymatni saqlashmi (qo'shtirnoq bilan)?

case da standart holatni (*)) unutish

Agar standart holat yozilmasa, hech qaysi variantga mos kelmagan qiymat jimgina e’tiborsiz qoldiriladi. Bu sezilmay qoladigan xato xavfini oshiradi.

Mashqlar

  1. df chiqishidan bandlik foizini o'qiydigan (uni son sifatida ajratib olish uchun awk yoki parameter expansion’dan foydalaning) va u 80% dan oshsa ogohlantiradigan, aks holda "joy yetarli" deydigan check_disk.sh skriptini yozing.
  2. case yordamida $1 orqali start, stop, restart yoki status qabul qiladigan va har biri uchun mos xabar chiqaradigan deploy.sh skriptini yozing. Noma’lum argument uchun foydalanish (usage) xabarini ko'rsating.
  3. while sikli yordamida berilgan sondan ($1) 1 gacha teskari sanaydigan, har safar 1 soniya kutadigan (sleep 1) va oxirida "Done!" chiqaradigan count_down.sh skriptini yozing.
  4. /etc/passwd ni while read -r line; do ... done < /etc/passwd bilan qatorma-qator o'qing va faqat shell’i bash bo'lgan foydalanuvchilarning nomlarini chiqaring (case dagi pattern matching'dan yoki [[ "$line" == *bash ]] dan foydalaning).

Xulosa

Testlar ([ ], [[ ]]) fayllar, satrlar va sonlar haqida true/false javob beradi. if/elif/else va case shu javoblarga asoslanib qaror qabul qiladi. for va while esa amallarni ro'yxat bo'ylab yoki shart bajarilayotgan ekan takrorlaydi. Bu uchtasi birgalikda skriptga real qaror qabul qilish imkonini beradi. Keyingi maqola, Funksiyalar, bu mantiqni takrorlash o'rniga bir marta yozib, uni nomi bilan chaqirishni ko'rib chiqadi — bu kod takrorlanishini kamaytirish usuli.

Manbalar