4. Signallar va traps โ
๐ฏ Bu bobda nimani o'rganasiz:
- Unix signallari โ SIGINT, SIGTERM, SIGKILL, SIGHUP va boshqalar
traporqali signallarni qabul qilishEXITpseudo-signali โ eng muhim cleanup mexanizmi- Cleanup pattern โ temp fayllar, lock fayllar, ulanishlar
- Graceful shutdown โ Ctrl+C bosilsa ham toza yakunlanish
- Background jarayonlar โ
&,wait, signal propagationโฑ Vaqt: ~25 daqiqa ๐งช Mashqlar:
bashlings watch 09_traps(kelajak sprint)
4.1. Nima uchun bu kerak? โ
Quyidagi vaziyatlarni tasavvur qiling:
Skriptingiz 100MB vaqtinchalik faylga ma'lumot yozayotgan edi. Foydalanuvchi
Ctrl+Cbosdi. Fayl qaytib qoldi โ diskni iflos qildi.Database backup skripti ishlayotgan edi. Terminal yopildi. Yarim-bajarilgan backup va ochiq ulanish saqlanib qoldi.
Server-monitoring skripti
cron'da yurar edi. Tizim qayta yuklandi. PID fayli qoldi, keyingi run "allaqachon ishlamoqda" deb noto'g'ri xato berdi.
Bularning hammasi trap va signallar bilan oldini olinadi.
Asosiy g'oya
Hech qachon "muvaffaqiyatli tugadi" deb umid qilmang. Skriptingiz istalgan vaqtda to'xtatilishi mumkin โ Ctrl+C, terminal yopilishi, OOM killer, kill -9. Cleanup'ni kafolatlash kerak.
4.2. Unix signallari โ qisqacha modeli โ
Signal โ bir jarayondan boshqasiga (yoki kernel'dan) asinxron xabar. Foydalanuvchining Ctrl+C โ bu signal. kill buyrug'i โ signal yuborish. Tizim ishini boshqarish โ signalar orqali.
Signallar โ har biri raqam va nomga ega โ
kill -l
# 1) SIGHUP 2) SIGINT 3) SIGQUIT ...
# 9) SIGKILL 15) SIGTERM ...Kerak bo'ladigan signallar โ
| Signal | Raqam | Mazmuni | Trap qilsa bo'ladimi? |
|---|---|---|---|
SIGINT | 2 | Ctrl+C โ odob bilan to'xtatish so'rovi | โ |
SIGTERM | 15 | "Iltimos to'xtang" (default kill PID) | โ |
SIGHUP | 1 | Terminal yopildi (hangup) | โ |
SIGQUIT | 3 | Ctrl+\ โ core dump bilan to'xtatish | โ |
SIGUSR1 | 10/30 | Foydalanuvchi belgilangan #1 | โ |
SIGUSR2 | 12/31 | Foydalanuvchi belgilangan #2 | โ |
SIGCHLD | 17/20 | Child process holati o'zgardi | โ |
SIGPIPE | 13 | Yozilayotgan pipe yopildi | โ |
SIGALRM | 14 | Timer ishladi | โ |
SIGKILL | 9 | Darhol o'ldirish โ qarshi tura olmaysiz | โ YO'Q |
SIGSTOP | 19/17 | Darhol pauza โ qarshi tura olmaysiz | โ YO'Q |
EXIT | (0) | Bash pseudo-signali โ har xil yakunda | โ (eng muhim) |
SIGKILL ni hech qachon ushlab bo'lmaydi
kill -9 PID โ jarayonni darhol o'ldiradi. Cleanup kodi ishlamaydi. Shuning uchun kritik holatlar uchun "tashqi qo'riqchi" jarayonlar kerak.
4.3. kill โ signal yuborish โ
# Default โ SIGTERM (15) yuboradi
kill 1234
# Aniq signal
kill -SIGINT 1234
kill -INT 1234 # SIG prefiksini tashlash mumkin
kill -2 1234 # raqam bilan
# Eng kuchlisi โ qarshi tura olmaydi
kill -9 1234
kill -KILL 1234
# Nom bo'yicha (process name)
killall my-script.sh
pkill -f "long pattern"
# Joriy jarayon PID si
echo $$ # bash uchunkill -0 โ faqat tekshirish โ
# Jarayon hayotmi?
if kill -0 1234 2>/dev/null; then
echo "Hali ishlayapti"
fi-0 signal yuborilmaydi, faqat ruxsat tekshiriladi.
4.4. trap asoslari โ
trap โ signal qabul qilinganda nima qilishni belgilaydi.
Sintaksis โ
trap '<bajariladigan kod>' SIGNAL [SIGNAL ...]Birinchi misol โ
#!/usr/bin/env bash
trap 'echo "โ Ctrl+C bosildi, lekin men tirikman!"' SIGINT
echo "5 soniya kutaman..."
for i in {1..5}; do
echo " $i"
sleep 1
done
echo "Yakunlandi"Ishga tushiring va Ctrl+C bosing โ skript ogohlantirib davom etadi.
Trap'larni ko'rish โ
trap -p # joriy o'rnatilgan trap'lar ro'yxati
trap -p SIGINT # faqat SIGINT uchunTrap'ni o'chirish (default'ga qaytarish) โ
trap - SIGINT # SIGINT default xulq-atvor (kill)
trap - EXIT INT TERM # bir nechta birgalikdaTrap'ni butunlay e'tibor bermaslik โ
trap '' SIGINT # Ctrl+C umuman ishlamaydiE'tiborsiz qoldirish xavfli
trap '' SIGINT โ foydalanuvchining Ctrl+C ni bekor qiladi. Bu shubhali UX. Faqat maxsus, hujjatlangan vaziyatlarda ishlating.
4.5. EXIT โ eng muhim pseudo-signal โ
EXIT โ haqiqiy Unix signali emas, bash ning maxsus pseudo-signali. U har qanday yo'l bilan skript tugaganda ishlaydi:
- Normal yakun (oxirgi buyruq bajarildi)
exitbuyrug'i chaqirildi- Xato bilan to'xtadi (
set -eostida) - Signal qabul qilindi (
SIGINT,SIGTERM)
Cleanup uchun zarba
Aynan shu xususiyat EXIT ni cleanup uchun mukammal joy qiladi. Bir marta yozasiz โ har holatda ishlaydi.
Klassik pattern โ
#!/usr/bin/env bash
set -euo pipefail
tmpfile=$(mktemp)
cleanup() {
rm -f "$tmpfile"
echo "๐งน Tozalandi"
}
trap cleanup EXIT
# Asosiy ish
echo "ma'lumot" > "$tmpfile"
# ... boshqa kod ...
# `cleanup` har holda chaqiriladiSinab ko'ring:
- Normal tugating โ cleanup chiqaradi
Ctrl+Cbosing โ cleanup chiqaradiexit 1qo'shing โ cleanup chiqaradi
4.6. Eng yaxshi cleanup pattern โ
Ko'pchilik skriptlar Ctrl+C ni alohida xabar bilan qarshilab, EXIT ni cleanup uchun ishlatadi. Bu eng toza model:
#!/usr/bin/env bash
set -euo pipefail
tmpfile=$(mktemp)
lockfile="/tmp/myapp.lock"
cleanup() {
local rc=$?
rm -f "$tmpfile"
rm -f "$lockfile"
if [[ $rc -ne 0 ]]; then
echo "โ Skript $rc kodi bilan tugadi"
fi
}
# 1) EXIT โ har holatda cleanup
trap cleanup EXIT
# 2) INT / TERM โ graceful exit (EXIT trap ham chaqiriladi)
trap 'echo "โ to`xtatish so`rovi qabul qilindi"; exit 130' INT TERM
# Asosiy ish
echo $$ > "$lockfile"
echo "ma'lumot" > "$tmpfile"
for i in {1..30}; do
echo "Qadam $i"
sleep 1
done
echo "โ
Yakunlandi"Nima ishlaydi? โ
| Hodisa | INT trap | EXIT trap | Cleanup chaqiriladi? |
|---|---|---|---|
| Normal yakun | โ | โ | Ha |
Ctrl+C | โ
โ exit 130 | โ | Ha |
kill PID | TERM โ exit 130 | โ | Ha |
kill -9 PID | โ ushlamaydi | โ | Yo'q โ |
Skript ichida exit 1 | โ | โ | Ha |
Exit kodlar konventsiyasi
130=128 + SIGINT(2)โ Ctrl+C natijasi143=128 + SIGTERM(15)โ kill natijasi
Bu Unix konvensiyasi. Programatik tekshirishda foydali.
4.7. Bir nechta cleanup va done flag โ
Agar cleanup ikki marta chaqirilishidan saqlanish kerak bo'lsa (masalan, Ctrl+C keyin exit 1 ham chaqirilsa):
__cleaned=0
cleanup() {
[[ $__cleaned -eq 1 ]] && return
__cleaned=1
rm -f "$tmpfile"
rm -f "$lockfile"
}
trap cleanup EXITBu โ idempotent cleanup patterni.
4.8. SIGKILL โ qarshi tura olmaslik โ
trap 'echo "haa"' SIGKILL # โ bash xato beradiBash hatto bunday yozishga ruxsat bermaydi. Sababi: SIGKILL va SIGSTOP โ kernel darajasidagi signallar, foydalanuvchi jarayoni hech qanday yo'l bilan to'sib qo'ya olmaydi.
Real qoidalar โ
kill PID(SIGTERM) โ odobli so'rov. Skript cleanup qilib chiqishi mumkin.kill -9 PID(SIGKILL) โ kuch ishlatish. Cleanup ishlamaydi.
Production xulq-atvor
- Birinchi yondashuv: odobli
SIGTERM - Bir nechta soniya kutish (cleanup uchun)
- Hech narsa bo'lmasa:
SIGKILL(oxirgi chora)
systemctl stop aniq shu modelda ishlaydi.
4.9. Background jarayonlar va wait โ
Asinxron ishlovchi skriptlarda โ & bilan ishga tushirilgan background jarayonlar โ alohida e'tibor kerak.
& va $! โ
sleep 30 &
echo "Background PID: $!" # eng oxirgi background PIDwait โ kutish โ
sleep 5 &
pid1=$!
sleep 3 &
pid2=$!
wait $pid1 $pid2
echo "Ikkalasi ham tugadi"Signal propagation โ
Default'da, agar parent skriptga Ctrl+C bosilsa โ child jarayonlar avtomatik o'lmaydi. Aniq yo'naltirish kerak:
#!/usr/bin/env bash
cleanup() {
echo "Child'larni o'ldiraman..."
jobs -p | xargs -r kill 2>/dev/null
}
trap cleanup EXIT INT TERM
# Bir nechta worker
worker.sh &
worker.sh &
worker.sh &
waitjobs -p โ barcha background PID'lar. xargs -r kill โ ularni o'ldirish.
wait -n (Bash 4.3+) โ
Birinchi tugagan child'ni kutish:
worker1 &
worker2 &
worker3 &
# Birortasi tugashini kutamiz
wait -n
echo "Bittasi tugadi"4.10. Graceful shutdown patterni โ
Uzun ishlovchi skriptlar uchun (server, monitor, daemon) โ to'xtash so'rovini ravon qabul qilish kerak.
#!/usr/bin/env bash
#
# monitor.sh โ har 5 soniyada disk holatini tekshirish
#
set -euo pipefail
running=1
shutdown() {
echo "Shutdown so'rovi qabul qilindi, joriy iteratsiyani yakunlayman..."
running=0
}
trap shutdown INT TERM
while [[ $running -eq 1 ]]; do
df -h / | tail -1
sleep 5
done
echo "โ
Toza yakunlanish"Bu nima qiladi? โ
Ctrl+Cbosilsa โrunning=0qiladi- Joriy iteratsiya tugaydi (
dfchiqishi to'liq) - Keyingi
whiletekshiruvifalseqaytaradi - Skript toza yakunlanadi
exit chaqirilmaydi โ har ishni tugatib chiqamiz.
4.11. Debug uchun ERR va DEBUG traplari โ
ERR โ har xatoda โ
#!/usr/bin/env bash
set -e
trap 'echo "โ Xato $LINENO qatorida: $BASH_COMMAND"' ERR
echo "Ish boshlandi"
ls /yoq-katalog # bu yerda xato bo'ladi
echo "Bu satrgacha yetmaydi"Ishga tushganda:
Ish boshlandi
ls: cannot access '/yoq-katalog': No such file or directory
โ Xato 6 qatorida: ls /yoq-katalogDEBUG โ har buyruqdan oldin โ
trap 'echo ">> [LINE $LINENO] $BASH_COMMAND"' DEBUG
x=5
echo "salom"
lsNatija:
>> [LINE 3] x=5
>> [LINE 4] echo "salom"
salom
>> [LINE 5] ls
...set -x bilan farqi
set -x ham har buyruqni chiqaradi, lekin DEBUG trap siz xohlagan ko'rinishda formatlash imkonini beradi.
4.12. Real misol โ robust backup skript โ
Hamma kontseptsiyalarni birlashtirgan to'liq misol:
#!/usr/bin/env bash
#
# backup.sh โ Ctrl+C ham, kill ham, oddiy yakun ham โ barchasini toza boshqaradi
#
set -euo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
readonly LOCKFILE="/tmp/${SCRIPT_NAME%.sh}.lock"
TMPDIR=""
# --- Lock check (bitta nusxa pattern) ---
if [[ -f "$LOCKFILE" ]]; then
pid=$(cat "$LOCKFILE")
if kill -0 "$pid" 2>/dev/null; then
echo "โ Backup allaqachon ishlamoqda (PID: $pid)" >&2
exit 1
else
echo "โ Eski stale lock topildi, tozalanmoqda"
rm -f "$LOCKFILE"
fi
fi
echo $$ > "$LOCKFILE"
# --- Cleanup funksiyasi ---
__cleaned=0
cleanup() {
local rc=$?
[[ $__cleaned -eq 1 ]] && return
__cleaned=1
echo ""
echo "๐งน Tozalanmoqda..."
[[ -n "$TMPDIR" && -d "$TMPDIR" ]] && rm -rf "$TMPDIR"
rm -f "$LOCKFILE"
if [[ $rc -eq 0 ]]; then
echo "โ
Muvaffaqiyatli yakunlandi"
elif [[ $rc -eq 130 ]]; then
echo "โ Foydalanuvchi to'xtatdi (Ctrl+C)"
else
echo "โ Xato bilan tugadi (exit=$rc)"
fi
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
# --- Asosiy ish ---
TMPDIR=$(mktemp -d)
echo "๐ Vaqtinchalik katalog: $TMPDIR"
echo "๐ฆ Backup boshlandi ($(date '+%T'))"
for i in {1..10}; do
echo " Qadam $i / 10"
sleep 1
done
archive="$HOME/backup_$(date +%Y%m%d_%H%M%S).tar.gz"
echo "๐พ Arxivga yozilmoqda: $archive"
# tar -czf "$archive" -C "$TMPDIR" . # haqiqiy buyruq
echo "๐ Hajmi: $(du -sh "$TMPDIR" 2>/dev/null | cut -f1)"Bu skript:
- Lock fayl orqali bitta nusxa kafolatlaydi (
kill -0bilan stale tekshiradi) trap cleanup EXITโ har qanday yakunda tozalanadiINTโ exit 130 vaTERMโ exit 143 โ Unix konventsiya__cleanedflag โ ikki marta chaqirilishni oldini oladi- Vaqtinchalik katalog
mktemp -dโ xavfsiz va noyob - Color/emoji โ foydalanuvchiga aniq feedback
4.13. Tez-tez uchraydigan xatolar โ
Klassik tuzoqlar
SIGKILLni trap qilishga urinish. Imkonsiz.kill -9cleanup'ni chetlab o'tadi. Tashqi qo'riqchi (systemd,supervisord) ishlating.Cleanup'da yangi xato chiqarish.
set -eostida cleanup ichidagi xato keyingi qatorlarni tushiradi.cleanup() { rm -f "$tmpfile" || true; ... }.EXIT trap ko'rsatkichlari.
cleanupchaqirilganda$?โ eng oxirgi buyruq exit code. Trap birinchi qatoridalocal rc=$?qilib ushlang.Background process'lar trap'siz qoldirilgan. Parent o'lganda children orfan bo'lib qoladi.
trap 'kill $(jobs -p) 2>/dev/null' EXITbilan saqlang.Trap'da o'zgaruvchi ishlatish โ single vs double quote.
bashtrap "echo $tmpfile" EXIT # โ trap O'RNATILGAN paytda almashtiriladi trap 'echo $tmpfile' EXIT # โ trap CHAQIRILGAN paytdaBu nozik farq!
cleanupfunksiya orqali yozish xavfsizroq.exitni cleanup ichida unutish.bashcleanup() { rm -f "$tmp"; } # OK โ EXIT trap holatida trap 'cleanup; exit 130' INT # โ exit ham kerak, aks holda davom etadiLock fayl tozalanmagan stale holatda. Skript crash bo'lsa, lock qoladi. Har doim
kill -0 $pidbilan tekshiring va kerak bo'lsa olib tashlang.
4.14. Mashqlar โ
๐งช Kelajakda
bashlings watch 09_trapspaketida.
Countdown โ 10 dan 1 gacha har soniyada chiqaruvchi skript yozing.
Ctrl+Cbosilsa "uzr, davom eta olmadi" deb chiqarsin va exit 130 qaytarsin.tmp-safeโmktemporqali fayl yaratadi, ichiga 5 ta tasodifiy son yozadi, har holatda o'chirib tashlaydi. Cleanup EXIT trap orqali bo'lsin.Single instance โ lock fayl orqali ikkinchi nusxa ishga tushishini taqiqlovchi skript. Stale lock'ni avtomatik aniqlasin.
Parallel workers โ 3 ta background worker ishga tushiruvchi parent skript.
Ctrl+Cda hammasini o'ldirib chiqsin (jobs -p | xargs kill).ERR trace โ
set -evatrap '... $LINENO $BASH_COMMAND ...' ERRishlatib, xato sodir bo'lganda qator raqami va buyruqni chiqaruvchi skript yozing.
4.15. Xulosa โ
| Tushuncha | Asosiy nuqta |
|---|---|
| Signal nima | Jarayonlar o'rtasidagi asinxron xabar |
SIGINT (2) | Ctrl+C โ eng ko'p uchraydigan |
SIGTERM (15) | Default kill โ odob bilan to'xtatish so'rovi |
SIGKILL (9) | Trap qilib bo'lmaydi โ cleanup ham yo'q |
EXIT | Bash pseudo-signal โ eng muhim cleanup nuqtasi |
trap kod SIGNAL | Signalga reaksiya o'rnatish |
trap - SIGNAL | Default'ga qaytarish |
trap '' SIGNAL | E'tibor bermay qo'yish |
trap -p | Joriy trap'lar ro'yxati |
| Exit code 130 | Ctrl+C natijasi (128+SIGINT) |
| Exit code 143 | kill natijasi (128+SIGTERM) |
kill -0 PID | Jarayon tirikligini tekshirish |
$! | Eng oxirgi background PID |
5 ta asosiy g'oya โ
- Hech qachon "muvaffaqiyatli" deb umid qilmang. Cleanup'ni
trap EXITbilan kafolatlang. - EXIT pseudo-signal โ bash dunyosining eng kuchli xususiyatlaridan biri. Foydalaning.
SIGKILLushlab bo'lmaydi. Kritik holatlar uchun tashqi qo'riqchi kerak.- Single instance + lock + kill -0 โ production skriptlarning standart pattern.
- Graceful shutdown โ
while [[ $running -eq 1 ]]modeli bilan har iteratsiyani tugatib chiqing.
๐ Endi sizning skriptlaringiz har qanday vaziyatda toza yakunlanadi. Keyingi va oxirgi bobda biz set -euo pipefail, ShellCheck va getopts orqali production-grade skript yozishni o'rganamiz.
Keyingi sahifa: 5. Robust skriptlar โ