Command-line ပတ်ဝန်းကျင်

ယခင်သင်ခန်းစာတွင် ဆွေးနွေးခဲ့သည့်အတိုင်း shell အများစုသည် အခြားပရိုဂရမ်များကို စတင်ပွင့်စေသည့် ရိုးရိုး launcher မျှသာမဟုတ်ဘဲ လက်တွေ့တွင် ဘုံသုံး pattern များနှင့် abstraction များ ပြည့်နှက်နေသော ပရိုဂရမ်မင်းဘာသာစကား တစ်ခုလုံးကို ပံ့ပိုးပေးထားပါသည်။ သို့သော် အခြားပရိုဂရမ်မင်းဘာသာစကား အများစုနှင့်မတူဘဲ shell scripting တွင် အရာအားလုံးကို ပရိုဂရမ်များ Run ရန်နှင့် ၎င်းတို့အချင်းချင်း ရိုးရှင်းထိရောက်စွာ ဆက်သွယ်ဆောင်ရွက်နိုင်စေရန်အတွက် ဒီဇိုင်းထုတ်ထားခြင်းဖြစ်ပါသည်။

အထူးသဖြင့် shell scripting သည် သဘောတူညီချက်များ (conventions) နှင့် အလွန်နီးကပ်စွာ ဆက်စပ်နေပါသည်။ Command line interface (CLI) ပရိုဂရမ်တစ်ခုသည် ပိုမိုကျယ်ပြန့်သော shell ပတ်ဝန်းကျင်အတွင်း အဆင်ပြေပြေ ပူးပေါင်းလုပ်ဆောင်နိုင်ရန်အတွက် လိုက်နာရန် လိုအပ်သည့် ဘုံ pattern အချို့ ရှိပါသည်။ ယခုအခါ command line ပရိုဂရမ်များ မည်သို့အလုပ်လုပ်သည်ကို နားလည်ရန် လိုအပ်သည့် သဘောတရားအများအပြားနှင့် ၎င်းတို့ကို အသုံးပြုပုံ၊ ချိန်ညှိပြင်ဆင်ပုံဆိုင်ရာ အသုံးများသော သဘောတူညီချက် (conventions) များကို လေ့လာသွားမည် ဖြစ်ပါသည်။

The Command Line Interface

ပရိုဂရမ်မင်းဘာသာစကား အများစုတွင် function တစ်ခု ရေးသားပုံသည် အောက်ပါအတိုင်း ဖြစ်လေ့ရှိသည် -

def add(x: int, y: int) -> int:
    return x + y

ဒီနေရာမှာ ပရိုဂရမ်ရဲ့ input တွေနဲ့ output တွေကို ရှင်းရှင်းလင်းလင်း မြင်တွေ့နိုင်ပါတယ်။ ၎င်းနှင့် ဆန့်ကျင်ဘက်အနေဖြင့် shell script များကို စတင်ကြည့်ရှုချိန်တွင် တော်တော်လေး ကွဲပြားခြားနားနေသည်ကို တွေ့ရပါလိမ့်မည်။

#!/usr/bin/env bash

if [[ -f $1 ]]; then
    echo "Target file already exists"
    exit 1
else
    if $DEBUG; then
        grep 'error' - | tee $1
    else
        grep 'error' - > $1
    fi
    exit 0
fi

ယခုကဲ့သို့ script မျိုးတွင် မည်သည့်အရာများ ဖြစ်ပျက်နေသည်ကို သေချာစွာ နားလည်နိုင်ရန်အတွက် shell ပရိုဂရမ်များ အချင်းချင်း သို့မဟုတ် shell ပတ်ဝန်းကျင်နှင့် ဆက်သွယ်ရာတွင် မကြာခဏ ပါဝင်လေ့ရှိသည့် သဘောတရား အနည်းငယ်ကို မိတ်ဆက်ပေးရန် လိုအပ်ပါသည် -

Arguments များ

Shell ပရိုဂရမ်များကို Run သည့်အခါ အဆိုပါ ပရိုဂရမ်များသည် argument စာရင်းတစ်ခုကို လက်ခံရရှိကြပါသည်။ Arguments တွေဟာ shell မှာ ရိုးရိုး string များဖြစ်ကြပြီး ၎င်းတို့ကို မည်သို့ အဓိပ္ပာယ်ဖော်မည်ဆိုသည်မှာ ပရိုဂရမ်အပေါ်၌သာ မူတည်ပါသည်။ ဥပမာအားဖြင့် ကျွန်ုပ်တို့သည် ls -l folder/ ဟု ရိုက်ထည့်လိုက်သောအခါ /bin/ls ပရိုဂရမ်ကို argument များဖြစ်သော ['-l', 'folder/'] တို့ဖြင့် Run ပေးခြင်းဖြစ်ပါသည်။

Shell script အတွင်းမှနေ၍ ၎င်းတို့ကို အထူး shell syntax ဖြင့် ရယူအသုံးပြုနိုင်ပါသည်။ ပထမဆုံး argument ကို ရယူရန် variable $1၊ ဒုတိယ argument အတွက် $2 စသဖြင့် $9 အထိ အသုံးပြုနိုင်ပါသည်။ Argument အားလုံးကို စာရင်း (list) အဖြစ် ရယူရန် $@ ကို သုံးပြီး argument အရေအတွက်ကို ရယူရန် $# ကို သုံးပါသည်။ ထို့ပြင် ပရိုဂရမ်၏ အမည်ကိုလည်း $0 ဖြင့် ရယူနိုင်ပါသည်။

ပရိုဂရမ် အများစုအတွက် argument များသည် flags များ နှင့် ရိုးရိုး string များ ရောနှောပါဝင်လေ့ရှိပါသည်။ Flag များကို အရှေ့တွင် dash တိုင်တို (-) သို့မဟုတ် double-dash (--) ပါဝင်ခြင်းဖြင့် ခွဲခြားသိရှိနိုင်ပါသည်။ Flag များသည် ပုံမှန်အားဖြင့် မထည့်လည်း ရပြီး ၎င်းတို့၏ အဓိကအခန်းကဏ္ဍမှာ ပရိုဂရမ်၏ လုပ်ဆောင်ပုံကို ပြောင်းလဲပေးရန် ဖြစ်ပါသည်။ ဥပမာအားဖြင့် ls -l သည် ls ၏ output ဖော်ပြပုံပုံစံကို ပြောင်းလဲပေးပါသည်။

သင့်အနေဖြင့် --all ကဲ့သို့ နာမည်ရှည်ပါသော double dash flag များနှင့် -a ကဲ့သို့ စာလုံးတစ်လုံးတည်း ပါလေ့ရှိသော single dash flag များကို တွေ့မြင်ရပါလိမ့်မည်။ ထို option တစ်ခုတည်းကိုပင် ပုံစံနှစ်မျိုးလုံးဖြင့် သတ်မှတ်နိုင်သည်၊ ဥပမာ ls -a နှင့် ls --all တို့သည် အတူတူပင် ဖြစ်ပါသည်။ Single dash flag များကို စုစည်း၍လည်း ရေးသားနိုင်သဖြင့် ls -l -a နှင့် ls -la တို့သည်လည်း အတူတူပင် ဖြစ်ပါသည်။ Flag များ၏ အစဉ်လိုက် သတ်မှတ်မှုသည်လည်း ပုံမှန်အားဖြင့် အရေးမကြီးပါ၊ ls -la နှင့် ls -al တို့သည် ရလဒ်တစ်ခုတည်းကိုပင် ထုတ်ပေးပါသည်။ Flag အချို့သည် အလွန်အသုံးများပြီး shell ပတ်ဝန်းကျင်နှင့် ပိုမိုရင်းနှီးလာသည်နှင့်အမျှ သဘာဝအတိုင်း အလိုလို သုံးစွဲမိလာပါလိမ့်မည် (ဥပမာ --help, --verbose, --version)။

Flag များသည် shell သဘောတူညီချက်များ (conventions) ၏ ကောင်းမွန်သော ပထမဆုံး ဥပမာတစ်ခု ဖြစ်ပါသည်။ Shell ဘာသာစကားသည် ကျွန်ုပ်တို့၏ ပရိုဂရမ်အား - သို့မဟုတ် -- ကို ဤကဲ့သို့ သီးသန့် အသုံးပြုရန် အတင်းအကျပ် လိုအပ်ခြင်း မရှိပါ။ myprogram +myoption myfile ကဲ့သို့ syntax မျိုးဖြင့် ပရိုဂရမ်တစ်ခု ရေးသားခြင်းကို မည်သည့်အရာကမျှ တားဆီးထားခြင်းမရှိသော်လည်း Dash များကို သုံးစွဲရန် မျှော်လင့်ထားကြသဖြင့် ရှုပ်ထွေးမှုများ ဖြစ်ပေါ်စေပါလိမ့်မည်။ လက်တွေ့တွင် ပရိုဂရမ်မင်းဘာသာစကား အများစုသည် CLI flag parsing library များကို ပံ့ပိုးပေးထားကြပါသည် (ဥပမာ dash syntax ဖြင့် argument များကို parse လုပ်ရန် python ရှိ argparse)။

CLI ပရိုဂရမ်များ၏ အခြား အသုံးများသော သဘောတူညီချက်တစ်ခုမှာ ပရိုဂရမ်များသည် အမျိုးအစားတူညီသော argument အရေအတွက် အမျိုးမျိုးကို လက်ခံနိုင်ခြင်း ဖြစ်ပါသည်။ ဤကဲ့သို့ argument များကို ပေးလိုက်သောအခါ command သည် ၎င်းတို့တစ်ခုစီအပေါ် တူညီသော လုပ်ဆောင်ချက်ကို လုပ်ဆောင်ပေးပါသည်။

mkdir src
mkdir docs
# သည် အောက်ပါအတိုင်း ရေးသည်နှင့် အတူတူပင်ဖြစ်သည်
mkdir src docs

ဤ syntax sugar သည် စတင်ချိန်တွင် မလိုအပ်ဟု ထင်ရနိုင်သော်လည်း globbing နှင့် တွဲဖက်လိုက်သောအခါ အလွန်ပင် စွမ်းအားထက်မြက်လာပါသည်။ Globbing သို့မဟုတ် globs တွေဆိုတာ ပရိုဂရမ်ကို မခေါ်မီ shell က အလိုအလျောက် ဖြန့်ကျက်ပေးမည့် (expand) အထူး pattern များ ဖြစ်ကြပါသည်။

ဥပမာ လက်ရှိ folder အတွင်းရှိ .py file အားလုံးကို nonrecursively ဖျက်ပစ်ချင်သည် ဆိုပါစို့။ ယခင်သင်ခန်းစာတွင် လေ့လာခဲ့သည်များအရ အောက်ပါအတိုင်း Run ပြီး ပြုလုပ်နိုင်ပါသည် -

for file in $(ls | grep -P '\.py$'); do
    rm "$file"
done

သို့သော် ၎င်းကို rm *.py ဟု သာ ရေးသားပြီး အစားထိုးနိုင်ပါသည်။

terminal ထဲတွင် rm *.py ဟု ရိုက်ထည့်လိုက်သောအခါ shell သည် /bin/rm ပရိုဂရမ်ကို ['*.py'] ဆိုသည့် argument ဖြင့် ခေါ်ယူမည် မဟုတ်ပါ။ ယင်းအစား shell သည် လက်ရှိ folder အတွင်း *.py pattern နှင့် ကိုက်ညီသော file များကို ရှာဖွေမည်ဖြစ်ပြီး၊ ဤနေရာတွင် * သည် မည်သည့်အမျိုးအစားမဆို ရှိသော စာလုံး အရေအတွက် သုည သို့မဟုတ် သုညထက်ပိုသော string တစ်ခုခုနှင့် ကိုက်ညီမှု ရှိနိုင်ပါသည်။ ထို့ကြောင့် ကျွန်ုပ်တို့၏ folder တွင် main.py နှင့် utils.py တို့ ရှိနေပါက rm ပရိုဂရမ်သည် ['main.py', 'utils.py'] ဆိုသည့် argument များကို လက်ခံရရှိမည် ဖြစ်ပါသည်။

