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/elsekonstruksiyalarini tuzish;casebilan ixchamroq ko'p variantli tanlov yozish;- ro'yxat elementlari bo'ylab
forsiklini va shart bajarilguncha ishlaydiganwhilesiklini yozish; - testlar va sikllarni real tekshiruv skriptiga birlashtirish.
Testlar: shartli ifodalar
Test — qavslar ichiga olingan va true yoki false natija (exit status) qaytaradigan ifoda:
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.
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 |
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 |
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
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:
Ro'yxat ko'pincha o'zgaruvchida saqlanadi:
Real amaliy misol — joriy katalogdagi har bir .jpg fayl nomi oldiga bugungi sanani prefiks sifatida qo'shish:
*.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:
$((...)) — arithmetic expansion. U butun sonlar ustida qo'shish, ayirish va boshqa amallar uchun ishlatiladi.
while faylni qatorma-qator o'qish uchun ham juda qulay:
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
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
[ 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
ikki qatorda yozilsa to'g'ri, lekin bitta qatorda ; kerak:
Testda o'zgaruvchini qo'shtirnoqsiz ishlatish
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
dfchiqishidan bandlik foizini o'qiydigan (uni son sifatida ajratib olish uchunawkyoki parameter expansion’dan foydalaning) va u 80% dan oshsa ogohlantiradigan, aks holda "joy yetarli" deydigancheck_disk.shskriptini yozing.caseyordamida$1orqalistart,stop,restartyokistatusqabul qiladigan va har biri uchun mos xabar chiqaradigandeploy.shskriptini yozing. Noma’lum argument uchun foydalanish (usage) xabarini ko'rsating.whilesikli yordamida berilgan sondan ($1) 1 gacha teskari sanaydigan, har safar 1 soniya kutadigan (sleep 1) va oxirida "Done!" chiqaradigancount_down.shskriptini yozing./etc/passwdniwhile read -r line; do ... done < /etc/passwdbilan qatorma-qator o'qing va faqat shell’ibashbo'lgan foydalanuvchilarning nomlarini chiqaring (casedagi 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.