Thuta Learning
Bash / Shell Scripting
AdvancedDevOps & Toolsbeginner

set -x နှင့် ShellCheck ဖြင့် Script များကို Debug လုပ်ခြင်း

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

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

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

script တစ်ခု အလုပ်မလုပ်တော့ဘဲ ဘာလို့ဆိုတာ မသိတဲ့အခါ set -x ကို ဖွင့်ရင် execution tracing စတင်တယ် — bash run မယ့် command တိုင်းကို stderr ဆီ print လုပ်ပေးတယ် (script ကိုယ်တိုင် stdout ဆီ ရေးထားတဲ့ ဘာမှ ပျက်စီးမသွားအောင်ပါ)၊ PS4 ရဲ့ တန်ဖိုး (default အနေနဲ့ +) နဲ့ ရှေ့ဆက်ပြီး၊ ပြီးတော့ အရေးကြီးတာက variable, glob, substitution တွေ expand ဖြစ်ပြီးသား တကယ့်တန်ဖိုးတွေနဲ့ print လုပ်ပေးတာပါ။ ဒီကွာခြားချက်က အရေးကြီးတယ်- cp file1.txt file2.txt ပြတဲ့ trace line တစ်ကြောင်းက source code ဘာပြောထားလဲဆိုတာ မဟုတ်ဘဲ တကယ် ဘာ run သွားလဲဆိုတာကို ပြောပြတယ်၊ variable တစ်ခုက မျှော်လင့်မထားတဲ့ တန်ဖိုးတစ်ခု — empty string, space တစ်ခု ပါနေတဲ့ value, ဒါမှမဟုတ် ထင်ထားတာထက် file များများ match ဖြစ်သွားတဲ့ glob — အဖြစ် expand ဖြစ်သွားတဲ့ bug တွေအတွက် အလွန်တန်ဖိုးရှိတယ်။ script တစ်ခုလုံးကို trace မလုပ်ဘဲ သံသယရှိတဲ့ အပိုင်းကိုပဲ set -x နဲ့ set +x ကြားထားပြီး ကျဉ်းကျဉ်းလေး trace လုပ်လို့ရတယ်၊ output ရေအလွန်များနေတာကို ရှောင်နိုင်တယ်။ PS4 ကို customize လုပ်ခြင်း — ဥပမာ PS4='+ [line ${LINENO}] ' — က trace line တိုင်းမှာ source line number ကို ထည့်ပေးလို့ အမည်မဲ့ + command ရှည်ရှည်ကြီး wall တစ်ခုကို file ထဲက line နဲ့ တိုက်ရိုက် ပြန်ကိုက်နိုင်တဲ့ အရာအဖြစ် ပြောင်းပေးတယ်၊ PS4 ကို ${FUNCNAME[0]} ဒါမှမဟုတ် ${BASH_SOURCE[0]} လို variable တွေနဲ့ file များများ ဒါမှမဟုတ် call stack နက်နက်ရှိတဲ့ script တွေအတွက် ထပ်ဆင့် ကိုးကားနိုင်သေးတယ်။ ShellCheck ကတော့ ဖြည့်စွက်တဲ့ static approach တစ်ခု သုံးတယ် — script run နေတာကို ကြည့်မယ့်အစား script text ကို parse လုပ်ပြီး line တစ်ကြောင်းမှ မ run ခင်ကတည်း ဖြစ်နိုင်ချေရှိတဲ့ bug တွေကို ဖော်ပြပေးတယ် — space ပါရင် ပျက်တတ်တဲ့ ဒါမှမဟုတ် မမျှော်လင့်ဘဲ glob ဖြစ်သွားတတ်တဲ့ unquoted variable, operator မှားနေတဲ့ string နှိုင်းယှဉ်မှု, == ဆိုလိုပေမယ့် = သုံးမိတာ, while read အစား command output ကို for loop ထဲ ဖတ်သွင်းတာ, နှင့် document ကောင်းကောင်းရှိတဲ့ shell footgun များစွာ — တစ်ခုစီမှာ အပြည့်အစုံ ရှင်းပြချက်ရှာနိုင်တဲ့ နံပါတ်တပ် SC code တစ်ခုစီ ပါဝင်တယ်။

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

