Thuta Learning
Bash / Shell Scripting
AdvancedDevOps & Toolsbeginner

trap ဖြင့် Signal ကိုင်တွယ်ခြင်း

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • trap ဖြင့် Signal ကိုင်တွယ်ခြင်း concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • နမူနာ code ကို ကိုယ်တိုင် run ပြီး output စစ်နိုင်ရန်
  • Tutorial Platform project နှင့် production scenario တွင် မှန်ကန်စွာအသုံးချနိုင်ရန်

နားလည်ထားရမယ့် အချက်

trap 'command' SIGNAL က signal တစ်ခု bash ဆီ ရောက်လာတိုင်း run မယ့် code အပိုင်းတစ်ခု — command တစ်ခုတည်း ဒါမှမဟုတ် ပိုအသုံးဝင်တာက function name တစ်ခု — register လုပ်ပေးပြီး ကိုယ်တိုင် ပြန်ပြောင်းမချင်း ဒါမှမဟုတ် shell ထွက်မချင်း register ဖြစ်နေမယ်။ အသုံးအများဆုံးက EXIT ပါ — real POSIX signal မဟုတ်တဲ့ pseudo-signal တစ်ခု၊ script ဘယ်အကြောင်းနဲ့ပဲ ပြီးသွားသွား (နောက်ဆုံး line ကနေ ပုံမှန် ကျသွားတာ၊ အစောကြီး exit ခေါ်တာ၊ ဒါမှမဟုတ် set -e အောက်က abort) bash ကိုယ်တိုင် fire ပေးတယ်။ EXIT handler အထဲမှာ $? ဟာ ၎င်းကို trigger လုပ်ခဲ့တဲ့ exit status ကို ဆက်ကိုင်ထားတယ်၊ ဒါက error ဖြစ်ပြီးနောက် cleanup ကို ပုံမှန် run ထက် အနည်းငယ် ကွဲပြားအောင် လုပ်ချင်ရင် အသုံးဝင်တယ်။ trap 'rm -f "$tmpfile"' EXIT က script ဘယ်လမ်းကြောင်းကနေပဲ ပြီးဆုံးဆုံး temp file ဖျက်ခြင်း ဖြစ်စေတယ် — script အောက်ခြေမှာ rm တစ်ကြောင်းတည်းထားပြီး code path (early exit တွေ, error တွေ အားလုံးအပါအဝင်) တိုင်း အဲဒီနေရာ တကယ် ရောက်မယ်လို့ မျှော်လင့်ထားတာထက် ပိုစိတ်ချရတယ်။ SIGINT (Ctrl+C နဲ့ ပို့တာ) နဲ့ SIGTERM (kill ကနေ default အနေနဲ့ ပို့တာ) လို signal အစစ်တွေကိုလည်း trap လုပ်နိုင်ပြီး script ကို ဘယ်နေရာမှာပဲ ရောက်နေရောက်နေ ရုတ်တရက် သေမသွားစေဘဲ — write တစ်ခု ပြီးအောင်, lock တစ်ခု release လုပ်တာအောင်, progress partial message တစ်ခု print ထုတ်တာအောင် — ချောချောမွေ့မွေ့ ထွက်ခွာနိုင်စေတယ်။ အစကတည်းက သိထားသင့်တာတစ်ခုကတော့ trap က handler တွေကို stack မလုပ်ဘူး — signal တစ်ခုတည်းအတွက် trap ကို ဒုတိယအကြိမ် ခေါ်ရင် ပထမ handler အနားက ဒုတိယတစ်ခု ထပ်ပေါင်းတာ မဟုတ်ဘဲ တိတ်ဆိတ်စွာ အစားထိုးလိုက်တာပါ၊ ဒါကြောင့် script တစ်ခုမှာ EXIT အတွက် cleanup action များစွာ လိုအပ်ရင် အားလုံးကို function တစ်ခုတည်းထဲမှာ ထားပြီး တစ်ကြိမ်တည်း register လုပ်သင့်တယ်၊ trap ခွဲပြီး ခေါ်ခြင်း မလုပ်သင့်ဘူး။

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

file ကြီးတစ်ခုကို temp path ကို download ဆွဲပြီး checksum စစ်၊ ပြီးမှ compress လုပ်ပြီး upload တင်တဲ့ backup script တစ်ခုကို စဉ်းစားကြည့်ပါ — step အများကြီး၊ တစ်ခုစီမှာ interrupt ဖြစ်နိုင်ချေ ရှိတယ်။ download လုပ်နေတုန်း user က Ctrl+C နှိပ်လိုက်ရင် ဒါမှမဟုတ် network ပြတ်ပြီး set -e အောက်မှာ script error ထွက်သွားရင် trap မလုပ်ထားတဲ့ script ဟာ ဘယ်နေရာရောက်နေရောက်နေ ရပ်သွားပြီး cleanup step ဘယ်တော့မှ မရောက်ဘဲ temp file တစ်ဝက်တစ်ပျက် disk ပေါ် ကျန်ခဲ့လိမ့်မယ်။ temp file ဖန်တီးပြီးတာနဲ့ trap cleanup EXIT ထည့်လိုက်ရင် — cleanup ဆိုတာ rm -f "$tmpfile" လုပ်ပေးမယ့် function တစ်ခု — ဒါပြောင်းသွားတယ်- ဒီ handler ဟာ script ရဲ့ ဖြစ်နိုင်ချေရှိတဲ့ အဆုံးသတ် (ပုံမှန် ပြီးဆုံးတာ, set -e ကနေ trigger ဖြစ်တဲ့ error, ဒါမှမဟုတ် သီးခြား SIGINT/SIGTERM trap က exit ခေါ်လိုက်တဲ့ interrupt) ဘယ်ဟာဖြစ်ဖြစ် တစ်ကြိမ်တည်း အလိုအလျောက် run သွားလိမ့်မယ်။ EXIT trap ဟာ ဘယ်လမ်းကြောင်းနဲ့ပဲ ရောက်ရောက် နောက်ဆုံးမှာ fire ဖြစ်တာကြောင့် cleanup logic ကို တစ်နေရာတည်းမှာ တစ်ကြိမ်တည်းပဲ ရေးရုံပဲ လိုတယ် — exit point တိုင်း, error branch တိုင်းရှေ့မှာ rm command ကို ထပ်ခါထပ်ခါ ရေးနေစရာမလိုဘူး၊ ဒါက ဒီနေ့ ထည့်ဖို့ လွယ်ပေမယ့် နောက်ထပ် edit သုံးလေးခု ကျန်တာနဲ့ မေ့ကျန်ဖို့ အလွယ်ဆုံး ပုံစံအတိအကျပါပဲ။

