နားလည်ထားရမယ့် အချက်
argument တွေကို if [ "$1" = "-v" ]; then ...; shift စတဲ့ check တွေနဲ့ လက်နဲ့ parse လုပ်တာ အရိုးရှင်းဆုံး case အတွက်တော့ အလုပ်လုပ်ပေမယ့် flag တွေက value ယူနိုင်တာ၊ order ပြောင်းရနိုင်တာ၊ ပေါင်းစပ်ရနိုင်တာ (-vo report.txt နဲ့ -v -o report.txt) များစတာနဲ့ မကြာခင် မယုံကြည်ရတော့ဘူး။ getopts ကတော့ ဒါကို မှန်ကန်စွာ လုပ်ပေးဖို့ bash ရဲ့ built-in loop တစ်ခုပါ- while getopts ":vo:h" opt; do case $opt in ... esac; done က positional parameter တွေကို flag တစ်ခုချင်းစီ ဖြတ်သွားပြီး letter တစ်ခုစီကို ဘယ်လို ကိုင်တွယ်ရမလဲဆိုတာ option string ကို သုံးပြီး သိတယ် — v နဲ့ h ကတော့ တစ်ကိုယ်တည်း flag၊ "vo:h" ထဲက o နောက်က colon ကတော့ -o ဟာ flag တစ်ခုတည်းအနေနဲ့ ရပ်နေမယ့်အစား နောက် argument ကို ၎င်းရဲ့ value အဖြစ် စားမယ်ဆိုတာ getopts ကို ပြောပြတယ်။ loop ထဲမှာ $opt ဟာ iteration တိုင်းက လက်ရှိ flag letter ကို ကိုင်ထားပြီး $OPTARG ကတော့ flag က value ယူတဲ့အခါ ၎င်းရဲ့ value ကို ကိုင်ထားတယ်၊ $OPTIND ကတော့ getopts က argument list ထဲ ဘယ်လောက်အထိ ရှေ့တိုးသွားပြီးလဲဆိုတာ internal အနေနဲ့ tracking လုပ်ပေးတဲ့ index တစ်ခုပါ။ flag အားလုံး consume ပြီးတဲ့အခါ shift $((OPTIND - 1)) က အားလုံးကို တစ်ကြိမ်တည်း ပယ်ချလိုက်ပြီး $1, $2 စတာတွေကို flag တွေ နောက်က တကယ့် positional argument (filename များအများစု) အဖြစ် ကျန်ခဲ့စေတယ်။ အစကတည်းက သိထားသင့်တာ နှစ်ခု ရှိတယ်- getopts က -v လို short, single-letter flag ကိုပဲ နားလည်တယ်၊ --verbose လို GNU-style long flag ကို နားမလည်ဘူး (အဲဒါတွေကို parse လုပ်ဖို့ getopt utility သို့မဟုတ် လက်နဲ့ ရေးထားတဲ့ logic လိုအပ်တယ်)၊ ပြီးတော့ shell session တစ်ခုတည်းထဲမှာ getopts loop ကို တစ်ကြိမ်ထက်ပို run ရင် OPTIND ကို auto-reset မလုပ်ပေးဘူး — ဒါကြောင့် parsing loop ကို ထပ်ခါထပ်ခါ run မယ့် script ဒါမှမဟုတ် test suite တစ်ခုက ပထမတစ်ကြိမ် run ပြီးရင် OPTIND=1 ပြန် reset ချရမယ်၊ မဟုတ်ရင် ဒုတိယ call က ပထမကြိမ် ရပ်ခဲ့တဲ့နေရာကနေ တိတ်ဆိတ်စွာ ဆက်သွားလိမ့်မယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
backup script တစ်ခုက ./backup.sh -v -o /mnt/backups file1.txt file2.txt ကို support လုပ်ဖို့ လိုတယ် — verbose flag တစ်ခု, value ယူတဲ့ output directory တစ်ခု, ပြီးတော့ တကယ် backup လုပ်မယ့် file အရေအတွက် မသေချာတာတစ်ခု။ if-statement chain နဲ့ လက်နဲ့ ရေးထားတဲ့ parsing ဟာ တစ်စုံတစ်ယောက်က flag order ပြောင်းပို့တာ, -v နဲ့ -o ကို ပေါင်းပို့တာ, ဒါမှမဟုတ် -o နောက်မှာ space မထားဘဲ output path ချပို့တာနဲ့ ပျက်စီးသွားလိမ့်မယ်၊ getopts ကတော့ standard convention အားလုံးကို extra code ဘာမှ ထည့်စရာမလိုဘဲ မှန်ကန်စွာ ကိုင်တွယ်ပေးတယ်။ ဥပမာကို လျှောက်ကြည့်ရင် — while getopts ":vo:h" opt က flag တစ်ခုစီအတွက် iteration တစ်ခု run တယ်၊ ပထမ pass မှာ opt ကို v လို့ သတ်မှတ်တယ် (ဒါနဲ့ case statement က verbose=1 လုပ်ပေးတယ်)၊ ဒုတိယ pass မှာ opt ကို o လို့ သတ်မှတ်ပြီး $OPTARG ကို report.txt အဖြစ် အလိုအလျောက် set လုပ်ပေးတယ်၊ ဒါကြောင့် output=$OPTARG က ၎င်းကို ဖမ်းယူတယ်။ getopts က flag နှစ်ခုနဲ့ ၎င်းရဲ့ value ကို consume ပြီးတဲ့အခါ OPTIND ဟာ အဲဒါတွေကို ကျော်ပြီး ညွှန်နေတယ်၊ ဒါကြောင့် shift $((OPTIND - 1)) က -v -o report.txt ကို argument list ထဲက လုံးဝ ဖယ်ရှားလိုက်ပြီး somefile.txt anotherfile.txt ကိုပဲ $1 နဲ့ $2 ထဲမှာ ကျန်ခဲ့စေတယ် — flag ဘယ်နှစ်ခု ရှေ့မှာ ဘယ် order နဲ့ ရှိရှိ script က တကယ် backup လုပ်ရမယ့် file တွေအတိအကျပါပဲ။
အတူတူ စမ်းရေးကြည့်မယ်
#!/bin/bash
set -euo pipefail
verbose=0
output="default.txt"
usage() {
echo "Usage: script.sh [-v] [-o file] [-h]"
}
# Simulate command-line arguments for this example
set -- -v -o report.txt somefile.txt anotherfile.txt
while getopts ":vo:h" opt; do
case $opt in
v)
verbose=1
;;
o)
output=$OPTARG
;;
h)
usage
exit 0
;;
\?)
echo "Unknown option: -$OPTARG" >&2
usage
exit 1
;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 1
;;
esac
done
shift $((OPTIND - 1))
echo "verbose=$verbose"
echo "output=$output"
echo "remaining positional args: $*"
အထက်ပါ script ကို run လိုက်ရင် ဒီအတိုင်း output ထွက်ပါမယ်:
verbose=1
output=report.txt
remaining positional args: somefile.txt anotherfile.txt၅ မိနစ် စမ်းကြည့်
ရှိပြီးသား flag တွေအပြင် -q (quiet) flag အသစ်တစ်ခုကို support လုပ်ဖို့ getopts loop ကို extend လုပ်ပါ၊ ပြီးရင် flag ဘာမှမပါဘဲ script ကို ခေါ်ရင် error print ပြီး exit ဖြစ်မယ့် case တစ်ခု ထပ်ထည့်ပါ။
သတိလေးတစ်ချက်
option string ရဲ့ ရှေ့က colon ကို မေ့ကျန်ခြင်း (":vo:h" နှင့် "vo:h" ကွာခြားချက်) — မထားရင် getopts ကိုယ်တိုင် invalid option အတွက် error message ပြသွားပြီး \? case ကနေတဆင့် script ကိုယ်တိုင် ကိုင်တွယ်ခွင့် မရတော့ဘူး
loop ပြီးရင် shift $((OPTIND - 1)) ကို မေ့ကျန်ခြင်း — ဒါဆိုရင် $1 က တကယ့် ပထမဆုံး positional argument မဟုတ်ဘဲ ပထမဆုံး flag ကိုပဲ ဆက်ညွှန်းနေမယ်
GNU Bash Manual — Bash Builtins (getopts) — Bash / Shell Scripting