Funksiyalar, argumentlar va xatolarni boshqarish — set -euo
Muammodan boshlaylik
37-darsdagi deploy skripti:
cd /opt/dokon
git pull
npm ci # ← internet uzildi, xato!
npm run build # ← node_modules yo'q — xato, lekin davom etadi
sudo systemctl restart dokon # ← buzilgan build bilan qayta ishga tushirildi!
Bash standart holatda xatolarga e'tibor bermaydi — keyingi qatorga o'tib ketadi. Natija: sayt ishlamay qoldi, skript esa "muvaffaqiyatli" tugadi.
Bu darsda — skriptni ishonchli qilish: xatoda to'xtash, tozalash, funksiyalar bilan tartib.
set -euo pipefail — xavfsiz rejim
Har bir jiddiy skriptning ikkinchi qatori:
#!/bin/bash
set -euo pipefail
| Bayroq | Nima qiladi |
|---|---|
-e |
Har qanday buyruq xato bersa (kod ≠ 0) — darhol to'xtash |
-u |
E'lon qilinmagan o'zgaruvchidan foydalanish — xato |
-o pipefail |
Pipe dagi har qanday buyruq xatosi — butun pipe xatosi |
-u nima uchun muhim
rm -rf "$PAPKA/"* # PAPKA da xato bor (PAPAK deb yozilgan)
-u siz: $PAPKA bo'sh → rm -rf /*. -u bilan: PAPKA: unbound variable — skript to'xtaydi.
pipefail nima uchun muhim
curl -s https://api.example.com/data | jq '.items'
curl muvaffaqiyatsiz bo'lsa ham, pipe ning chiqish kodi — oxirgi buyruqniki (jq). pipefail bilan — har qanday qismning xatosi hisobga olinadi.
Kutilgan xatolar
Ba'zan xato normal (masalan, grep hech narsa topmasa — kod 1):
set -e
grep "ERROR" app.log | wc -l # topmasa — skript to'xtaydi!
SONI=$(grep -c "ERROR" app.log || true) # || true — xatoni "yutish"
if grep -q "ERROR" app.log; then ... # if ichida -e ishlamaydi — xavfsiz
Xatoni ushlash: trap
#!/bin/bash
set -euo pipefail
trap 'echo "✗ Xato: $LINENO-qatorda, buyruq: $BASH_COMMAND" >&2' ERR
Xato bo'lganda qaysi qatorda va qaysi buyruqda — aniq ko'rsatadi.
Tozalash: EXIT
Vaqtinchalik fayllar, lock-fayllar — skript qanday tugashidan qat'i nazar o'chirilishi kerak:
VAQTINCHA=$(mktemp -d)
trap 'rm -rf "$VAQTINCHA"' EXIT
# ... VAQTINCHA da ishlash ...
# Skript muvaffaqiyatli tugasa ham, xato bilan to'xtasa ham, Ctrl+C bosilsa ham — papka o'chiriladi
mktemp -d — noyob nomli vaqtinchalik papka (/tmp/tmp.Xa8Kj3).
Bir vaqtda faqat bitta nusxa
Cron har 5 daqiqada skriptni ishga tushiradi, lekin u ba'zan 10 daqiqa ishlaydi — ikki nusxa bir-biriga xalaqit beradi:
exec 200>/var/lock/zaxira.lock
flock -n 200 || { echo "Boshqa nusxa ishlayapti"; exit 1; }
Funksiyalar
salomlash() {
echo "Salom, $1!"
}
salomlash "Otabek"
salomlash "Malika"
- Funksiya ichida
$1,$2,$@— funksiyaning argumentlari (skriptniki emas) - Avval e'lon qilinadi, keyin chaqiriladi
local — lokal o'zgaruvchilar
hisobla() {
local A="$1"
local B="$2"
local NATIJA=$(( A + B ))
echo "$NATIJA"
}
JAVOB=$(hisobla 5 7)
echo "Javob: $JAVOB" # 12
local siz — o'zgaruvchilar global bo'ladi va skriptning boshqa qismlarini buzishi mumkin. Funksiya ichida doim local.
Qiymat qaytarish
Bash funksiyasi ikki xil "qaytaradi":
- Natija —
echoorqali;$(funksiya)bilan olinadi - Holat —
return 0/return 1(chiqish kodi);if funksiya; thenbilan tekshiriladi
ishlayaptimi() {
curl -sf "localhost:$1/health" > /dev/null
}
if ishlayaptimi 3000; then
echo "API ishlayapti"
fi
Oxirgi buyruqning chiqish kodi — funksiyaning chiqish kodi.
Yaxshi skript shabloni
#!/usr/bin/env bash
#
# zaxira.sh — MySQL bazasining zaxira nusxasini olish
# Foydalanish: ./zaxira.sh <baza_nomi> [saqlash_kunlari]
#
set -euo pipefail
# --- Sozlamalar ---
readonly ZAXIRA_PAPKA="/var/backups/mysql"
readonly LOG="/var/log/zaxira.log"
# --- Yordamchi funksiyalar ---
log() {
echo "[$(date '+%F %T')] $*" | tee -a "$LOG"
}
xato() {
log "XATO: $*" >&2
exit 1
}
foydalanish() {
echo "Foydalanish: $0 <baza_nomi> [saqlash_kunlari]"
echo "Misol: $0 dokon 14"
exit 1
}
# --- Argumentlar ---
[[ $# -lt 1 ]] && foydalanish
readonly BAZA="$1"
readonly KUNLAR="${2:-7}"
[[ "$KUNLAR" =~ ^[0-9]+$ ]] || xato "Kunlar soni raqam bo'lishi kerak: $KUNLAR"
# --- Asosiy ish ---
main() {
log "Zaxira boshlandi: $BAZA"
mkdir -p "$ZAXIRA_PAPKA"
local fayl="$ZAXIRA_PAPKA/${BAZA}_$(date +%F_%H%M).sql.gz"
mysqldump --single-transaction "$BAZA" | gzip > "$fayl"
[[ -s "$fayl" ]] || xato "Zaxira fayli bo'sh: $fayl"
log "Saqlandi: $fayl ($(du -h "$fayl" | cut -f1))"
local ochirildi
ochirildi=$(find "$ZAXIRA_PAPKA" -name "${BAZA}_*.sql.gz" -mtime +"$KUNLAR" -print -delete | wc -l)
log "Eski nusxalar o'chirildi: $ochirildi ta"
}
main "$@"
Tuzilma:
- Shebang va izoh — nima qiladi, qanday ishlatiladi
set -euo pipefail- Sozlamalar —
readonly(o'zgarmas) - Yordamchi funksiyalar —
log,xato - Argumentlarni tekshirish
mainfunksiyasi va oxiridamain "$@"
$* — barcha argumentlar bitta satr sifatida (log xabarlari uchun qulay).
Debug
bash -x skript.sh # har bir buyruqni bajarishdan oldin ko'rsatish
+ BAZA=dokon
+ KUNLAR=7
+ mkdir -p /var/backups/mysql
...
Skript ichida faqat bir qismi uchun:
set -x
# ... muammoli qism ...
set +x
ShellCheck — avtomatik tekshiruv
sudo apt install shellcheck
shellcheck zaxira.sh
In zaxira.sh line 12:
rm -rf $PAPKA/*
^-----^ SC2086: Double quote to prevent globbing and word splitting.
Qo'shtirnoqsiz o'zgaruvchilar va boshqa ko'plab tipik xatolarni topadi. VS Code uchun ShellCheck kengaytmasi ham bor — yozish paytida ogohlantiradi. Har bir skriptni tekshiring.
Nima uchun shunday?
Nima uchun bash standart holatda xatolarda to'xtamaydi — bu xavfli emasmi?
Tarixiy sabab: bash avval interaktiv shell — terminalda bitta buyruq xato bersa, butun sessiya yopilib ketmasligi kerak. Skriptlar uchun ham shu xatti-harakat meros qoldi va orqaga moslik uchun o'zgartirilmadi. Shuning uchun set -euo pipefail — skriptlar uchun "zamonaviy standart": tajribali muhandislar uni deyarli har bir skriptga avtomatik yozadi. Bu — "tez va baland ovozda muvaffaqiyatsizlikka uchra" (fail fast) tamoyili: xatoni darhol ko'rish, uni yashirib, keyinroq sirli oqibatlarini tekshirishdan ko'ra ancha yaxshi.
Xulosa
Eslab qoling:
set -euo pipefail— har bir skriptda; kutilgan xatolar —|| trueyokiiftrap '...' ERR— xato qatorini ko'rsatish;trap '...' EXIT— tozalashmktemp -d— vaqtinchalik papka;flock— bitta nusxafunksiya() { ... }; argumentlar —$1; ichida doimlocal- Natija —
echo+$(...); holat —return 0/1+if - Shablon: izoh → set → readonly sozlamalar → log/xato → argumentlar →
main "$@" bash -x— debug; shellcheck — har bir skriptni tekshiring
Keyingi dars
40-dars: Rejalashtirish — cron va systemd timer