ကိုယ့်စက်ပေါ်မှာတော့ ကောင်းကောင်း အလုပ်လုပ်ပေမယ့် CI ထဲမှာ တိတ်ဆိတ်စွာ မှားနေတဲ့ deploy script တစ်ခုကို ထိန်းသိမ်းနေတယ်ဆိုပါစို့၊ ဘာလို့မှားလဲဆိုတာ ညွှန်ပြမယ့် error message တစ်ခုမှ မရှိဘူး။ echo statement တွေ script ထဲမှာ ရေအလွန်များများ ထည့်ပြီး ခန့်မှန်းတိုင်း CI pipeline တစ်ခုလုံး ပြန် run နေမယ့်အစား CI ထဲမှာ bash -x deploy.sh run ကြည့်ရင် အဲဒီနေရာမှာ တကယ် execute ဖြစ်ခဲ့တဲ့ command တိုင်းကို တကယ့်တန်ဖိုးတွေ substitute လုပ်ပြီး trace အပြည့်အစုံ ထုတ်ပေးလိမ့်မယ် — if-statement ရဲ့ ဘယ် branch run သွားလဲ၊ variable တစ်ခုစီက အဲဒီ environment ရဲ့ အဲဒီအချိန်မှာ ဘာအဖြစ် expand ဖြစ်သွားလဲဆိုတာ တိတိကျကျ ပြပေးလိမ့်မယ်။ ဒါက diagnosis တစ်ခုလုံးပဲ ဖြစ်တတ်တယ် — CI က local shell နဲ့ မတူညီအောင် သတ်မှတ်ထားတဲ့ environment variable တစ်ခုက တကယ့်အပြစ်ရှင် ဖြစ်နေတာ ပေါ်လာတတ်တယ်၊ local မှာ အဲဒီ variable ဟာ ပြဿနာဖြစ်တဲ့ value ကို တစ်ခါမှ မရခဲ့လို့ source code ကို local မှာ ဘယ်လောက်ပဲ ကြည့်ကြည့် ဒါကို မတွေ့နိုင်ဘူး။ CI ရောက်မရောက် script တူညီတာကို ကြိုတင်ပြီး shellcheck deploy.sh run ကြည့်ရင် root cause ကို ပိုစောစော ဖော်ထုတ်နိုင်ခဲ့မှာပါ — ဥပမာ branch name တစ်ခုမှာ slash ဒါမှမဟုတ် space ပါလာတဲ့အချိန် တိတ်ဆိတ်စွာ ပျက်စီးမယ့် quote မလုပ်ထားတဲ့ $CI_BRANCH ကို flag ထုတ်ပေးမယ်၊ အဲဒီ branch name အမှန်တကယ် ဖန်တီးပြီး bug ကို trigger ဖြစ်စမနေခင်ကတည်းပါ။

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

bash
#!/bin/bash
set -euo pipefail

add() {
  local a=$1
  local b=$2
  echo $((a + b))
}

echo "before tracing"

PS4='+ [line ${LINENO}] '
set -x
result=$(add 2 3)
set +x

echo "after tracing, result was: $result"
You should see
အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်:

before tracing
++ [line 14] add 2 3
++ [line 5] local a=2
++ [line 6] local b=3
++ [line 7] echo 5
+ [line 14] result=5
+ [line 15] set +x
after tracing, result was: 5

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

nested function call ပါတဲ့ script တစ်ခုကို ယူပြီး call ကို set -x / set +x ကြားမှာ ထည့်ပြီး expand ဖြစ်ပြီးသား trace အပြည့်အစုံကို ကြည့်ပါ။ ပြီးရင် space ပါတဲ့ value တစ်ခုကို ကိုင်ထားတဲ့ variable တစ်ခုကို တမင် quote မလုပ်ဘဲ ထားကြည့်ပါ၊ ShellCheck က ဖော်ပြလား စစ်ကြည့်ပါ။

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

အပိုင်းတစ်ခုပဲ သံသယရှိပေမယ့် script တစ်ခုလုံးအတွက် set -x ကို ဖွင့်ချန်ထားခြင်း — trace output များစွာ ပေါ်လာပြီး တကယ့်ပြဿနာကို ဖုံးကွယ်သွားစေတယ်

ShellCheck ရဲ့ SC2086 (quote မလုပ်ထားတဲ့ variable) သတိပေးချက်တွေကို ကျော်ကျော်နေတာ ပုံမှန်ပဲလို့ ယူဆခြင်း — ဒါတွေက style ပြဿနာလေးတွေသက်သက်မဟုတ်ဘဲ word-splitting နဲ့ globbing bug အစစ်တွေကို အများကြီး ဖမ်းပေးနိုင်တယ်

ShellCheck — a shell script static analysis toolBash / Shell Scripting

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

  • အပိုင်းတစ်ခုပဲ သံသယရှိပေမယ့် script တစ်ခုလုံးအတွက် set -x ကို ဖွင့်ချန်ထားခြင်း — trace output များစွာ ပေါ်လာပြီး တကယ့်ပြဿနာကို ဖုံးကွယ်သွားစေတယ်
  • ShellCheck ရဲ့ SC2086 (quote မလုပ်ထားတဲ့ variable) သတိပေးချက်တွေကို ကျော်ကျော်နေတာ ပုံမှန်ပဲလို့ ယူဆခြင်း — ဒါတွေက style ပြဿနာလေးတွေသက်သက်မဟုတ်ဘဲ word-splitting နဲ့ globbing bug အစစ်တွေကို အများကြီး ဖမ်းပေးနိုင်တယ်
  • နမူနာ code ကို production system ပေါ် တိုက်ရိုက်မစမ်းဘဲ local/test environment တွင် အရင်အတည်ပြုပါ။

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

nested function call ပါတဲ့ script တစ်ခုကို ယူပြီး call ကို set -x / set +x ကြားမှာ ထည့်ပြီး expand ဖြစ်ပြီးသား trace အပြည့်အစုံကို ကြည့်ပါ။ ပြီးရင် space ပါတဲ့ value တစ်ခုကို ကိုင်ထားတဲ့ variable တစ်ခုကို တမင် quote မလုပ်ဘဲ ထားကြည့်ပါ၊ ShellCheck က ဖော်ပြလား စစ်ကြည့်ပါ။

You'll know it worked when: အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်: before tracing ++ [line 14] add 2 3 ++ [line 5] local a=2 ++ [line 6] local b=3 ++ [line 7] echo 5 + [line 14] result=5 + [line 15] set +x after tracing, result was: 5

set -x နှင့် ShellCheck ဖြင့် Script များကို Debug လုပ်ခြင်း | Thuta Learning