Thuta Learning
Bash / Shell Scripting
AdvancedDevOps & Toolsbeginner

Exit Code များနှင့် Strict Mode

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

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

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

run ပြီးသွားတဲ့ command တိုင်းက ကိန်းဂဏန်း exit status တစ်ခု (0 ကနေ 255 ကြား) ကို special variable ဖြစ်တဲ့ $? ထဲမှာ သိမ်းထားတယ် — ဒါက Unix ရဲ့ သက်တမ်းကြာနေတဲ့ convention တစ်ခုပါ- 0 ဆိုရင် အောင်မြင်တယ်၊ 0 မဟုတ်တဲ့ ဘယ်နံပါတ်ဆိုရင်မဆို တစ်ခုခု မှားနေတယ်လို့ ဆိုလိုပေမယ့် အဲဒီနံပါတ် တိတိကျကျ ဘာကို ဆိုလိုလဲဆိုတာကတော့ program တစ်ခုချင်းစီပေါ် မူတည်တယ် (1 က အထွေထွေ error, 127 က 'command not found', 130 က SIGINT နဲ့ kill ခံရတာ စသဖြင့်)။ $? ကို run ပြီးတဲ့ command နောက်တစ်ခုက ချက်ချင်း overwrite လုပ်လိုက်တဲ့အတွက် သိချင်ရင် echo တစ်ကြောင်းမှ မဖြတ်ခင် ချက်ချင်း capture လုပ်ဖို့ လိုတယ်။ စစ်ဆေးမထားရင် script မှာ ဒီလို failure ကို ဖမ်းဖို့ default protection ဘာမှ မရှိဘူး — command တစ်ခု fail သွားလည်း ဆက် run နေပြီး error state ကို တိတ်ဆိတ်စွာ ဆက်ပို့နေမယ်။ strict-mode trio က ဒီတိတ်ဆိတ်မှုကို ဆူညံအောင် ပြောင်းပေးတယ်။ set -e က command တစ်ခုခု nonzero နဲ့ ပြီးတာနဲ့ script ကို ချက်ချင်း ရပ်ပေးတယ် — ဒါပေမယ့် သိထားသင့်တဲ့ ချွင်းချက်တွေ ရှိတယ်- if/while/until ရဲ့ test အဖြစ် run နေတဲ့ command၊ ဒါမှမဟုတ် && , || ရဲ့ ဘယ်ဘက်က command အတွက်တော့ အလုပ်မလုပ်ဘူး၊ ဘာလို့ဆိုတော့ အဲဒီ construct တွေက failure ကို တမင် စစ်ဆေးနေတာဖြစ်လို့ မိမိကိုယ်တိုင် ကိုင်တွယ်မယ်လို့ မျှော်လင့်ထားလို့ပါ။ set -u က define မလုပ်ရသေးတဲ့ variable ကို ရည်ညွှန်းမိတိုင်း empty string အဖြစ် တိတ်ဆိတ်စွာ expand မလုပ်ဘဲ error တက်စေတယ် — ဒါက rm -rf "$TARGET_DIR/" "$OLD_DIR/" လိုမျိုး command တစ်ခုမှာ အလွန်အရေးကြီးတယ်၊ $OLD_DIR ကို spell မှားထား ဒါမှမဟုတ် unset ဖြစ်နေရင် ဒါက empty string အဖြစ် တိတ်ဆိတ်စွာ ပျောက်သွားပြီး command ကို ဘေးအန္တရာယ်ရှိတဲ့ ပုံစံအဖြစ် ပြောင်းသွားနိုင်လို့ပါ။ set -o pipefail ကတော့ pipeline တစ်ခုမှာ stage ဘယ်ခုမှမဆို fail ဖြစ်ရင် (နောက်ဆုံး command တစ်ခုတည်း မဟုတ်ဘဲ) pipeline တစ်ခုလုံးရဲ့ exit status ကို fail လို့ ပြောင်းသတ်မှတ်ပေးတယ် — မထားရင် false | true က default အားဖြင့် success လို့ ပြပါလိမ့်မယ်၊ bash က true ရဲ့ exit status ကိုသာ ကြည့်လို့ပါ။ script တစ်ခုရဲ့ အစမှာ set -euo pipefail ထားတာက bash မှာ 'မြန်မြန် ကျယ်ကျယ် fail ခြင်း' နဲ့ အနီးစပ်ဆုံးပဲ။

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