တွေ့ရှိရမည့် အသုံးအများဆုံး glob တွေကတော့ wildcard * (သုည သို့မဟုတ် သုညထက်ပိုသော မည်သည့်စာလုံးမဆို)၊ ? (အတိအကျ တစ်လုံးတည်းသော မည်သည့်စာလုံးမဆို) နှင့် curly braces တွေ ဖြစ်ကြပါတယ်။ Curly braces {} သည် ကော်မာခံထားသော pattern စာရင်းကို argument အများအပြားအဖြစ် ဖြန့်ကျက် (expand) ပေးပါသည်။

လက်တွေ့တွင် glob များကို စိတ်ဝင်စားဖွယ် ဥပမာများဖြင့် နားလည်ရလွယ်ကူပါသည် -

touch folder/{a,b,c}.py
# အောက်ပါအတိုင်း Expand ဖြစ်သွားမည်
touch folder/a.py folder/b.py folder/c.py

convert image.{png,jpg}
# အောက်ပါအတိုင်း Expand ဖြစ်သွားမည်
convert image.png image.jpg

cp /path/to/project/{setup,build,deploy}.sh /newpath
# အောက်ပါအတိုင်း Expand ဖြစ်သွားမည်
cp /path/to/project/setup.sh /path/to/project/build.sh /path/to/project/deploy.sh /newpath

# Globbing နည်းလမ်းများကို ပေါင်းစပ်၍လည်း သုံးနိုင်သည်
mv *{.py,.sh} folder
# *.py နှင့် *.sh file အားလုံးကို ရွှေ့ပေးမည် ဖြစ်သည်

