Mashqlar — zaxira nusxa skripti

~25 daq · Linux · Bash skriptlar

Muammodan boshlaylik

Siz kichik do'kon saytining yagona administratorisiz. Sizga kerak:

  1. Har kecha sayt fayllari va bazaning zaxira nusxasi, eski nusxalarni avtomatik tozalash bilan
  2. Har 5 daqiqada tekshiruv: sayt javob beryaptimi, disk to'lmayaptimi — muammo bo'lsa, Telegram ga xabar

Bu — 37–40-darslarning hammasi: o'zgaruvchilar, shartlar, tsikllar, funksiyalar, set -euo pipefail, trap va rejalashtirish. Har bir mashqni avval o'zingiz yozing.

Tayyorgarlik — sinov "sayti":

mkdir -p ~/mashq-skript/sayt/{uploads,config} && cd ~/mashq-skript
for i in $(seq 1 20); do head -c 50K /dev/urandom > "sayt/uploads/rasm-$i.jpg"; done
echo "DB_HOST=localhost" > sayt/config/.env
echo "<h1>Dokon</h1>" > sayt/index.html

1-mashq: Oddiy zaxira

zaxira.sh skriptini yozing:

  • sayt/ papkasini zaxiralar/sayt_YYYY-MM-DD_HHMM.tar.gz ga arxivlaydi
  • zaxiralar papkasi yo'q bo'lsa — yaratadi
  • Oxirida fayl nomi va hajmini chiqaradi
Yechim
#!/usr/bin/env bash
set -euo pipefail

MANBA="$HOME/mashq-skript/sayt"
MANZIL="$HOME/mashq-skript/zaxiralar"
FAYL="$MANZIL/sayt_$(date +%F_%H%M).tar.gz"

mkdir -p "$MANZIL"
tar -czf "$FAYL" -C "$(dirname "$MANBA")" "$(basename "$MANBA")"

echo "Saqlandi: $FAYL ($(du -h "$FAYL" | cut -f1))"
chmod +x zaxira.sh && ./zaxira.sh
tar -tzf zaxiralar/sayt_*.tar.gz | head

-C "$(dirname ...)" — arxivda absolyut yo'llar emas, sayt/... bo'lishi uchun (31-dars).


2-mashq: Argumentlar va tekshiruvlar

Skriptni yaxshilang:

  • 1-argument — manba papka (majburiy), 2-argument — saqlash kunlari (standart 7)
  • Argument berilmasa — foydalanish yo'riqnomasi
  • Manba papka mavjud bo'lmasa — xato
  • Kunlar soni raqam bo'lmasa — xato
  • Arxiv bo'sh yoki buzilgan bo'lsa — xato (tar -tzf bilan tekshirish)
Yechim
#!/usr/bin/env bash
set -euo pipefail

xato() { echo "XATO: $*" >&2; exit 1; }