database migration run ပြီး app server ကို restart ပြန်လုပ်တဲ့ deployment script တစ်ခုကို စဉ်းစားကြည့်ပါ — တစ်ခုပြီးမှ တစ်ခု လုပ်ရမယ့် command နှစ်ခု၊ order မှားရင် ရလဒ်ဆိုးတွေ ရှိနိုင်တယ်။ strict mode မထားဘူးဆိုရင် migration command က fail သွားရင် (ဥပမာ SQL file ထဲမှာ syntax error) bash ရဲ့ default အပြုအမူက fail သွားတဲ့ command ရဲ့ error message ကို print လုပ်ပြီး နောက်လိုင်းဆက် run သွားတာပါပဲ — ဆိုလိုတာက server ကို restart ဆက်လုပ်သွားလိမ့်မယ်။ အခုတော့ ပိုဆိုးတဲ့ အခြေအနေရောက်သွားပြီ- migration တစ်ဝက်တစ်ပျက်ပဲ ပြီးသေးတဲ့ database ကို ညွှန်းနေတဲ့ service တစ်ခု run နေတယ်၊ data ပျက်စီးနေတာ ဒါမှမဟုတ် column မရှိလို့ crash ဖြစ်တာတွေ ဖြစ်နိုင်တယ်၊ deploy log ကို သေချာဖတ်မှသာ ဘာဖြစ်သွားလဲဆိုတာ သိနိုင်မယ်။ script ထိပ်မှာ set -euo pipefail ထည့်လိုက်ရင် ဒါလုံးဝ ပြောင်းသွားမယ်- migration command က nonzero exit status ပြန်လိုက်တာနဲ့ script က အဲဒီနေရာမှာ ချက်ချင်း ရပ်သွားပြီး restart command ကို ရောက်တောင် မရောက်တော့ဘူး။ failure ကလည်း ကျယ်ကျယ်လောင်လောင်ပေါ်တယ် (bash က fail နေတဲ့ လိုင်းနဲ့ command ကို report လုပ်ပေးတယ်)၊ ချက်ချင်းလည်း ဖြစ်တယ်၊ ဒါကြောင့် deploy ကို စောင့်ကြည့်နေသူ (လူဖြစ်ဖြစ် CI pipeline ဖြစ်ဖြစ်) က အောင်မြင်ပုံပေါ်ပေမယ့် system ကို တိတ်ဆိတ်စွာ ပျက်စီးအောင်ချန်ထားခဲ့တဲ့ deploy အစား ရှင်းရှင်းလင်းလင်း fail နေတဲ့ deploy တစ်ခုကိုသာ မြင်ရမယ်။ ဒါက strict-mode trio ရဲ့ trade-off အတိအကျပါပဲ- script က ပိုမကြာခဏ ရပ်ချင်ရပ်မယ်၊ အဲဒီအစား ပြန်လည်မကုစားနိုင်တော့တဲ့ state ကို တိတ်ဆိတ်စွာ ဆက်မသွားတော့ဘူး။

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

bash
#!/bin/bash
set -euo pipefail

echo "Running a command that succeeds..."
true
echo "Exit status of last command: $?"

echo "Running a command that fails, but caught with ||..."
false || echo "Caught with ||, exit status of false was: $?"

echo "--- set -e gotcha: a failing command inside an if condition does NOT stop the script ---"
if false; then
  echo "this branch does not run"
else
  echo "if caught the failure itself, script keeps going"
fi

echo "--- pipefail demo: without it, a failing command hidden earlier in a pipe is invisible ---"
if false | true; then
  echo "pipeline looked like success"
else
  echo "pipefail caught the failing 'false' even though 'true' ran last, exit status: $?"
fi