အချို့သော shell များ (ဥပမာ zsh) သည် recursive path များအထိ ပါဝင်အောင် expand လုပ်ပေးနိုင်သော ** ကဲ့သို့ ပိုမိုဆင့်မြင့်သည့် globbing ပုံစံများကိုပင် ပံ့ပိုးပေးထားပါသည်။ ထို့ကြောင့် rm **/*.py သည် .py file အားလုံးကို recursive နည်းဖြင့် ဖျက်ပစ်မည် ဖြစ်ပါသည်။

Streams များ

အောက်ပါအတိုင်း ပရိုဂရမ် pipeline တစ်ခုကို Run သည့်အခါတိုင်း -

cat myfile | grep -P '\d+' | uniq -c

grep ပရိုဂရမ်သည် cat နှင့် uniq ပရိုဂရမ် နှစ်ခုလုံးနှင့် ဆက်သွယ်ဆောင်ရွက်နေသည်ကို တွေ့မြင်နိုင်ပါသည်။

ဤနေရာတွင် သတိပြုရန် အရေးကြီးသော အချက်မှာ ပရိုဂရမ် သုံးခုလုံးသည် တစ်ပြိုင်နက်တည်း Run နေခြင်း ဖြစ်ပါသည်။ အသေးစိတ် ပြောရလျှင် shell သည် ပထမဆုံး cat ကို ခေါ်၊ ပြီးမှ grep ကို ခေါ်၊ ပြီးမှ uniq ကို ခေါ်ခြင်း မဟုတ်ပါ။ ယင်းအစား ပရိုဂရမ် သုံးခုလုံးကို တစ်ပြိုင်နက်တည်း စတင်လိုက်ပြီး shell က cat ၏ output ကို grep ၏ input သို့၊ grep ၏ output ကို uniq ၏ input သို့ ချိတ်ဆက်ပေးခြင်း ဖြစ်ပါသည်။ Pipe operator | ကို အသုံးပြုသောအခါ shell သည် စီးကြောင်းတစ်လျှောက်တွင် ပရိုဂရမ်တစ်ခုမှ နောက်တစ်ခုသို့ စီးဆင်းသွားသော data stream များကို စီမံဆောင်ရွက်ပေးပါသည်။

ဤပြိုင်တူလုပ်ဆောင်မှုကို သက်သေပြနိုင်ပါသည်၊ pipeline တစ်ခုအတွင်းရှိ command အားလုံးသည် ချက်ချင်း စတင်ကြပါသည် -

$ (sleep 15 && cat numbers.txt) | grep -P '^\d$' | sort | uniq  &
[1] 12345
$ ps | grep -P '(sleep|cat|grep|sort|uniq)'
  32930 pts/1    00:00:00 sleep
  32931 pts/1    00:00:00 grep
  32932 pts/1    00:00:00 sort
  32933 pts/1    00:00:00 uniq
  32948 pts/1    00:00:00 grep

cat မှလွဲ၍ အခြား process အားလုံးသည် ချက်ချင်း Run နေသည်ကို တွေ့မြင်နိုင်ပါသည်။ Shell သည် ၎င်းတို့ထဲမှ မည်သည့် process မျှ မပြီးဆုံးမီ process အားလုံးကို စတင်ဖွင့်လှစ်ပြီး ၎င်းတို့၏ stream များကို ချိတ်ဆက်ပေးလိုက်ပါသည်။ cat သည် sleep ပြီးဆုံးမှသာ စတင်မည်ဖြစ်ပြီး၊ cat ၏ output ကို grep ထံ ပို့ပေးကာ ၎င်းမှတစ်ဆင့် ဆက်လက် စီးဆင်းသွားမည် ဖြစ်ပါသည်။

ပရိုဂရမ်တိုင်းတွင် stdin (standard input အတွက်) ဟု ခေါ်သော input stream တစ်ခု ရှိပါသည်။ Pipe လုပ်သောအခါ stdin ကို အလိုအလျောက် ချိတ်ဆက်ပေးပါသည်။ Script အတွင်းတွင် ပရိုဂရမ်အများစုသည် “stdin မှ ဖတ်ရှုမည်” ဟု အဓိပ္ပာယ်ရသော file အမည်အဖြစ် - ကို လက်ခံကြပါသည် -

# ဒေတာသည် pipe မှ လာသောအခါ ဤနှစ်ခုသည် အတူတူပင်ဖြစ်သည်
echo "hello" | grep "hello"
echo "hello" | grep "hello" -

အလားတူပင် ပရိုဂရမ်တိုင်းတွင် output stream နှစ်ခု ရှိကြပါသည် - stdout နှင့် stderr တို့ဖြစ်ကြပါသည်။ Standard output (stdout) သည် အများဆုံး တွေ့ကြုံရသည့် stream ဖြစ်ပြီး ပရိုဂရမ်၏ output ကို pipeline ၏ နောက်ထပ် command ထံသို့ pipe လုပ်ရာတွင် အသုံးပြုပါသည်။ Standard error (stderr) သည် ပရိုဂရမ်များမှ သတိပေးချက်များနှင့် အခြား ပြဿနာအမျိုးအစားများကို သတင်းပို့ရန်အတွက် သီးသန့် ရည်ရွယ်ထားသော တခြား stream တစ်ခုဖြစ်ပြီး အဆိုပါ output သည် စီးကြောင်း၏ နောက် command မှ parse လုပ်ခြင်း မခံရပါ။

$ ls /nonexistent
ls: cannot access '/nonexistent': No such file or directory
$ ls /nonexistent | grep "pattern"
ls: cannot access '/nonexistent': No such file or directory
# stderr ကို pipe မလုပ်ထားသောကြောင့် Error စာတမ်းသည် ပေါ်နေဆဲဖြစ်သည်
$ ls /nonexistent 2>/dev/null
# Output မရှိတော့ပါ - stderr ကို /dev/null သို့ လမ်းကြောင်းပြောင်းလိုက်သောကြောင့်ဖြစ်သည်

Shell သည် ဤ stream များကို လမ်းကြောင်းပြောင်းရန် (redirect) အတွက် syntax များကို ထောက်ပံ့ပေးထားပါသည်။ အောက်တွင် ဥပမာအချို့ကို ဖော်ပြထားပါသည် -

# stdout ကို file တစ်ခုသို့ လမ်းကြောင်းပြောင်းရန် (အသစ်ထပ်ရေးမည်)
echo "hello" > output.txt

# stdout ကို file တစ်ခုသို့ လမ်းကြောင်းပြောင်းရန် (အနောက်မှ ဆက်ရေးမည်)
echo "world" >> output.txt

# stderr ကို file တစ်ခုသို့ လမ်းကြောင်းပြောင်းရန်
ls foobar 2> errors.txt

# stdout နှင့် stderr နှစ်ခုလုံးကို file တစ်ခုတည်းသို့ လမ်းကြောင်းပြောင်းရန်
ls foobar &> all_output.txt

# stdin ကို file တစ်ခုမှ လမ်းကြောင်းပြောင်းယူရန်
grep "pattern" < input.txt

# /dev/null သို့ လမ်းကြောင်းပြောင်း၍ output ကို စွန့်ပစ်ရန်
cmd > /dev/null 2>&1

Unix ဒဿန (philosophy) ကို ထင်ဟပ်စေသည့် အခြား စွမ်းအားထက်မြက်သော tool တစ်ခုမှာ fuzzy finder တစ်ခုဖြစ်သည့် fzf ဖြစ်ပါသည်။ ၎င်းသည် stdin မှ စာကြောင်းများကို ဖတ်ရှုပြီး စစ်ထုတ်ရန်နှင့် ရွေးချယ်ရန် အပြန်အလှန်လုပ်ဆောင်နိုင်သော interface တစ်ခုကို ပံ့ပိုးပေးပါသည် -

$ ls | fzf
$ cat ~/.bash_history | fzf

fzf ကို shell လုပ်ဆောင်ချက်များစွာနှင့် ပေါင်းစပ်အသုံးပြုနိုင်ပါသည်။ Shell စိတ်ကြိုက်ပြင်ဆင်ခြင်းအကြောင်း ဆွေးနွေးသည့်အခါ ၎င်း၏ အသုံးပြုပုံများကို ပိုမိုတွေ့မြင်ရပါလိမ့်မည်။

Environment variables များ

Bash တွင် variable များ သတ်မှတ်ရန် foo=bar syntax ကို အသုံးပြုပြီး variable ၏ တန်ဖိုးကို ရယူရန် $foo syntax ကို သုံးပါသည်။ foo = bar ဟု ရေးသားခြင်းသည် မှားယွင်းသော syntax ဖြစ်ကြောင်း သတိပြုပါ၊ အကြောင်းမှာ shell က ၎င်းကို foo ပရိုဂရမ်အား ['=', 'bar'] ဆိုသည့် argument များနှင့် ခေါ်ယူခြင်းအဖြစ် parse လုပ်မည်ဖြစ်သောကြောင့် ဖြစ်သည်။ Shell scripting တွင် space (ကွက်လပ်) ၏ အခန်းကဏ္ဍမှာ argument များကို ခွဲခြားပေးရန် ဖြစ်ပါသည်။ ဤပြုမူပုံသည် ရှုပ်ထွေးနိုင်ပြီး ကျင့်သုံးရခက်ခဲနိုင်သဖြင့် စိတ်ထဲတွင် မှတ်သားထားပါ။

Shell variable များတွင် type များ မရှိပါ၊ ၎င်းတို့အားလုံးသည် string များ ဖြစ်ကြသည်။ Shell တွင် string expression များကို ရေးသားသောအခါ single quote (') နှင့် double quote (") တို့သည် အပြန်အလှန် အစားထိုး၍ မရကြောင်း သတိပြုပါ။ ' ဖြင့် ဝိုင်းထားသော string များသည် စာသားအတိုင်း ဖြစ်ပြီး variable များကို expand လုပ်မည်မဟုတ်၊ command substitution ပြုလုပ်မည်မဟုတ် သို့မဟုတ် escape sequence များကို ပရိုဆက်လုပ်မည်မဟုတ်ပါ။ ၎င်းနှင့် ဆန့်ကျင်ဘက်အနေဖြင့် " ဖြင့် ဝိုင်းထားသော string များသည် ထိုသို့ ပြုလုပ်ပေးမည် ဖြစ်သည်။

foo=bar
echo "$foo"
# bar ဟု ရိုက်နှိပ်မည်
echo '$foo'
# $foo ဟု ရိုက်နှိပ်မည်

Command တစ်ခု၏ output ကို variable တစ်ခုအတွင်း သမ်းဆည်းရန်အတွက် command substitution ကို အသုံးပြုပါသည်။ ကျွန်ုပ်တို့သည်

files=$(ls)
echo "$files" | grep README
echo "$files" | grep ".py"

ဟု Run လိုက်သောအခါ ls ၏ output (အတိအကျပြောရလျှင် stdout) ကို နောက်ပိုင်းတွင် ရယူသုံးစွဲနိုင်သည့် variable $files ထဲသို့ ထည့်သွင်းပေးလိုက်မည် ဖြစ်သည်။ $files variable ၏ အကြောင်းအရာတွင် ls output မှ newline များ ပါဝင်နေပြီး၊ ၎င်းသည် grep ကဲ့သို့သော ပရိုဂရမ်များက item တစ်ခုစီကို သီးခြားစီ လုပ်ဆောင်နိုင်ရန် သိရှိသည့် ပုံစံဖြစ်ပါသည်။

သိသူနည်းပါးသော အလားတူ feature တစ်ခုမှာ process substitution ဖြစ်ပြီး၊ <( CMD ) သည် CMD ကို Run ပြီး ရလဒ် output ကို ယာယီ file တစ်ခုအတွင်း ထည့်သွင်းကာ <() ကို ထို file ၏ အမည်ဖြင့် အစားထိုးပေးမည် ဖြစ်ပါသည်။ ဒါဟာ command တွေက STDIN အစား file ဖြင့် တန်ဖိုးများ ပေးပို့ရန် မျှော်လင့်ထားသည့်အခါမျိုးတွင် အသုံးဝင်ပါသည်။ ဥပမာအားဖြင့် diff <(ls src) <(ls docs) သည် src နှင့် docs directory နှစ်ခုအတွင်းရှိ file များ၏ ခြားနားချက်များကို ပြသပေးမည် ဖြစ်ပါသည်။

Shell ပရိုဂရမ်တစ်ခုသည် အခြားပရိုဂရမ်တစ်ခုကို ခေါ်ယူသည့်အခါတိုင်း ၎င်းသည် environment variables ဟု အလွယ်ခေါ်ကြသော variable အစုအဝေးတစ်ခုကို ထပ်ဆင့် ပေးပို့ပေးပါသည်။ Shell အတွင်းမှနေ၍ လက်ရှိ environment variable များကို printenv ကို Run ခြင်းဖြင့် ရှာဖွေနိုင်ပါသည်။ Environment variable တစ်ခုကို အတိအကျ ပေးပို့ရန်အတွက် command ၏ အရှေ့တွင် variable သတ်မှတ်ချက်ကို အောက်ပါအတိုင်း ရှေ့ဆက်ထည့်နိုင်သည် -

Environment variable များကို သဘောတူညီချက် (convention) အဖြစ် စာလုံးကြီးများ (ALL_CAPS) ဖြင့် ရေးသားလေ့ရှိကြပါသည် (ဥပမာ HOME, PATH, DEBUG)။ ဒါဟာ နည်းပညာအရ မဖြစ်မနေ လိုအပ်ချက်မဟုတ်ဘဲ convention တစ်ခုသာ ဖြစ်သော်လည်း ၎င်းကို လိုက်နာခြင်းဖြင့် ပုံမှန်အားဖြင့် စာလုံးသေးဖြင့် ရေးသားသော local shell variable များနှင့် environment variable များကို ခွဲခြားသိမြင်စေရန် ကူညီပေးပါသည်။

TZ=Asia/Tokyo date  # တိုကျိုမြို့၏ လက်ရှိအချိန်ကို ရိုက်နှိပ်မည်
echo $TZ  # TZ ကို child command အတွက်သာ သတ်မှတ်ခဲ့သောကြောင့် ဤနေရာတွင် လွတ်နေမည် ဖြစ်သည်

သို့မဟုတ်ပါက ကျွန်ုပ်တို့၏ လက်ရှိ environment ကို ပြုပြင်ပြောင်းလဲပေးမည့် export built-in function ကို အသုံးပြုနိုင်ပြီး ထိုသို့ ပြုလုပ်ခြင်းဖြင့် child process အားလုံးသည် အဆိုပါ variable ကို လက်ခံရရှိသွားမည် ဖြစ်သည် -

export DEBUG=1
# ဤအချိန်မှစ၍ ပရိုဂရမ်အားလုံးသည် ၎င်းတို့၏ environment တွင် DEBUG=1 ပါရှိသွားမည် ဖြစ်သည်
bash -c 'echo $DEBUG'
# 1 ဟု ရိုက်နှိပ်မည်

Variable တစ်ခုကို ဖျက်ပစ်ရန်အတွက် unset built-in command ကို သုံးပါ၊ ဥပမာ unset DEBUG။

Environment variable တွေဟာ အခြားသော shell သဘောတူညီချက် (convention) တစ်ခု ဖြစ်ပါတယ်။ ၎င်းတို့ကို ပရိုဂရမ်အများအပြား၏ ပြုမူပုံကို အတိအကျ ပြင်ဆင်ခိုင်းခြင်းထက် သွယ်ဝိုက်သော နည်းလမ်းဖြင့် ပြောင်းလဲရန် အသုံးပြုနိုင်ပါသည်။ ဥပမာအားဖြင့် shell သည် လက်ရှိ user ၏ home folder path ဖြင့် $HOME environment variable ကို သတ်မှတ်ပေးထားပါသည်။ ထို့ကြောင့် ပရိုဂရမ်များသည် --home /home/alice ကဲ့သို့ အတိအကျ တောင်းဆိုရန် မလိုဘဲ ဤအချက်အလက်ကို ရယူရန် အဆိုပါ variable ကို အသုံးပြုနိုင်ပါသည်။ အခြား အသုံးများသော ဥပမာတစ်ခုမှာ $TZ ဖြစ်ပြီး ပရိုဂရမ်များစွာသည် သတ်မှတ်ထားသော စံတော်ချိန် (timezone) အလိုက် ရက်စွဲနှင့် အချိန်များကို ပုံစံထုတ်ရန် သုံးစွဲကြပါသည်။

Return codes များ

ယခင်က တွေ့မြင်ခဲ့ရသည့်အတိုင်း shell ပရိုဂရမ်တစ်ခု၏ အဓိက output ကို stdout/stderr stream များနှင့် filesystem ဘေးထွက်ဆိုးကျိုး (side effects) များမှတစ်ဆင့် ဖော်ပြပေးပါသည်။

မူလအားဖြင့် shell script တစ်ခုသည် exit code သုည (0) ကို Return ပြန်ပေးမည် ဖြစ်ပါသည်။ သဘောတူညီချက် (convention) အရ သုညသည် အရာအားလုံး အဆင်ပြေပြေ ပြီးမြောက်ခဲ့သည်ဟု အဓိပ္ပာယ်ရပြီး သုညမဟုတ်သော တန်ဖိုး (nonzero) သည် ပြဿနာအချို့ ကြုံတွေ့ခဲ့ရသည်ဟု အဓိပ္ပာယ်ရပါသည်။ သုညမဟုတ်သော exit code ကို Return ပြန်ပေးရန်အတွက် exit NUM ဆိုသည့် shell built-in ကို အသုံးပြုရပါမည်။ နောက်ဆုံး Run ခဲ့သော command ၏ return code ကို အထူး variable $? ကို ဝင်ရောက်ကြည့်ရှုခြင်းဖြင့် ရယူနိုင်ပါသည်။

Shell တွင် AND နှင့် OR လုပ်ဆောင်ချက်များကို လုပ်ဆောင်ရန်အတွက် boolean operator များဖြစ်ကြသော && နှင့် || တို့ ပါရှိပါသည်။ ပုံမှန် ပရိုဂရမ်မင်းဘာသာစကားများတွင် တွေ့ရသည်များနှင့် မတူဘဲ shell ရှိ operator များသည် ပရိုဂရမ်များ၏ return code အပေါ် မူတည်၍ လုပ်ဆောင်ကြခြင်း ဖြစ်ပါသည်။ ဤနှစ်ခုလုံးသည် short-circuiting operator များ ဖြစ်ကြပါသည်။ ဆိုလိုသည်မှာ ယခင် command များ၏ အောင်မြင်မှု သို့မဟုတ် လှဲမှားမှုအပေါ် မူတည်၍ နောက် command များကို အခြေအနေအလိုက် Run ရန် ၎င်းတို့ကို အသုံးပြုနိုင်ပြီး၊ အောင်မြင်မှုကို return code သည် သုည ဟုတ်မဟုတ်အပေါ် မူတည်၍ ဆုံးဖြတ်ခြင်း ဖြစ်ပါသည်။ ဥပမာအချို့ -

# grep အောင်မြင်ပါက (ကိုက်ညီမှုတွေ့ရှိပါက) မှသာ echo ကို Run မည်ဖြစ်သည်
grep -q "pattern" file.txt && echo "Pattern found"

# grep မအောင်မြင်ပါက (ကိုက်ညီမှုမတွေ့ပါက) မှသာ echo ကို Run မည်ဖြစ်သည်
grep -q "pattern" file.txt || echo "Pattern not found"

# true သည် အမြဲတမ်း အောင်မြင်သော shell ပရိုဂရမ်တစ်ခု ဖြစ်သည်
true && echo "This will always print"

# false သည် အမြဲတမ်း မအောင်မြင်သော shell ပရိုဂရမ်တစ်ခု ဖြစ်သည်
false || echo "This will always print"

အလားတူ သဘောတရားကို if နှင့် while statement များတွင်လည်း ကျင့်သုံးသည်၊ ၎င်းတို့ နှစ်ခုလုံးသည် ဆုံးဖြတ်ချက်ချရန်အတွက် return code များကို အသုံးပြုကြပါသည် -

# if သည် condition command ၏ return code ကို အသုံးပြုသည် (0 = true, nonzero = false)
if grep -q "pattern" file.txt; then
    echo "Found"
fi

# while loop သည် command က 0 ပြန်ပေးနေသရွေ့ ဆက်လက် လုပ်ဆောင်နေမည်ဖြစ်သည်
while read line; do
    echo "$line"
done < file.txt

Signals များ

အချို့သော အခြေအနေများတွင် ပရိုဂရမ်တစ်ခု လုပ်ဆောင်နေစဉ်အတွင်း ဥပမာ command တစ်ခု ပြီးမြောက်ရန် အချိန်ကြာလွန်းနေပါက ၎င်းအား ကြားဖြတ်ရပ်တန့်ရန် လိုအပ်ပါလိမ့်မည်။ ပရိုဂရမ်တစ်ခုကို ကြားဖြတ်ရပ်တန့်ရန် အရိုးရှင်းဆုံး နည်းလမ်းမှာ Ctrl-C ကို နှိပ်လိုက်ခြင်းဖြစ်ပြီး အဆိုပါ command သည် ရပ်တန့်သွားလေ့ရှိပါသည်။ သို့သော် ဒါဟာ လက်တွေ့မှာ မည်သို့ အလုပ်လုပ်သနည်း၊ ၎င်းသည် ရံဖန်ရံခါ process ကို အဘယ်ကြောင့် ရပ်တန့်ရန် ပျက်ကွက်ရသနည်း။

$ sleep 100
^C
$

ဤနေရာတွင် ^C ဆိုသည်မှာ terminal ထဲတွင် Ctrl-C ကို ရိုက်ထည့်လိုက်သောအခါ ဖော်ပြပေးသည့် ပုံစံဖြစ်ပါသည်။

ကွယ်လွန်နောက်ကွယ်တွင် ဖြစ်ပျက်သွားသည်မှာ အောက်ပါအတိုင်း ဖြစ်ပါသည် -

  1. ကျွန်ုပ်တို့ Ctrl-C ကို နှိပ်လိုက်သည်
  2. Shell က ထိုအထူး စာလုံးပေါင်းစပ်မှုကို ခွဲခြားသိရှိလိုက်သည်
  3. Shell process က SIGINT signal တစ်ခုကို sleep process ထံသို့ ပေးပို့လိုက်သည်
  4. Signal သည် sleep process ၏ လုပ်ဆောင်မှုကို ကြားဖြတ်ရပ်တန့်လိုက်သည်

Signal များသည် အထူး ဆက်သွယ်ရေး ယန္တရားတစ်ခု ဖြစ်ကြသည်။ Process တစ်ခုသည် signal တစ်ခုကို လက်ခံရရှိသောအခါ ၎င်း၏ လုပ်ဆောင်မှုကို ရပ်တန့်လိုက်ပြီး signal ကို ဖြေရှင်းကာ အဆိုပါ signal မှ ပေးပို့လိုက်သော အချက်အလက်များအပေါ် မူတည်၍ လုပ်ဆောင်မှု စီးဆင်းပုံကို ပြောင်းလဲနိုင်ပါသည်။ ဤအကြောင်းကြောင့် signal များကို software interrupts ဟု ခေါ်ဆိုကြပါသည်။

ကျွန်ုပ်တို့၏ အခြေအနေတွင် Ctrl-C ကို ရိုက်ထည့်လိုက်သောအခါ ၎င်းသည် process ထံသို့ SIGINT signal ပေးပို့ရန် shell ကို တိုက်တွန်းလိုက်ခြင်း ဖြစ်ပါသည်။ အောက်တွင် SIGINT ကို ဖမ်းယူပြီး ပစ်ပယ်ထားကာ ဆက်လက် ရပ်တန့်ခြင်းမရှိတော့သည့် Python ပရိုဂရမ်၏ အသေးငယ်ဆုံး ဥပမာကို ဖော်ပြထားပါသည်။ ဤပရိုဂရမ်ကို Kill လုပ်ရန်အတွက် Ctrl-\ ကို ရိုက်ထည့်ခြင်းဖြင့် SIGQUIT signal ကို အစားထိုး အသုံးပြုနိုင်ပါပြီ။

#!/usr/bin/env bash
import signal, time

def handler(signum, time):
    print("\nI got a SIGINT, but I am not stopping")

signal.signal(signal.SIGINT, handler)
i = 0
while True:
    time.sleep(.1)
    print("\r{}".format(i), end="")
    i += 1

ဤပရိုဂရမ်ထံသို့ SIGINT နှစ်ကြိမ် ပေးပို့ပြီးနောက် SIGQUIT ပေးပို့လိုက်သောအခါ ဖြစ်ပေါ်လာပုံမှာ အောက်ပါအတိုင်း ဖြစ်ပါသည်။ Terminal ထဲတွင် ရိုက်ထည့်လိုက်သောအခါ Ctrl ကို ^ ဖြင့် ဖော်ပြကြောင်း သတိပြုပါ -

$ python sigint.py
24^C
I got a SIGINT, but I am not stopping
26^C
I got a SIGINT, but I am not stopping
30^\[1]    39913 quit       python sigint.py

SIGINT နှင့် SIGQUIT နှစ်ခုလုံးသည် ပုံမှန်အားဖြင့် terminal ဆိုင်ရာ တောင်းဆိုချက်များနှင့် ဆက်စပ်နေသော်လည်း၊ process တစ်ခုအား သပ်သပ်ရပ်ရပ် ပိတ်သိမ်းရန် တောင်းဆိုသည့် ပိုမို အထွေထွေကျသော signal မှာ SIGTERM signal ဖြစ်ပါသည်။ ဤ signal ကို ပေးပို့ရန်အတွက် kill command ကို kill -TERM <PID> syntax ဖြင့် အသုံးပြုနိုင်ပါသည်။

Signal များသည် process တစ်ခုကို kill ပြုလုပ်ခြင်းထက် အခြားအရာများကိုလည်း ဆောင်ရွက်နိုင်ပါသည်။ ဥပမာ SIGSTOP သည် process ကို ခဏရပ်တန့် (pause) စေပါသည်။ Terminal တွင် Ctrl-Z ရိုက်ထည့်ပါက Terminal Stop ၏ အတိုကောက်ဖြစ်သော SIGTSTP signal ကို ပေးပို့ရန် shell ကို တိုက်တွန်းမည် ဖြစ်သည် (ဆိုလိုသည်မှာ SIGSTOP ၏ terminal ဗားရှင်းဖြစ်သည်)။

ထို့နောက် ရပ်တန့်ထားသော job ကို foreground တွင် ဖြစ်စေ၊ background တွင် ဖြစ်စေ အသီးသီး fg သို့မဟုတ် bg များကို အသုံးပြု၍ ဆက်လက် လုပ်ဆောင်စေနိုင်ပါသည်။

jobs command သည် လက်ရှိ terminal session နှင့် ဆက်စပ်နေသော မပြီးဆုံးသေးသည့် job များကို စာရင်းဖော်ပြပေးပါသည်။ အဆိုပါ job များကို ၎င်းတို့၏ pid ကို အသုံးပြု၍ ညွှန်းဆိုနိုင်သည် (ရှာဖွေရန် pgrep ကို သုံးနိုင်သည်)။ ပိုမိုလွယ်ကူစွာဖြင့် ၎င်းတို့၏ job number ( jobs မှ ဖော်ပြထားသော) ရှေ့တွင် ရာခိုင်နှုန်းသင်္ကေတ (%) ကို တပ်၍လည်း ညွှန်းဆိုနိုင်ပါသည်။ နောက်ဆုံး background သို့ ပို့ထားသော job ကို ညွှန်းဆိုရန် အထူး parameter ဖြစ်သော $! ကို အသုံးပြုနိုင်ပါသည်။

သိရှိထားရန် အခြားအချက်တစ်ခုမှာ command တစ်ခု၏ အနောက်တွင် & ထည့်သွင်းလိုက်ပါက command ကို background တွင် Run ပေးမည်ဖြစ်ပြီး သင့်ထံ prompt ပြန်လည် ရရှိစေမည် ဖြစ်သည်၊ သို့သော် ၎င်းသည် shell ၏ STDOUT ကို သုံးနေဆဲဖြစ်သဖြင့် စိတ်အနှောင့်အယှက် ဖြစ်စရာ ကောင်းနိုင်ပါသည် (ထိုသို့ဖြစ်ပါက shell redirection များကို သုံးပါ)။ အလားတူပင် Run နေပြီးသား ပရိုဂရမ်တစ်ခုကို background သို့ ပို့ရန် Ctrl-Z နှိပ်ပြီးနောက် bg ဟု ရိုက်ထည့်နိုင်ပါသည်။

Background သို့ ပို့ထားသော process များသည် သင့် terminal ၏ child process များဖြစ်နေဆဲဖြစ်ပြီး terminal ကို ပိတ်လိုက်ပါက သေဆုံးသွားမည်ဖြစ်ကြောင်း သတိပြုပါ (ဒါဟာ အခြား signal တစ်ခုဖြစ်သည့် SIGHUP ကို ပေးပို့လိုက်ခြင်း ဖြစ်သည်)။ ထိုသို့ မဖြစ်ပွားစေရန်အတွက် ပရိုဂရမ်ကို nohup (SIGHUP ကို ပစ်ပယ်ရန် wrapper) ဖြင့် Run နိုင်သည်၊ သို့မဟုတ် process က စတင်နေပြီးပါက disown ကို အသုံးပြုနိုင်ပါသည်။ သို့မဟုတ်ပါက နောက်အပိုင်းတွင် လေ့လာရမည့် terminal multiplexer တစ်ခုကို အသုံးပြုနိုင်ပါသည်။

အောက်တွင် ဤသဘောတရားအချို့ကို ပြသရန် နမူနာ session တစ်ခုကို ဖော်ပြထားပါသည် -

$ sleep 1000
^Z
[1]  + 18653 suspended  sleep 1000

$ nohup sleep 2000 &
[2] 18745
appending output to nohup.out

$ jobs
[1]  + suspended  sleep 1000
[2]  - running    nohup sleep 2000

$ kill -SIGHUP %1
[1]  + 18653 hangup     sleep 1000

$ kill -SIGHUP %2   # nohup protects from SIGHUP

$ jobs
[2]  + running    nohup sleep 2000

$ kill %2
[2]  + 18745 terminated  nohup sleep 2000

SIGKILL သည် ထူးခြားသော signal တစ်ခုဖြစ်သည်၊ အကြောင်းမှာ ၎င်းကို process မှ ဖမ်းယူထား၍ မရနိုင်ဘဲ ချက်ချင်း အမြဲတမ်း ပြီးဆုံးစေသောကြောင့် ဖြစ်သည်။ သို့သော် ၎င်းတွင် မိဘမဲ့ child process များ ကျန်ရစ်ခဲ့ခြင်းကဲ့သို့သော မကောင်းသော ဘေးထွက်ဆိုးကျိုးများ ရှိနိုင်ပါသည်။

ဤအရာများနှင့် အခြား signal များအကြောင်းကို ဤနေရာတွင် သို့မဟုတ် man signal သို့မဟုတ် kill -l ဟု ရိုက်ထည့်၍ ပိုမို လေ့လာနိုင်ပါသည်။

Shell script များအတွင်း Signal များကို လက်ခံရရှိသည့်အခါ command များကို စီမံဆောင်ရွက်ရန် trap built-in ကို သုံးနိုင်သည်။ ဒါဟာ ရှင်းလင်းရေး လုပ်ငန်းစဉ်များ (cleanup) အတွက် အလွန် အသုံးဝင်ပါသည် -

#!/usr/bin/env bash
cleanup() {
    echo "Cleaning up temporary files..."
    rm -f /tmp/mytemp.*
}
trap cleanup EXIT  # Script မှ ထွက်ခွာသည့်အခါ cleanup ကို Run မည်
trap cleanup SIGINT SIGTERM  # Ctrl-C သို့မဟုတ် kill ပြုလုပ်သည့်အခါတွင်လည်း Run မည်

Remote Machine များ

ပရိုဂရမာများအနေဖြင့် ၎င်းတို့၏ နေ့စဉ်အလုပ်တွင် remote server များနှင့် တွဲဖက်လုပ်ဆောင်ရခြင်းမှာ ပိုမို ခေတ်စားလာပါသည်။ ဤနေရာတွင် အလုပ်အတွက် အသုံးအများဆုံး tool မှာ SSH (Secure Shell) ဖြစ်ပြီး ၎င်းသည် remote server သို့ ချိတ်ဆက်နိုင်ရန်နှင့် ယခု ရင်းနှီးနေပြီဖြစ်သော shell interface ကို ပံ့ပိုးပေးနိုင်ရန် ကူညီပေးပါလိမ့်မည်။ ကျွန်ုပ်တို့သည် server တစ်ခုသို့ အောက်ပါအတိုင်း command ဖြင့် ချိတ်ဆက်ကြပါသည် -

ssh alice@server.mit.edu

ဤနေရာတွင် ကျွန်ုပ်တို့သည် server.mit.edu server ရှိ user alice အဖြစ် ssh ဝင်ရောက်ရန် ကြိုးပမ်းနေခြင်း ဖြစ်ပါသည်။

ssh ၏ သတိမမူမိတတ်ကြသော feature တစ်ခုမှာ command များကို non-interactively (အပြန်အလှန် တုံ့ပြန်စရာ မလိုဘဲ) Run နိုင်သည့် စွမ်းရည် ဖြစ်ပါသည်။ ssh သည် command ၏ stdin ပေးပို့ခြင်းနှင့် stdout လက်ခံရရှိခြင်းတို့ကို မှန်ကန်စွာ စီမံပေးသဖြင့် ၎င်းကို အခြား command များနှင့် ပေါင်းစပ်အသုံးပြုနိုင်ပါသည် -

# ဤနေရာတွင် ls ကို remote တွင် Run ပြီး wc ကို local တွင် Run သည်
ssh alice@server ls | wc -l

# ဤနေရာတွင် ls နှင့် wc နှစ်ခုလုံးကို server တွင် Run သည်
ssh alice@server 'ls | wc -l'

ချိတ်ဆက်မှုပြတ်တောက်ခြင်း၊ sleep ဝင်/ထွက်ခြင်း၊ ကွန်ရက်ပြောင်းလဲခြင်းနှင့် latency မြင့်မားသော ချိတ်ဆက်မှုများကို စီမံနိုင်သည့် SSH အစားထိုး tool အဖြစ် Mosh ကို install လုပ်ကြည့်ပါ။

ssh က ကျွန်ုပ်တို့အား remote server တွင် command များကို Run ခွင့်ပြုရန်အတွက် ကျွန်ုပ်တို့သည် ထိုသို့ ပြုလုပ်ရန် အခွင့်အာဏာရှိကြောင်း သက်သေပြရန် လိုအပ်ပါသည်။ ၎င်းကို password များ သို့မဟုတ် ssh key များမှတစ်ဆင့် ပြုလုပ်နိုင်ပါသည်။ Key အခြေပြု authentication သည် အဓိက လျှို့ဝှက် private key ကို ထုတ်ဖော်ပြသခြင်း မရှိဘဲ client သည် ထို key ကို ပိုင်ဆိုင်ကြောင်း server သို့ သက်သေပြရန်အတွက် public-key cryptography ကို အသုံးပြုပါသည်။ Key အခြေပြု authentication သည် ပိုမို အဆင်ပြေပြီး ပိုမို လုံခြုံသဖြင့် ၎င်းကိုသာ ဦးစားပေး အသုံးပြုသင့်ပါသည်။ Private key (မကြာခဏ ~/.ssh/id_rsa နှင့် နောက်ပိုင်းတွင် ~/.ssh/id_ed25519) သည် ထိရောက်စွာဖြင့် သင့် password ဖြစ်သောကြောင့် ၎င်းအတိုင်း သဘောထားပါ၊ ၎င်း၏ အကြောင်းအရာများကို မည်သူ့ကိုမျှ မျှဝေခြင်း မပြုပါနှင့်။

Key တွဲတစ်ခုကို ထုတ်လုပ်ရန်အတွက် ssh-keygen ကို Run နိုင်ပါသည်။

ssh-keygen -a 100 -t ed25519 -f ~/.ssh/id_ed25519

အကယ်၍ သင့်အနေဖြင့် SSH key များ သုံးပြီး GitHub သို့ push ပြုလုပ်ရန် ချိန်ညှိဖူးပါက ဤနေရာတွင် ဖော်ပြထားသော အဆင့်များကို ပြုလုပ်ဖူးပြီးသားဖြစ်နိုင်ပြီး တရားဝင် key တွဲတစ်ခု ရှိနှင့်ပြီး ဖြစ်ပါလိမ့်မည်။ သင့်တွင် passphrase ရှိမရှိ စစ်ဆေးရန်နှင့် အတည်ပြုရန်အတွက် ssh-keygen -y -f /path/to/key ကို Run နိုင်ပါသည်။

Server ဘက်တွင် ssh သည် မည်သည့် client များကို ဝင်ရောက်ခွင့်ပြုရမည်နည်းဆိုသည်ကို ဆုံးဖြတ်ရန် .ssh/authorized_keys တွင် ကြည့်ရှုမည် ဖြစ်ပါသည်။ Public key တစ်ခုကို ကူးယူထည့်သွင်းရန် အောက်ပါအတိုင်း သုံးနိုင်သည် -

cat .ssh/id_ed25519.pub | ssh alice@remote 'cat >> ~/.ssh/authorized_keys'

# သို့မဟုတ် ပိုမို ရိုးရှင်းစွာဖြင့် (ssh-copy-id ရှိပါက)

ssh-copy-id -i .ssh/id_ed25519 alice@remote

Command များကို Run ခြင်းအပြင် SSH ဖြင့် ထူထောင်ထားသော ချိတ်ဆက်မှုကို server ထံသို့ သို့မဟုတ် server ထံမှ file များကို လုံခြုံစွာ လွှဲပြောင်းရန်လည်း အသုံးပြုနိုင်ပါသည်။ scp သည် ရိုးရာအကျဆုံး tool ဖြစ်ပြီး syntax မှာ scp path/to/local_file remote_host:path/to/remote_file ဖြစ်ပါသည်။ rsync သည် local နှင့် remote တွင် တူညီသော file များကို ရှာဖွေတွေ့ရှိခြင်းဖြင့် ၎င်းတို့ကို ထပ်မံ ကူးယူခြင်းမှ တားဆီးကာ scp ထက် ပိုမို ကောင်းမွန်အောင် ပြုလုပ်ထားပါသည်။ ၎င်းသည် symlink များနှင့် permission များအပေါ် ပိုမို သီးသန့် ထိန်းချုပ်နိုင်မှုကို ပေးစွမ်းပြီး ယခင် ကြားဖြတ် ရပ်တန့်သွားသော copy ပြုလုပ်မှုကို ပြန်လည် စတင်နိုင်သည့် --partial flag ကဲ့သို့သော အပို feature များ ပါရှိပါသည်။ rsync တွင် scp နှင့် အလားတူ syntax ရှိပါသည်။

SSH client ချိန်ညှိချက်များသည် ~/.ssh/config တွင် တည်ရှိပြီး ၎င်းသည် host များကို ကြေညာရန်နှင့် ၎င်းတို့အတွက် မူလ setting များကို သတ်မှတ်ရန် ခွင့်ပြုပေးပါသည်။ ဤ configuration file ကို ssh ကသာမက scp, rsync, mosh အစရှိသည့် အခြား ပရိုဂရမ်များကလည်း ဖတ်ရှုကြပါသည်။

Host vm
    User alice
    HostName 172.16.174.141
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

# Configuration များတွင် wildcard များကိုလည်း သုံးနိုင်သည်
Host *.mit.edu
    User alice

Terminal Multiplexers

Command line interface ကို အသုံးပြုသည့်အခါ အရာတစ်ခုထက်မက တစ်ပြိုင်နက်တည်း Run လိုသည့် အခြေအနေများ မကြာခဏ ရှိပါလိမ့်မည်။ ဥပမာ သင့်အနေဖြင့် သင့် editor နှင့် သင့်ပရိုဂရမ်ကို ဘေးချင်းယှဉ် Run လိုနိုင်ပါသည်။ ၎င်းကို terminal window အသစ်များ ဖွင့်လှစ်ခြင်းဖြင့် အောင်မြင်နိုင်သော်လည်း terminal multiplexer တစ်ခုကို အသုံးပြုခြင်းသည် ပိုမို စွမ်းဆောင်ရည်စုံလင်သော ဖြေရှင်းချက်တစ်ခု ဖြစ်ပါသည်။

tmux ကဲ့သို့သော Terminal multiplexer များသည် ပိုမိုထိရောက်သော နည်းလမ်းဖြင့် shell session အများအပြားနှင့် ဆက်သွယ်နိုင်စေရန် pane များနှင့် tab များကို အသုံးပြု၍ terminal window များကို multiplex ပြုလုပ်ခွင့် ပေးထားပါသည်။ ထို့ပြင် terminal multiplexer များသည် လက်ရှိ terminal session ကို ဖြုတ်ထုတ်ထားနိုင်ခဲ့ (detach) ပြီး နောက်ပိုင်းတွင် ပြန်လည် ချိတ်ဆက်နိုင်ခဲ့ (reattach) ပါသည်။ ဤအကြောင်းကြောင့် terminal multiplexer များသည် nohup သို့မဟုတ် အလားတူ လှည့်ကွက်များကို သုံးရန် မလိုတော့ဘဲ remote machine များနှင့် အလုပ်လုပ်သည့်အခါ အလွန်ပင် အဆင်ပြေလှပါသည်။

ယခုခေတ်တွင် အသုံးအများဆုံး terminal multiplexer မှာ tmux ဖြစ်ပါသည်။ tmux သည် စိတ်ကြိုက်ပြင်ဆင်နိုင်စွမ်း မြင့်မားပြီး သက်ဆိုင်ရာ keybinding များကို အသုံးပြု၍ tab များနှင့် pane အများအပြားကို ဖန်တီးကာ ၎င်းတို့အကြား လျင်မြန်စွာ သွားလာနိုင်ပါသည်။

tmux သည် သင့်အား ၎င်း၏ keybinding များကို သိရှိထားရန် မျှော်လင့်ထားပြီး၊ ၎င်းတို့အားလုံးသည် <C-b> x ပုံစံ ရှိကြပြီး ဆိုလိုသည်မှာ (၁) Ctrl+b ကို နှိပ်ပါ၊ (၂) Ctrl+b ကို လွှတ်ပါ၊ ထို့နောက် (၃) x ကို နှိပ်ပါ။ tmux တွင် အောက်ပါ အဆင့်ဆင့် ပါဝင်သော အရာများ (hierarchy of objects) ရှိပါသည် -

tmux အကြောင်း ပိုမို လေ့လာရန် ဤ ရိုးရှင်း လွယ်ကူသော သင်ခန်းစာနှင့် ဤ ပိုမို အသေးစိတ်ကျသော ရှင်းလင်းချက်ကို ဖတ်ရှုရန် စဉ်းစားပါ။

သင့် toolkit တွင် tmux နှင့် SSH တို့ ပါဝင်လာပြီဖြစ်ရာ မည်သည့် စက်တွင်မဆို သင့် ပတ်ဝန်းကျင်ကို ကိုယ်ပိုင်အိမ်ကဲ့သို့ ခံစားရစေချင်ပါလိမ့်မည်။ ထိုနေရာတွင် shell စိတ်ကြိုက်ပြင်ဆင်ခြင်း (shell customization) ရောက်ရှိလာပါသည်။

Shell ကို စိတ်ကြိုက်ပြင်ဆင်ခြင်း

Command line ပရိုဂရမ် အမြောက်အမြားကို dotfiles ဟု သိကြသော ရိုးရိုးစာသား (plain-text) file များကို သုံး၍ ချိန်ညှိပြင်ဆင်ကြပါသည် (အဘယ်ကြောင့်ဆိုသော် file အမည်များသည် . ဖြင့် စတင်လေ့ရှိကြပြီး ဥပမာ ~/.vimrc ဖြစ်သဖြင့် ၎င်းတို့ကို မူလအားဖြင့် ls directory စာရင်းထုတ်ရာတွင် ဝှက်ထားသောကြောင့် ဖြစ်သည်)။

Dotfiles တွေဟာ အခြားသော shell သဘောတူညီချက် (convention) တစ်ခု ဖြစ်ပါတယ်။ အရှေ့မှ အစက်သည် စာရင်းထုတ်ရာတွင် ၎င်းတို့ကို “ဝှက်ထားရန်” ဖြစ်ပါတယ် (ဟုတ်ပါတယ်၊ အခြား convention တစ်ခုပါပဲ)။

Shell တွေဟာ ထိုကဲ့သို့သော file များဖြင့် ချိန်ညှိပြင်ဆင်ရသည့် ပရိုဂရမ်များ၏ ဥပမာတစ်ခု ဖြစ်ကြပါသည်။ စတင်ပွင့်ချိန်တွင် သင့် shell သည် ၎င်း၏ configuration များကို Load လုပ်ရန် file အများအပြားကို ဖတ်ရှုမည် ဖြစ်ပါသည်။ Shell နှင့် သင်သည် login နှင့်/သို့မဟုတ် interactive session တစ်ခုကို စတင်နေခြင်း ရှိမရှိအပေါ် မူတည်၍ လုပ်ငန်းစဉ်တစ်ခုလုံးသည် တော်တော်လေး ရှုပ်ထွေးနိုင်ပါသည်။ ဤနေရာတွင် ဤအကြောင်းအရာအတွက် အလွန်ကောင်းမွန်သော အရင်းအမြစ်တစ်ခု ရှိပါသည်။

bash အတွက် သင့် .bashrc သို့မဟုတ် .bash_profile ကို ပြင်ဆင်ခြင်းသည် စနစ်အများစုတွင် အလုပ်လုပ်ပါလိမ့်မည်။ Dotfiles များမှတစ်ဆင့် ချိန်ညှိပြင်ဆင်နိုင်သော အခြားသော tools များ၏ ဥပမာအချို့မှာ -

အသုံးများသော configuration ပြောင်းလဲမှုတစ်ခုမှာ shell က ပရိုဂရမ်များကို ရှာဖွေနိုင်ရန် တည်နေရာအသစ်များ ပေါင်းစပ်ထည့်သွင်းပေးခြင်း ဖြစ်ပါသည်။ Software များကို install လုပ်သည့်အခါ ဤ pattern ကို တွေ့ကြုံရပါလိမ့်မည် -

export PATH="$PATH:path/to/append"

ဤနေရာတွင် ကျွန်ုပ်တို့သည် shell အား $PATH variable ၏ တန်ဖိုးကို ၎င်း၏ လက်ရှိတန်ဖိုးတွင် တည်နေရာအသစ် တစ်ခုပေါင်းစပ်၍ သတ်မှတ်ခိုင်းနေခြင်းဖြစ်ပြီး၊ child process အားလုံးသည် PATH အတွက် ဤတန်ဖိုးအသစ်ကို လက်ခံရရှိသွားမည် ဖြစ်ပါသည်။ ဒါဟာ child process များကို path/to/append အောက်တွင် တည်ရှိသော ပရိုဂရမ်များကို ရှာဖွေနိုင်စေမည် ဖြစ်ပါသည်။

သင့် shell ကို စိတ်ကြိုက်ပြင်ဆင်ခြင်းသည် command-line tools အသစ်များကို install လုပ်ခြင်းဟု မကြာခဏ အဓိပ္ပာယ်ရပါသည်။ Package manager များသည် ဤအရာကို လွယ်ကူစေပါသည်။ ၎င်းတို့သည် software များကို ဒေါင်းလုဒ်ဆွဲခြင်း၊ install လုပ်ခြင်းနှင့် update လုပ်ခြင်းတို့ကို စီမံဆောင်ရွက်ပေးကြပါသည်။ မတူညီသော operating system များတွင် မတူညီသော package manager များ ရှိကြပါသည် - macOS သည် Homebrew ကို သုံးသည်၊ Ubuntu/Debian တို့သည် apt ကို သုံးသည်၊ Fedora သည် dnf ကို သုံးပြီး Arch က pacman ကို သုံးပါသည်။ Shipping code သင်ခန်းစာတွင် package manager များကို အသေးစိတ် ထပ်မံ ဖော်ပြသွားပါမည်။

macOS တွင် Homebrew ကို အသုံးပြု၍ အသုံးဝင်သော tools နှစ်ခုကို မည်သို့ install လုပ်ရမည်ကို ဖော်ပြထားပါသည် -

# ripgrep: ပိုမိုကောင်းမွန်သော မူလပြင်ဆင်ချက်များ ပါဝင်ပြီး ပိုမိုမြန်ဆန်သော grep ဖြစ်သည်
brew install ripgrep

# fd: ပိုမိုမြန်ဆန်ပြီး သုံးစွဲသူအတွက် အဆင်ပြေသော find ဖြစ်သည်
brew install fd

ဤအရာများကို install လုပ်ပြီးပါက grep အစား rg ကိုလည်းကောင်း၊ find အစား fd ကိုလည်းကောင်း အသုံးပြုနိုင်ပါပြီ။

curl | bash နှင့် ပတ်သက်၍ သတိပေးချက် - curl -fsSL https://example.com/install.sh | bash ကဲ့သို့သော installation ညွှန်ကြားချက်များကို မကြာခဏ တွေ့ရပါလိမ့်မည်။ ဤ pattern သည် script တစ်ခုကို ဒေါင်းလုဒ်လုပ်ပြီး ချက်ချင်း Run လိုက်ခြင်း ဖြစ်ရာ အဆင်ပြေသော်လည်း စွန့်စားမှု ရှိပါသည်။ အကြောင်းမှာ သင် စစ်ဆေးမထားသော စိုက်ထုတ်ထားသည့် code ကို Run နေခြင်းကြောင့် ဖြစ်သည်။ ပိုမို လုံခြုံသော နည်းလမ်းမှာ ပထမဆုံး ဒေါင်းလုဒ်လုပ်ရန်၊ ပြန်လည် သုံးသပ်ရန်၊ ထို့နောက်မှ Run ရန် ဖြစ်ပါသည် -

curl -fsSL https://example.com/install.sh -o install.sh
less install.sh  # script ကို ပြန်လည်သုံးသပ်ပါ
bash install.sh

အချို့သော installer များသည် အနည်းငယ် ပိုမိုလုံခြုံသော ပုံစံကွဲကို အသုံးပြုကြသည် - /bin/bash -c "$(curl -fsSL https://url)" ၎င်းသည် အနည်းဆုံးတော့ သင့်လက်ရှိ shell အစား bash က script ကို parse လုပ်ပေးကြောင်း သေချာစေပါသည်။

Install လုပ်မထားသော command တစ်ခုကို Run ရန် ကြိုးစားသောအခါ သင့် shell က command not found ဟု ဖော်ပြပါလိမ့်မည်။ ဝဘ်ဆိုက် command-not-found.com သည် မတူညီသော package manager များနှင့် distribution များအလိုက် မည်သို့ install လုပ်ရမည်ဆိုသည်ကို ရှာဖွေနိုင်သည့် အသုံးဝင်သော အရင်းအမြစ်တစ်ခု ဖြစ်ပါသည်။

အခြား အသုံးဝင်သော tool တစ်ခုမှာ tldr ဖြစ်ပြီး ၎င်းသည် ရိုးရှင်း၍ ဥပမာကို အဓိကထားသော man page များကို ပံ့ပိုးပေးပါသည်။ ရှည်လျားသော Documentation များကို ဖတ်ရှုမည့်အစား အသုံးများသော အသုံးပြုမှု pattern များကို လျှောက်ကြည့်နိုင်ပါသည် -

$ tldr fd
  An alternative to find.
  Aims to be faster and easier to use than find.

  Recursively find files matching a pattern in the current directory:
      fd "pattern"

  Find files that begin with "foo":
      fd "^foo"

  Find files with a specific extension:
      fd --extension txt

အချို့အခြေအနေများတွင် ပရိုဂရမ်အသစ် တစ်ခုလုံး မလိုအပ်ဘဲ သီးသန့် flag များပါသည့် လက်ရှိ command ၏ အတိုကောက် (shortcut) မျှသာ လိုအပ်တတ်ပါသည်။ ထိုနေရာတွင် alias များ ရောက်ရှိလာပါသည်။

Shell built-in ဖြစ်သော alias ကို အသုံးပြု၍ ကျွန်ုပ်တို့ကိုယ်ပိုင် command alias များကိုလည်း ဖန်တီးနိုင်ပါသည်။ Shell alias ဆိုသည်မှာ ၎င်း expression ကို မစစ်ဆေးမီ သင့် shell က အလိုအလျောက် အစားထိုးပေးမည့် အခြား command တစ်ခုအတွက် အတိုကောက် ပုံစံဖြစ်ပါသည်။ ဥပမာအားဖြင့် bash ရှိ alias တစ်ခုတွင် အောက်ပါ တည်ဆောက်ပုံ ပါရှိသည် -

alias alias_name="command_to_alias arg1 arg2"

ညီမျှခြင်းသင်္ကေတ = ၏ ဘေးပတ်လည်တွင် space (ကွက်လပ်) မပါရှိကြောင်း သတိပြုပါ၊ အကြောင်းမှာ alias သည် argument တစ်ခုတည်းသာ ယူသော shell command တစ်ခု ဖြစ်သောကြောင့် ဖြစ်သည်။

Alias တွေမှာ အဆင်ပြေစေမည့် feature များစွာ ရှိကြပါတယ် -

# ဘုံသုံး flag များအတွက် အတိုကောက် ဖန်တီးပါ
alias ll="ls -lh"

# အသုံးများသော command များအတွက် စာရိုက်ရမှု အများအပြားကို သက်သာစေပါ
alias gs="git status"
alias gc="git commit"

# စာလုံးမှားယွင်း ရိုက်မိခြင်းမှ ကယ်တင်ပေးပါ
alias sl=ls

# ပိုမိုကောင်းမွန်သော မူလပြင်ဆင်ချက်များ ရရှိရန် လက်ရှိ command များကို ထပ်ရေးပါ
alias mv="mv -i"           # -i သည် အသစ်မထပ်ရေးမီ မေးမြန်းမည်ဖြစ်သည်
alias mkdir="mkdir -p"     # -p သည် လိုအပ်ပါက မိခင် dir များကို ဖန်တီးပေးမည်ဖြစ်သည်
alias df="df -h"           # -h သည် လူနားလည်လွယ်သော ပုံစံဖြင့် ရိုက်နှိပ်ပေးမည်ဖြစ်သည်

# Alias များကို ပေါင်းစပ်ဖွဲ့စည်းနိုင်သည်
alias la="ls -A"
alias lla="la -l"

# Alias ကို ပစ်ပယ်ထားရန် ရှေ့တွင် \ တပ်၍ Run ပါ
\ls
# သို့မဟုတ် unalias ဖြင့် alias ကို လုံးဝ ပိတ်ထားပါ
unalias la

# Alias သတ်မှတ်ချက်ကို ရယူရန် alias ဖြင့် ခေါ်ယူရုံမျှသာ ပြုလုပ်ပါ
alias ll
# ll='ls -lh' ဟု ရိုက်နှိပ်မည်ဖြစ်သည်

Alias များတွင် ကန့်သတ်ချက်များ ရှိသည် - ၎င်းတို့သည် command ၏ အလယ်တွင် argument များကို ယူ၍ မရပါ။ ပိုမို ရှုပ်ထွေးသော ပြုမူဆောင်ရွက်ချက်များအတွက် ၎င်းအစား shell function များကို သုံးသင့်ပါသည်။

Shell အများစုသည် ရာဇဝင် (history) ကို နောက်ပြန် ရှာဖွေရန်အတွက် Ctrl-R ကို ပံ့ပိုးပေးထားပါသည်။ Ctrl-R ကို နှိပ်ပြီး ယခင် command များကို ရှာဖွေရန် စာရိုက်ပါ။ အစောပိုင်းက fuzzy finder အဖြစ် fzf ကို မိတ်ဆက်ခဲ့ပါသည်၊ fzf ၏ shell integration ဖြင့် ချိန်ညှိထားပါက Ctrl-R သည် မူလပုံစံထက် မက ပိုမို စွမ်းအားထက်မြက်သည့် သင့် history တစ်ခုလုံးအပေါ် အပြန်အလှန်လုပ်ဆောင်နိုင်သော fuzzy search တစ်ခု ဖြစ်လာပါသည်။

သင့် dotfile များကို မည်သို့ စုစည်းသင့်သနည်း။ ၎င်းတို့သည် ကိုယ်ပိုင် folder တွင် ရှိသင့်ပြီး၊ version control အောက်တွင် ရှိကာ script ကို သုံး၍ နေရာတကျ symlink ပြုလုပ်ထားသင့်ပါသည်။ ဒါဟာ အောက်ပါ အကျိုးကျေးဇူးများ ရှိစေပါသည် -

သင့် dotfile တွေထဲမှာ မည်သည့်အရာတွေ ထည့်ရမလဲ။ အွန်လိုင်း Documentation များ သို့မဟုတ် man page များကို ဖတ်ရှုခြင်းဖြင့် သင့် tool ၏ setting များကို လေ့လာနိုင်ပါသည်။ အခြား နည်းလမ်းကောင်းတစ်ခုမှာ စာရေးသူများက ၎င်းတို့ နှစ်သက်သော စိတ်ကြိုက်ပြင်ဆင်မှုများအကြောင်း ပြောပြထားသည့် သီးသန့် ပရိုဂရမ်များဆိုင်ရာ blog post များကို အင်တာနက်တွင် ရှာဖွေခြင်း ဖြစ်ပါသည်။ စိတ်ကြိုက်ပြင်ဆင်မှုများအကြောင်း လေ့လာရန် အခြားနည်းလမ်းတစ်ခုမှာ အခြားသူများ၏ dotfile များကို လျှောက်ကြည့်ခြင်း ဖြစ်ပါသည် - GitHub တွင် dotfiles repository မြောက်မြားစွာကို ရှာဖွေတွေ့ရှိနိုင်ပါသည် — လူကြိုက်အများဆုံး တစ်ခုကို ဤနေရာတွင် ကြည့်ရှုနိုင်ပါသည် (သို့သော် configuration များကို မျက်စိမှိတ် မကူးယူရန် အကြံပြုပါသည်)။ ဤနေရာတွင် ဤအကြောင်းအရာနှင့် ပတ်သက်သည့် အခြား ကောင်းမွန်သော အရင်းအမြစ်တစ်ခု ရှိပါသည်။

သင်တန်းဆရာများ အားလုံး၏ dotfile များကို GitHub တွင် အများပြည်သူ ကြည့်ရှုနိုင်ရန် ဖွင့်လှစ်ပေးထားပါသည် - Anish, Jon, Jose။

Framework များနှင့် plugin များ သည်လည်း သင့် shell ကို ပိုမိုကောင်းမွန်စေနိုင်ပါသည်။ အသုံးများသော အထွေထွေ framework အချို့မှာ prezto သို့မဟုတ် oh-my-zsh တို့ဖြစ်ကြပြီး၊ သီးသန့် feature များကို အဓိကထားသည့် သေးငယ်သော plugin များမှာ -

fish ကဲ့သို့သော shell တွင် ဤ feature အများအပြားကို မူလအားဖြင့် ထည့်သွင်းပေးထားပါသည်။

ဤ feature များကို ရရှိရန် oh-my-zsh ကဲ့သို့သော ကြီးမားသည့် framework တစ်ခု မလိုအပ်ပါ။ တစ်သီးပုဂ္ဂလ plugin များကို install လုပ်ခြင်းသည် မကြာခဏ ပိုမိုမြန်ဆန်ပြီး ပိုမို ထိန်းချုပ်ခွင့် ပေးပါသည်။ ကြီးမားသော framework များသည် shell စတင်ချိန်ကို သိသာစွာ နှေးကွေးစေနိုင်သဖြင့် သင် အမှန်တကယ် သုံးစွဲသည့်အရာများကိုသာ install လုပ်ရန် စဉ်းစားပါ။

Shell အတွင်း AI အသုံးပြုခြင်း

Shell တွင် AI tools များကို ထည့်သွင်းအသုံးပြုရန် နည်းလမ်းများစွာ ရှိပါသည်။ အောက်တွင် မတူညီသော ပေါင်းစပ်မှု အဆင့်များအလိုက် ဥပမာအချို့ကို ဖော်ပြထားပါသည် -

Command ထုတ်လုပ်ပေးခြင်း - simonw/llm ကဲ့သို့သော tools များသည် သဘာဝဘာသာစကား ဖော်ပြချက်များမှတစ်ဆင့် shell command များကို ထုတ်လုပ်ပေးနိုင်ရန် ကူညီပေးနိုင်ပါသည်။

$ llm cmd "find all python files modified in the last week"
find . -name "*.py" -mtime -7

Pipeline တွင် ပေါင်းစပ်ခြင်း - LLM များကို ဒေတာများကို ပရိုဆက်လုပ်ရန်နှင့် ပြောင်းလဲရန် shell pipeline များအတွင်း ပေါင်းစပ်နိုင်ပါသည်။ ရိုးရိုး regex သုံးလျှင် ခက်ခဲမည့် ပုံစံမမှန်သော ဖော်မတ်များမှ အချက်အလက်များကို ထုတ်ယူရန် လိုအပ်သည့်အခါ ၎င်းတို့သည် အထူး အသုံးဝင်ပါသည် -

$ cat users.txt
Contact: john.doe@example.com
User 'alice_smith' logged in at 3pm
Posted by: @bob_jones on Twitter
Author: Jane Doe (jdoe)
Message from mike_wilson yesterday
Submitted by user: sarah.connor
$ INSTRUCTIONS="Extract just the username from each line, one per line, nothing else"
$ llm "$INSTRUCTIONS" < users.txt
john.doe
alice_smith
bob_jones
jdoe
mike_wilson
sarah.connor

Variable တွင် space (ကွက်လပ်) များ ပါဝင်နေသောကြောင့် "$INSTRUCTIONS" (quote ဖြင့်) ကို သုံးထားပုံနှင့် file ၏ အကြောင်းအရာကို stdin သို့ လမ်းကြောင်းပြောင်းရန် < users.txt ကို သုံးထားပုံကို သတိပြုပါ။

AI Shell များ - Claude Code ကဲ့သို့သော tools များသည် အင်္ဂလိပ်စာ command များကို လက်ခံပြီး ၎င်းတို့ကို shell လုပ်ဆောင်ချက်များ၊ file ပြင်ဆင်မှုများနှင့် ပိုမို ရှုပ်ထွေးသော အဆင့်များစွာပါဝင်သည့် အလုပ်များအဖြစ် ဘာသာပြန်ပေးသည့် meta-shell အဖြစ် လုပ်ဆောင်ပေးပါသည်။

Terminal Emulator များ

သင့် shell ကို စိတ်ကြိုက်ပြင်ဆင်ခြင်းနှင့်အတူ သင့်အနေဖြင့် terminal emulator ရွေးချယ်မှုနှင့် ၎င်း၏ setting များကို စူးစမ်းရန် အချိန်အနည်းငယ် ပေးသင့်ပါသည်။ Terminal emulator ဆိုသည်မှာ သင့် shell Run နေသည့် စာသားအခြေပြု interface ကို ပံ့ပိုးပေးသော GUI ပရိုဂရမ်တစ်ခု ဖြစ်ပါသည်။ ထွက်ရှိထားသော terminal emulator များစွာ ရှိကြပါသည်။

သင့် terminal တွင် နာရီပေါင်း ရာနှင့်ချီ၍ သို့မဟုတ် ထောင်နှင့်ချီ၍ အချိန်ကုန်လွန်ရဖွယ် ရှိသဖြင့် ၎င်း၏ setting များကို ဝင်ရောက်ကြည့်ရှုရန် ထိုက်တန်လှပါသည်။ သင့် terminal တွင် ပြုပြင်ပြောင်းလဲလိုနိုင်သည့် အချက်အချို့တွင် အောက်ပါတို့ ပါဝင်သည် -

လေ့ကျင့်ခန်းများ (Exercises)

Arguments များနှင့် Globs တွဲဖက်သုံးခြင်း

  1. သင့်အနေဖြင့် cmd --flag -- --notaflag ကဲ့သို့သော command များကို တွေ့ရနိုင်ပါသည်။ -- သည် ပရိုဂရမ်အား flag များကို parse လုပ်ခြင်း ရပ်တန့်ရန် ပြောဆိုသော အထူး argument ဖြစ်ပါသည်။ -- ၏ အနောက်ရှိ အရာအားလုံးကို positional argument အဖြစ် သတ်မှတ်ပါသည်။ ဒါဟာ အဘယ်ကြောင့် အသုံးဝင်နိုင်သနည်း။ touch -- -myfile ကို Run ကြည့်ပြီး -- မပါဘဲ ၎င်းကို ပြန်လည် ဖျက်ပစ်ရန် ကြိုးစားကြည့်ပါ။

  2. man ls ကို ဖတ်ရှုပြီး file များကို အောက်ပါ ပုံစံအတိုင်း စာရင်းထုတ်ပေးသည့် ls command တစ်ခု ရေးသားပါ -
    • ဝှက်ထားသော file များ ပါဝင်သော file အားလုံး ပါဝင်ရမည်
    • အရွယ်အစားများကို လူနားလည်လွယ်သော ပုံစံဖြင့် စာရင်းဖော်ပြရမည် (ဥပမာ 454279954 အစား 454M)
    • File များကို လတ်တဆတ်ဆုံး အစီအစဉ်အတိုင်း စီစဉ်ထားရမည်
    • Output ကို အရောင်ဆိုးထားရမည်

    နမူနာ output သည် အောက်ပါအတိုင်း ဖြစ်ပါလိမ့်မည် -

     -rw-r--r--   1 user group 1.1M Jan 14 09:53 baz
     drwxr-xr-x   5 user group  160 Jan 14 09:53 .
     -rw-r--r--   1 user group  514 Jan 14 06:42 bar
     -rw-r--r--   1 user group 106M Jan 13 12:12 foo
     drwx------+ 47 user group 1.5K Jan 12 18:08 ..
    
  3. Process substitution <(command) သည် command တစ်ခု၏ output ကို file တစ်ခုကဲ့သို့ အသုံးပြုခွင့် ပေးပါသည်။ printenv နှင့် export တို့၏ output များကို နှိုင်းယှဉ်ရန် process substitution နှင့်အတူ diff ကို အသုံးပြုပါ။ ၎င်းတို့ အဘယ်ကြောင့် မတူညီကြသနည်း။ (အကြံပြုချက် - diff <(printenv | sort) <(export | sort) ကို စမ်းကြည့်ပါ)။

Environment Variables များ

  1. အောက်ပါအတိုင်း လုပ်ဆောင်ပေးသော bash function marco နှင့် polo တို့ကို ရေးသားပါ - သင့်အနေဖြင့် marco ကို Run သည့်အခါတိုင်း လက်ရှိ working directory ကို တစ်နည်းနည်းဖြင့် သိမ်းဆည်းထားရမည်၊ ထို့နောက် မည်သည့် directory သို့ ရောက်ရှိနေပါစေ polo ကို Run လိုက်ပါက polo က သင့်အား marco ကို Run ခဲ့သည့် directory သို့ cd ဖြင့် ပြန်လည် ပို့ဆောင်ပေးရမည်။ Debug ပြုလုပ်ရန် လွယ်ကူစေရေးအတွက် code ကို marco.sh file တစ်ခုတွင် ရေးသားနိုင်ပြီး source marco.sh ကို Run ခြင်းဖြင့် သင့် shell သို့ သတ်မှတ်ချက်များကို (ပြန်လည်) Load ပြုလုပ်နိုင်ပါသည်။

Return Codes များ

  1. သင့်တွင် ခဲယဉ်းစွာ မအောင်မြင်တတ်သော command တစ်ခု ရှိသည် ဆိုပါစို့။ ၎င်းကို debug ပြုလုပ်ရန် output ကို ဖမ်းယူရန် လိုအပ်သော်လည်း မအောင်မြင်သော Run ကို ရရှိရန် အချိန်ကုန်နိုင်ပါသည်။ အောက်ပါ script ကို မအောင်မြင်မချင်း Run ပြီး ၎င်း၏ standard output နှင့် error stream များကို file များသို့ ဖမ်းယူကာ အဆုံးတွင် အရာအားလုံးကို ရိုက်နှိပ်ပေးသည့် bash script တစ်ခု ရေးသားပါ။ Script မအောင်မြင်မီ အရေအတွက် မည်မျှ Run ခဲ့ရသည်ကိုလည်း သတင်းပို့နိုင်ပါက Bonus အမှတ် ရရှိပါမည်။

     #!/usr/bin/env bash
    
     n=$(( RANDOM % 100 ))
    
     if [[ $n -eq 42 ]]; then
        echo "Something went wrong"
        >&2 echo "The error was using magic numbers"
        exit 1
     fi
    
     echo "Everything went according to plan"
    

Signal များနှင့် Job Control

  1. Terminal ထဲတွင် sleep 10000 job တစ်ခုကို စတင်ပါ၊ Ctrl-Z ဖြင့် background သို့ ပို့ပြီး bg ဖြင့် ၎င်း၏ လုပ်ဆောင်မှုကို ဆက်လက် ပြုလုပ်ပါ။ ယခုအခါ ၎င်း၏ pid ကို ရှာရန် pgrep ကို အသုံးပြုပြီး pid ကိုယ်တိုင် ရိုက်ထည့်ရန် မလိုဘဲ ၎င်းအား kill ပြုလုပ်ရန် pkill ကို အသုံးပြုပါ။ (အကြံပြုချက် - -lf flag များကို သုံးပါ)။

  2. အခြား process တစ်ခု မပြီးဆုံးမီ process တစ်ခုကို မစတင်ချင်ဟု ဆိုပါစို့။ မည်သို့ ပြုလုပ်မည်နည်း။ ဤလေ့ကျင့်ခန်းတွင် ကျွန်ုပ်တို့၏ ကန့်သတ်ချက် process သည် အမြဲတမ်း sleep 60 & ဖြစ်ပါမည်။ ဤအရာကို ရရှိရန် နည်းလမ်းတစ်ခုမှာ wait command ကို အသုံးပြုခြင်း ဖြစ်ပါသည်။ Sleep command ကို စတင်ပြီး background process မပြီးဆုံးမီအထိ ls တစ်ခုက စောင့်ဆိုင်းအောင် ပြုလုပ်ကြည့်ပါ။

    သို့သော် ဤနည်းဗျူဟာသည် မတူညီသော bash session တစ်ခုတွင် စတင်ပါက ပျက်ကွက်ပါလိမ့်မည်၊ အကြောင်းမှာ wait သည် child process များအတွက်သာ အလုပ်လုပ်သောကြောင့် ဖြစ်သည်။ မှတ်စုများတွင် ကျွန်ုပ်တို့ မဆွေးနွေးခဲ့သည့် feature တစ်ခုမှာ kill command ၏ exit status သည် အောင်မြင်ပါက သုညဖြစ်ပြီး သို့မဟုတ်ပါက သုညမဟုတ်ဘဲ ဖြစ်နေခြင်းပင် ဖြစ်သည်။ kill -0 သည် signal တစ်ခုကို မပို့သော်လည်း process မရှိပါက သုညမဟုတ်သော exit status ကို ပေးပါလိမ့်မည်။ Pid တစ်ခုကို ယူပြီး ပေးထားသော process မပြီးဆုံးမီအထိ စောင့်ဆိုင်းပေးသည့် pidwait ဟု ခေါ်သော bash function တစ်ခု ရေးသားပါ။ CPU ကို မလိုအပ်ဘဲ ဖြုန်းတီးခြင်းမှ ရှောင်ရှားရန် sleep ကို အသုံးပြုသင့်ပါသည်။

File များနှင့် Permission များ

  1. (အဆင့်မြင့်) Directory တစ်ခုအတွင်း လတ်တဆတ်ဆုံး ပြင်ဆင်ထားသော file ကို recursive နည်းဖြင့် ရှာဖွေရန် command သို့မဟုတ် script တစ်ခု ရေးသားပါ။ ပိုမို အထွေထွေကျစွာဖြင့် file အားလုံးကို လတ်တဆတ်ဆုံး အစီအစဉ်အတိုင်း စာရင်းထုတ်နိုင်ပါသလား။

Terminal Multiplexers

  1. ဤ tmux သင်ခန်းစာ ကို လိုက်နာပါ၊ ထို့နောက် ဤအဆင့်များ ကို လိုက်နာ၍ အခြေခံ စိတ်ကြိုက်ပြင်ဆင်မှုများ ပြုလုပ်နည်းကို လေ့လာပါ။

Alias များနှင့် Dotfiles

  1. စာလုံးမှား ရိုက်မိသည့်အခါ cd သို့ ရောက်ရှိသွားမည့် alias dc တစ်ခု ဖန်တီးပါ။

  2. သင့်အသုံးအများဆုံး ထိပ်တန်း command ၁၀ ခုကို ရရှိရန် history | awk '{$1="";print substr($0,2)}' | sort | uniq -c | sort -n | tail -n 10 ကို Run ပြီး ၎င်းတို့အတွက် ပိုမိုတိုတောင်းသော alias များ ရေးသားရန် စဉ်းစားပါ။ မှတ်ချက် - ဤအရာသည် Bash အတွက် အလုပ်လုပ်ပါသည်၊ အကယ်၍ ZSH ကို သုံးနေပါက history အစား history 1 ကို သုံးပါ။

  3. သင့် dotfile များအတွက် folder တစ်ခု ဖန်တီးပြီး version control ထူထောင်ပါ။

  4. စိတ်ကြိုက်ပြင်ဆင်မှု အချို့ ပါရှိသော အနည်းဆုံး ပရိုဂရမ်တစ်ခု (ဥပမာ သင့် shell) အတွက် configuration တစ်ခု ထည့်သွင်းပါ (စတင်ရန်အတွက် $PS1 သတ်မှတ်ခြင်းဖြင့် သင့် shell prompt ကို စိတ်ကြိုက်ပြင်ခြင်းကဲ့သို့ ရိုးရှင်းသော အရာတစ်ခု ဖြစ်နိုင်ပါသည်)။

  5. စက်အသစ်တွင် သင့် dotfile များကို လျှင်မြန်စွာ (နှင့် ကိုယ်တိုင် အားထုတ်စရာမလိုဘဲ) install လုပ်မည့် နည်းလမ်းတစ်ခု ထူထောင်ပါ။ ဒါဟာ file တစ်ခုစီအတွက် ln -s ကို ခေါ်ယူသည့် ရိုးရှင်းသော shell script တစ်ခု ဖြစ်နိုင်သည်၊ သို့မဟုတ် သီးသန့် utility တစ်ခုကို သုံးနိုင်ပါသည်။

  6. သန့်ရှင်းသော virtual machine အသစ်တစ်ခုတွင် သင့် installation script ကို စမ်းသပ်ပါ။

  7. သင့် လက်ရှိ tool configuration အားလုံးကို သင့် dotfiles repository ထံ သို့ ပြောင်းရွှေ့ပါ။

  8. သင့် dotfile များကို GitHub တွင် ထုတ်ဝေပါ။

Remote Machine များ (SSH)

ဤလေ့ကျင့်ခန်းများအတွက် Linux virtual machine တစ်ခု ထည့်သွင်းပါ (သို့မဟုတ် ရှိနှင့်ပြီးသား တစ်ခုကို သုံးပါ)။ အကယ်၍ သင့်အနေဖြင့် virtual machine များနှင့် မရင်းနှီးပါက တစ်ခု ထည့်သွင်းရန်အတွက် ဤ သင်ခန်းစာကို ကြည့်ရှုပါ။

  1. ~/.ssh/ သို့ သွားပြီး သင့်တွင် SSH key တွဲတစ်ခု ရှိမရှိ စစ်ဆေးပါ။ မရှိပါက ssh-keygen -a 100 -t ed25519 ဖြင့် ထုတ်လုပ်ပါ။ Password အသုံးပြုရန်နှင့် ssh-agent ကို သုံးရန် အကြံပြုပါသည်၊ အချက်အလက် ထပ်မံကြည့်ရှုရန် ဤနေရာတွင် ကြည့်ပါ။

  2. အောက်ပါအတိုင်း ပါဝင်အောင် .ssh/config ကို ပြင်ဆင်ပါ -

     Host vm
         User username_goes_here
         HostName ip_goes_here
         IdentityFile ~/.ssh/id_ed25519
         LocalForward 9999 localhost:8888
    
  3. Server ထံသို့ သင့် ssh key ကူးယူရန် ssh-copy-id vm ကို သုံးပါ။

  4. သင့် VM တွင် python -m http.server 8888 ကို Run ၍ webserver တစ်ခု စတင်ပါ။ သင့်စက်ရှိ http://localhost:9999 သို့ ဝင်ရောက်၍ VM webserver ကို ကြည့်ရှုပါ။

  5. sudo vim /etc/ssh/sshd_config ပြုလုပ်၍ သင့် SSH server config ကို ပြင်ဆင်ပြီး PasswordAuthentication တန်ဖိုးကို ပြင်ဆင်၍ password authentication ကို ပိတ်ထားပါ။ PermitRootLogin တန်ဖိုးကို ပြင်ဆင်၍ root login ကို ပိတ်ထားပါ။ sudo service sshd restart ဖြင့် ssh service ကို restart ပြုလုပ်ပါ။ SSH ပြန်ဝင်ကြည့်ပါ။

  6. (စိန်ခေါ်မှု) VM တွင် mosh ကို install လုပ်ပြီး ချိတ်ဆက်မှု ထူထောင်ပါ။ ထို့နောက် server/VM ၏ network adapter ကို ဖြုတ်လိုက်ပါ။ Mosh က ၎င်းမှ မှန်ကန်စွာ ပြန်လည် သက်သာလာနိုင်ပါသလား။

  7. (စိန်ခေါ်မှု) ssh တွင် -N နှင့် -f flag များ အဘယ်သို့ ပြုလုပ်သည်ကို ရှာဖွေပြီး background port forwarding ကို ရရှိရန် command တစ်ခုကို စိစစ်ဖော်ထုတ်ပါ။


Edit this page.

Licensed under CC BY-NC-SA.