[[ $# -lt 1 ]] && { echo "Foydalanish: $0 <papka> [kunlar=7]"; exit 1; }

MANBA="$(realpath "$1")"
KUNLAR="${2:-7}"
MANZIL="$HOME/mashq-skript/zaxiralar"

[[ -d "$MANBA" ]] || xato "Papka topilmadi: $MANBA"
[[ "$KUNLAR" =~ ^[0-9]+$ ]] || xato "Kunlar raqam bo'lishi kerak: $KUNLAR"

NOM="$(basename "$MANBA")"
FAYL="$MANZIL/${NOM}_$(date +%F_%H%M%S).tar.gz"

mkdir -p "$MANZIL"
tar -czf "$FAYL" -C "$(dirname "$MANBA")" "$NOM"

tar -tzf "$FAYL" > /dev/null || xato "Arxiv buzilgan: $FAYL"
[[ -s "$FAYL" ]] || xato "Arxiv bo'sh: $FAYL"

echo "Saqlandi: $FAYL ($(du -h "$FAYL" | cut -f1))"
./zaxira.sh                  # yo'riqnoma
./zaxira.sh yoq-papka        # XATO: Papka topilmadi
./zaxira.sh sayt abc         # XATO: Kunlar raqam bo'lishi kerak
./zaxira.sh sayt 14          # ✓

realpath — nisbiy yo'lni absolyutga aylantiradi.


3-mashq: Rotatsiya va log

Qo'shing:

  • KUNLAR dan eski zaxiralarni o'chirish (nechtasi o'chirilganini chiqarish)
  • Har bir xabar — vaqt bilan, ham ekranga, ham zaxira.log ga (log funksiyasi)
  • Xato bo'lganda — qaysi qatorda ekanini log ga yozish (trap ERR)

Sinash uchun eski fayllar yarating: touch -d "10 days ago" zaxiralar/sayt_eski{1,2,3}.tar.gz

Yechim
#!/usr/bin/env bash
set -euo pipefail

readonly MANZIL="$HOME/mashq-skript/zaxiralar"
readonly LOG="$HOME/mashq-skript/zaxira.log"

log()  { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; }
xato() { log "XATO: $*"; exit 1; }
trap 'log "XATO: $LINENO-qatorda: $BASH_COMMAND"' ERR

[[ $# -lt 1 ]] && { echo "Foydalanish: $0 <papka> [kunlar=7]"; exit 1; }
readonly MANBA="$(realpath "$1")"
readonly KUNLAR="${2:-7}"
[[ -d "$MANBA" ]] || xato "Papka topilmadi: $MANBA"
[[ "$KUNLAR" =~ ^[0-9]+$ ]] || xato "Kunlar raqam bo'lishi kerak"

main() {
  local nom fayl ochirildi
  nom="$(basename "$MANBA")"
  fayl="$MANZIL/${nom}_$(date +%F_%H%M%S).tar.gz"

  log "Boshlandi: $MANBA"
  mkdir -p "$MANZIL"
  tar -czf "$fayl" -C "$(dirname "$MANBA")" "$nom"
  tar -tzf "$fayl" > /dev/null || xato "Arxiv buzilgan"
  log "Saqlandi: $fayl ($(du -h "$fayl" | cut -f1))"

  ochirildi=$(find "$MANZIL" -name "${nom}_*.tar.gz" -mtime +"$KUNLAR" -print -delete | wc -l)
  log "Eski zaxiralar o'chirildi: $ochirildi ta (>$KUNLAR kun)"
  log "Jami zaxiralar: $(find "$MANZIL" -name "${nom}_*.tar.gz" | wc -l) ta, $(du -sh "$MANZIL" | cut -f1)"
}

main
touch -d "10 days ago" zaxiralar/sayt_eski{1,2,3}.tar.gz
./zaxira.sh sayt 7
cat zaxira.log
[2026-11-06 15:02:11] Boshlandi: /home/otabek/mashq-skript/sayt
[2026-11-06 15:02:11] Saqlandi: .../sayt_2026-11-06_150211.tar.gz (1.1M)
[2026-11-06 15:02:11] Eski zaxiralar o'chirildi: 3 ta (>7 kun)
[2026-11-06 15:02:11] Jami zaxiralar: 2 ta, 2.1M

4-mashq: Sog'liq tekshiruvi

tekshir.sh skriptini yozing. U tekshiradi:

  1. URL javob beradimi (curl -sf, 5 soniya timeout) — URL argument sifatida
  2. Disk ildizi 85% dan oshmaganmi
  3. Mavjud xotira (available) 200 MB dan kammi

Har bir muammo — ro'yxatga yig'iladi. Oxirida: muammo bo'lmasa — ✓ Hammasi joyida (chiqish kodi 0), bo'lsa — barcha muammolar ro'yxati (chiqish kodi 1).

Yechim
#!/usr/bin/env bash
set -euo pipefail

URL="${1:-http://localhost:3000/health}"
DISK_CHEGARA=85
XOTIRA_MIN_MB=200
MUAMMOLAR=()

# 1. Sayt
if ! curl -sf --max-time 5 "$URL" > /dev/null; then
  MUAMMOLAR+=("Sayt javob bermayapti: $URL")
fi

# 2. Disk
DISK=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if (( DISK >= DISK_CHEGARA )); then
  MUAMMOLAR+=("Disk ${DISK}% to'la (chegara ${DISK_CHEGARA}%)")
fi

# 3. Xotira
XOTIRA=$(free -m | awk '/^Mem:/ {print $7}')
if (( XOTIRA < XOTIRA_MIN_MB )); then
  MUAMMOLAR+=("Mavjud xotira kam: ${XOTIRA} MB")
fi

# Natija
if (( ${#MUAMMOLAR[@]} == 0 )); then
  echo "✓ Hammasi joyida (disk ${DISK}%, xotira ${XOTIRA} MB)"
  exit 0
fi

echo "⚠️  $(hostname): ${#MUAMMOLAR[@]} ta muammo"
for M in "${MUAMMOLAR[@]}"; do
  echo " - $M"
done
exit 1
chmod +x tekshir.sh
./tekshir.sh https://google.com
./tekshir.sh http://localhost:9999       # muammo bilan
echo $?                                  # 1
  • MUAMMOLAR=() — bo'sh massiv; +=("...") — qo'shish; ${#MUAMMOLAR[@]} — elementlar soni
  • if ! curl ... — ! buyruq natijasini inkor qiladi; set -e ham if ichida ishlamaydi

5-mashq: Telegram ga xabar

Muammo bo'lganda xabarni Telegram ga yuboring.

Tayyorlash: Telegram da @BotFather → /newbot → token oling. Botga biror xabar yozing, keyin chat ID ni bilib oling:

curl -s "https://api.telegram.org/bot<TOKEN>/getUpdates" | jq '.result[0].message.chat.id'

xabar_yubor funksiyasini yozing va tekshir.sh ga qo'shing. Token va chat ID — muhit o'zgaruvchilaridan (TG_TOKEN, TG_CHAT), skriptda yozilmasin.

Yechim

tekshir.sh boshiga:

: "${TG_TOKEN:?TG_TOKEN o'rnatilmagan}"
: "${TG_CHAT:?TG_CHAT o'rnatilmagan}"

xabar_yubor() {
  curl -sf --max-time 10 \
    "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
    -d chat_id="$TG_CHAT" \
    --data-urlencode text="$1" > /dev/null
}

Natija qismini o'zgartiring:

MATN="⚠️ $(hostname): ${#MUAMMOLAR[@]} ta muammo"
for M in "${MUAMMOLAR[@]}"; do
  MATN+=
#39;\n'"- $M" done echo "$MATN" xabar_yubor "$MATN" || echo "Telegram ga yuborib bo'lmadi" >&2 exit 1

Maxfiy ma'lumotlar — alohida faylda:

cat > ~/.tekshir.env << 'EOF'
TG_TOKEN=123456:ABC-...
TG_CHAT=987654321
EOF
chmod 600 ~/.tekshir.env

set -a; source ~/.tekshir.env; set +a
./tekshir.sh http://localhost:9999
  • --data-urlencode — maxsus belgilar va yangi qatorlarni to'g'ri yuborish
  • #39;\n'
    — haqiqiy yangi qator belgisi

6-mashq: Rejalashtirish

  1. Zaxira — har kecha 02:30 da (cron)
  2. Tekshiruv — har 5 daqiqada (cron), .tekshir.env ni yuklab
  3. Har ikkalasining chiqishi — log fayllarga
  4. Ishlayotganini tekshiring
Yechim
crontab -e
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin

30 2 * * *   $HOME/mashq-skript/zaxira.sh $HOME/mashq-skript/sayt 14 >> $HOME/mashq-skript/cron-zaxira.log 2>&1
*/5 * * * *  set -a; . $HOME/.tekshir.env; set +a; $HOME/mashq-skript/tekshir.sh https://google.com >> $HOME/mashq-skript/cron-tekshir.log 2>&1
crontab -l
# 5 daqiqa kuting...
tail ~/mashq-skript/cron-tekshir.log
grep CRON /var/log/syslog | tail -3

Production da tekshiruv uchun systemd timer (40-dars) yaxshiroq — EnvironmentFile= bilan .env yuklash ancha toza.

Tozalash: crontab -e da qatorlarni o'chiring yoki crontab -r (barcha vazifalar!), rm -rf ~/mashq-skript ~/.tekshir.env.


Nima uchun shunday?

Nima uchun tekshiruv skripti barcha muammolarni yig'ib, keyin bitta xabar yuboradi — har biri uchun alohida emas?

Chunki ogohlantirish tizimining eng katta dushmani — shovqin. Har 5 daqiqada uchta alohida xabar kelsa — bir kunda yuzlab; odam ularni o'qishni to'xtatadi va haqiqiy muammo e'tibordan chetda qoladi ("alert fatigue"). Bitta xabar, barcha muammolar bilan, server nomi bilan — darhol manzarani beradi. Keyingi qadam (o'zingiz qo'shib ko'ring): bir xil muammo uchun har 5 daqiqada emas, faqat holat o'zgarganda xabar yuborish — "muammo paydo bo'ldi" va "muammo hal bo'ldi". Haqiqiy monitoring tizimlari (Grafana, Uptime Kuma) aynan shunday ishlaydi.


Xulosa

Siz yozdingiz:

  • Arxivlash + tekshirish + rotatsiya bilan zaxira skripti
  • log, xato funksiyalari, trap ERR, readonly, main
  • Argumentlarni tekshirish: [[ -d ]], =~ ^[0-9]+$
  • Massivlar: MUAMMOLAR+=(), ${#MUAMMOLAR[@]}
  • curl bilan sayt tekshiruvi va Telegram API
  • Sirlar — chmod 600 fayl va ${VAR:?}
  • cron da: SHELL, PATH, absolyut yo'llar, log ga yo'naltirish

Keyingi bo'lim

9-bo'lim: Server — yangi VPS ni sozlash, Nginx va yakuniy loyiha.