အတူတူ စမ်းရေးကြည့်မယ်

bash
#!/bin/bash
set -euo pipefail

tmpfile=$(mktemp)
echo "created temp file: exists=$([ -f "$tmpfile" ] && echo yes)"

cleanup() {
  rm -f "$tmpfile"
  echo "cleanup ran: tmpfile removed"
}
trap cleanup EXIT

on_interrupt() {
  echo "caught SIGINT, shutting down gracefully"
  exit 1
}
trap on_interrupt SIGINT SIGTERM

echo "doing some work..."
echo "some data" > "$tmpfile"
echo "wrote to tmpfile, contents: $(cat "$tmpfile")"

echo "script ending normally, EXIT trap will fire next"
You should see
အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်:

created temp file: exists=yes
doing some work...
wrote to tmpfile, contents: some data
script ending normally, EXIT trap will fire next
cleanup ran: tmpfile removed

၅ မိနစ် စမ်းကြည့်

temp file နှစ်ခု ဖန်တီးပြီး နှစ်ခုလုံးကို ဖယ်ရှားမယ့် EXIT trap တစ်ခုတည်း register လုပ်တဲ့ script တစ်ခု ရေးပါ။ script ကို ပုံမှန် run ကြည့်ပါ၊ ပြီးရင် ထပ် run ပြီး လယ်ကနေ Ctrl+C နှိပ်ကြည့်ပါ၊ ကြိမ်တိုင်းမှာ temp file ဘာမှ မကျန်တာကို စစ်ဆေးပါ။

သတိလေးတစ်ချက်

trap ... EXIT ကို ထပ်ခေါ်ရင် ပေါင်းစပ်သွားလိမ့်မယ်လို့ ထင်ခြင်း — call တိုင်းက အဲဒီ signal ရဲ့ ယခင် handler ကို အစားထိုးလိုက်တာဖြစ်လို့ နောက်ဆုံး register လုပ်ခဲ့တဲ့ trap ကပဲ run မယ်၊ cleanup logic အားလုံးကို handler တစ်ခုတည်းထဲ ပေါင်းထားသင့်တယ်

cleanup လုပ်မယ့် resource အမှန်တကယ် မရှိသေးခင် trap ကို set လုပ်ခြင်း (ဥပမာ mktemp မ run ရသေးခင် $tmpfile ကို trap လုပ်တာ) — ကြားထဲမှာ တစ်ခုခု fail သွားရင် trap က empty ဒါမှမဟုတ် မှားနေတဲ့ path နဲ့ fire ဖြစ်ပြီး ဘာမှ cleanup မလုပ်နိုင်တော့ဘူး

GNU Bash Manual — SignalsBash / Shell Scripting

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • trap ... EXIT ကို ထပ်ခေါ်ရင် ပေါင်းစပ်သွားလိမ့်မယ်လို့ ထင်ခြင်း — call တိုင်းက အဲဒီ signal ရဲ့ ယခင် handler ကို အစားထိုးလိုက်တာဖြစ်လို့ နောက်ဆုံး register လုပ်ခဲ့တဲ့ trap ကပဲ run မယ်၊ cleanup logic အားလုံးကို handler တစ်ခုတည်းထဲ ပေါင်းထားသင့်တယ်
  • cleanup လုပ်မယ့် resource အမှန်တကယ် မရှိသေးခင် trap ကို set လုပ်ခြင်း (ဥပမာ mktemp မ run ရသေးခင် $tmpfile ကို trap လုပ်တာ) — ကြားထဲမှာ တစ်ခုခု fail သွားရင် trap က empty ဒါမှမဟုတ် မှားနေတဲ့ path နဲ့ fire ဖြစ်ပြီး ဘာမှ cleanup မလုပ်နိုင်တော့ဘူး
  • နမူနာ code ကို production system ပေါ် တိုက်ရိုက်မစမ်းဘဲ local/test environment တွင် အရင်အတည်ပြုပါ။

လေ့ကျင့်ခန်း

temp file နှစ်ခု ဖန်တီးပြီး နှစ်ခုလုံးကို ဖယ်ရှားမယ့် EXIT trap တစ်ခုတည်း register လုပ်တဲ့ script တစ်ခု ရေးပါ။ script ကို ပုံမှန် run ကြည့်ပါ၊ ပြီးရင် ထပ် run ပြီး လယ်ကနေ Ctrl+C နှိပ်ကြည့်ပါ၊ ကြိမ်တိုင်းမှာ temp file ဘာမှ မကျန်တာကို စစ်ဆေးပါ။

You'll know it worked when: အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်: created temp file: exists=yes doing some work... wrote to tmpfile, contents: some data script ending normally, EXIT trap will fire next cleanup ran: tmpfile removed

trap ဖြင့် Signal ကိုင်တွယ်ခြင်း | Thuta Learning