set +e
false
echo "set +e disabled strict mode, so we reached here. Exit status of false was: $?"
You should see
အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်:

Running a command that succeeds...
Exit status of last command: 0
Running a command that fails, but caught with ||...
Caught with ||, exit status of false was: 1
--- set -e gotcha: a failing command inside an if condition does NOT stop the script ---
if caught the failure itself, script keeps going
--- pipefail demo: without it, a failing command hidden earlier in a pipe is invisible ---
pipefail caught the failing 'false' even though 'true' ran last, exit status: 1
set +e disabled strict mode, so we reached here. Exit status of false was: 1

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

set -euo pipefail ပါတဲ့ script တစ်ခု ရေးပါ — file တစ်ခုကို backup location ကို copy လုပ်ပြီးရင် အထဲက စာလုံးတစ်လုံးကို grep နဲ့ ရှာပါ။ source filename ကို တမင် အမှားရေးထားပြီး script ကို run ကြည့်ပါ၊ backup file မရှိသေးတာနဲ့ grep ကို ဆက်မ run ဘဲ script က ချက်ချင်းရပ်တာကို သတိပြုပါ။

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

set -e က failure အားလုံးကို ဖမ်းမယ်လို့ ထင်ခြင်း — if/while condition အတွင်း ဒါမှမဟုတ် && , || ရဲ့ ဘယ်ဘက်မှာဆိုရင် အဲဒီ context တွေက failure ကို စစ်ဆေးဖို့ မျှော်လင့်ထားတဲ့အတွက် တိတ်ဆိတ်စွာ အလုပ်မလုပ်ဘူး

variable တွေကို default မပေးရသေးခင် set -u ကို သုံးမိတာ — ${1:-} လိုမျိုး ရွေးချယ်နိုင်တဲ့ variable တွေအတွက် script crash သွားတတ်တယ်၊ unset variable တွေကို ${VAR:-default} နဲ့ safe fallback ပေးထားသင့်တယ်

GNU Bash Manual — The Set BuiltinBash / Shell Scripting

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

  • set -e က failure အားလုံးကို ဖမ်းမယ်လို့ ထင်ခြင်း — if/while condition အတွင်း ဒါမှမဟုတ် && , || ရဲ့ ဘယ်ဘက်မှာဆိုရင် အဲဒီ context တွေက failure ကို စစ်ဆေးဖို့ မျှော်လင့်ထားတဲ့အတွက် တိတ်ဆိတ်စွာ အလုပ်မလုပ်ဘူး
  • variable တွေကို default မပေးရသေးခင် set -u ကို သုံးမိတာ — ${1:-} လိုမျိုး ရွေးချယ်နိုင်တဲ့ variable တွေအတွက် script crash သွားတတ်တယ်၊ unset variable တွေကို ${VAR:-default} နဲ့ safe fallback ပေးထားသင့်တယ်
  • နမူနာ code ကို production system ပေါ် တိုက်ရိုက်မစမ်းဘဲ local/test environment တွင် အရင်အတည်ပြုပါ။

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

set -euo pipefail ပါတဲ့ script တစ်ခု ရေးပါ — file တစ်ခုကို backup location ကို copy လုပ်ပြီးရင် အထဲက စာလုံးတစ်လုံးကို grep နဲ့ ရှာပါ။ source filename ကို တမင် အမှားရေးထားပြီး script ကို run ကြည့်ပါ၊ backup file မရှိသေးတာနဲ့ grep ကို ဆက်မ run ဘဲ script က ချက်ချင်းရပ်တာကို သတိပြုပါ။

You'll know it worked when: အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်: Running a command that succeeds... Exit status of last command: 0 Running a command that fails, but caught with ||... Caught with ||, exit status of false was: 1 --- set -e gotcha: a failing command inside an if condition does NOT stop the script --- if caught the failure itself, script keeps going --- pipefail demo: without it, a failing command hidden earlier in a pipe is invisible --- pipefail caught the failing 'false' even though 'true' ran last, exit status: 1 set +e disabled strict mode, so we reached here. Exit status of false was: 1

Exit Code များနှင့် Strict Mode | Thuta Learning