Командын мөрийн орчин

Өмнөх лекцээр үзсэнчлэн, ихэнх shell-үүд нь зөвхөн бусад програмыг ажиллуулагч төдийгүй практикт тэдгээр нь түгээмэл хэв маяг, абстрактуудыг агуулсан бүрэн хэмжээний програмчлалын хэлийг санал болгодог. Гэсэн хэдий ч бусад ихэнх програмчлалын хэлнүүдээс ялгаатай нь shell скрипт бичихэд бүх зүйл програмуудыг ажиллуулах болон тэдгээрийг хооронд нь хялбар бөгөөд үр дүнтэй харилцуулахад чиглэгдсэн байдаг.

Ялангуяа, shell скрипт нь дүрмүүд-тэй (conventions) маш нягт холбоотой байдаг. Командын мөрийн интерфейстэй (CLI) програм нь shell-ийн орчинд саадгүй ажиллахын тулд дагаж мөрдөх ёстой зарим нийтлэг загварууд бий. Бид одоо командын мөрийн програмууд хэрхэн ажилладаг талаарх ойлголтууд болон тэдгээрийг хэрхэн ашиглах, тохируулах түгээмэл дүрмүүдийг үзэх болно.

Командын мөрийн интерфейс

Ихэнх програмчлалын хэл дээр функц бичихэд дараах байдалтай харагддаг:

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

Эндээс бид програмын оролт болон гаралтыг тодорхой харж болно. Харин үүний эсрэгээр shell скриптүүд эхлээд харахад огт өөр харагдаж болно.

#!/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

Ийм скриптүүдэд юу болж байгааг зөв ойлгохын тулд эхлээд shell програмууд хоорондоо эсвэл shell орчинтой харилцах үед байнга гарч ирдэг хэд хэдэн ойлголтыг танилцуулах шаардлагатай:

Аргументууд

Shell програмууд ажиллах үедээ аргументуудын жагсаалтыг хүлээн авдаг. Аргументууд нь shell-д ердийн тэмдэгт мөр хэлбэрээр дамжих ба тэдгээрийг хэрхэн тайлбарлах нь тухайн програмаас хамаарна. Жишээлбэл, бид ls -l folder/ гэж ажиллуулах үед /bin/ls програмыг ['-l', 'folder/'] гэсэн аргументуудтай ажиллуулж байгаа хэрэг юм.

Shell скрипт дотроос бид эдгээрийг shell-ийн тусгай синтакс ашиглан авч ашигладаг. Эхний аргументыг авахын тулд $1, хоёр дахь аргументыг авахын тулд $2 гэх мэтээр $9 хүртэл ашиглана. Бүх аргументыг жагсаалт байдлаар авахын тулд $@, харин аргументын тоог авахын тулд $# ашигладаг. Үүнээс гадна ажиллуулж буй програмын нэрийг $0 хувьсагчаар дамжуулан авч болно.

Ихэнх програмын хувьд аргументууд нь flags (тугууд) болон ердийн тэмдэгт мөрүүдийн холимог байдаг. Flags-ийг нэг зураас (-) эсвэл давхар зураас (--)-аар эхэлсэн байдлаар нь ялгаж болно. Flags нь ихэвчлэн сонголтоор ашиглагддаг бөгөөд тэдгээрийн үүрэг нь програмын ажиллагааг өөрчлөх явдал юм. Жишээлбэл, ls -l нь ls командын гаралтыг хэрхэн форматлахыг өөрчилдөг.

Та --all гэх мэт урт нэртэй давхар зураастай flags болон ихэвчлэн ганц үсгээс бүрдэх -a гэх мэт нэг зураастай flags-ийг харах болно. Ижил тохиргоог хоёр форматаар зааж өгч болно, ls -a болон ls --all нь ижил утгатай. Нэг зураастай flags-ийг ихэвчлэн нэгтгэж бичдэг тул ls -l -a болон ls -la нь мөн ижил утгатай. Flags-ийн дараалал нь мөн гаралтын үр дүнд нөлөөлөхгүй, ls -la болон ls -al нь ижил үр дүн үзүүлнэ. Зарим flags нь маш түгээмэл ашиглагддаг бөгөөд та shell орчинтой илүү танилцах тусам тэдгээрийг шууд ашиглах болно (жишээ нь, --help, --verbose, --version).

Flags нь shell-ийн дүрмүүдийн (conventions) маш сайн жишээ юм. Shell хэл нь програмаа заавал - эсвэл -- ашиглахыг шаарддаггүй. Таныг myprogram +myoption myfile гэсэн синтакс ашиглан програм бичихэд юу ч саад болохгүй боловч энэ нь бусад хүмүүсийг төөрөгдүүлэх болно, учир нь хүмүүс зураас ашиглахыг хүлээж байдаг. Практикт ихэнх програмчлалын хэлнүүд CLI flags задлан шинжлэх бэлэн сангуудыг (жишээ нь, Python дээрх argparse) санал болгодог.

CLI програмуудын өөр нэг нийтлэг дүрэм бол програмууд нь ижил төрлийн олон тооны аргумент хүлээн авдаг явдал юм. Ийм байдлаар аргумент өгөхөд команд нь тэдгээрийн файл/хавтас бүр дээр ижил үйлдлийг гүйцэтгэдэг.

mkdir src
mkdir docs
# нь дараахтай ижил утгатай
mkdir src docs

Энэхүү хялбар синтакс нь эхлээд харахад шаардлагагүй мэт санагдаж болох ч globbing-той хамтран ашиглах үед маш хүчирхэг болж ирдэг. Globbing эсвэл globs нь shell програмыг дуудахаас өмнө өргөжүүлэн хувиргадаг тусгай хэв маяг юм.

Бид одоогийн хавтас доторх бүх .py файлуудыг рекурсив бус байдлаар устгахыг хүссэн гэж бодъё. Өмнөх лекцээр сурсан зүйлээр бол бид үүнийг дараах байдлаар хийж болно:

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

Гэвч бид үүнийг ердөө rm *.py гэж орлуулж болно!

Бид терминал дээр rm *.py гэж бичих үед shell /bin/rm програмыг ['*.py'] гэсэн аргументтай ажиллуулдаггүй. Үүний оронд shell одоогийн хавтаснаас *.py загварт таарч буй файлуудыг хайдаг бөгөөд эндэх * нь ямар ч төрлийн тэмдэгтээс бүрдсэн хоосон буюу түүнээс олон тэмдэгт мөрийг илэрхийлнэ. Тиймээс хэрэв манай хавтас дотор main.py болон utils.py байгаа бол rm програм ['main.py', 'utils.py'] гэсэн аргументуудыг хүлээн авах болно.

Хамгийн түгээмэл хэрэглэгддэг globs нь орлуулах тэмдэгт * (юу ч байж болох хоосон эсвэл олон тэмдэгт), ? (яг нэг дурын тэмдэгт) болон хаалт {} юм. Хаалт {} нь таслалаар тусгаарлагдсан загваруудын жагсаалтыг олон аргумент болгон задлан өргөжүүлдэг.

Практикт globs-ийг тодорхой жишээнүүдээр ойлгох нь илүү хялбар байдаг.

touch folder/{a,b,c}.py
# нь дараах байдлаар өргөжнө
touch folder/a.py folder/b.py folder/c.py

convert image.{png,jpg}
# нь дараах байдлаар өргөжнө
convert image.png image.jpg

cp /path/to/project/{setup,build,deploy}.sh /newpath
# нь дараах байдлаар өргөжнө
cp /path/to/project/setup.sh /path/to/project/build.sh /path/to/project/deploy.sh /newpath

# Globbing аргуудыг хослуулж болно
mv *{.py,.sh} folder
# Бүх *.py болон *.sh файлуудыг зөөнө

Зарим shell-үүд (жишээ нь, zsh) рекурсив замыг багтаасан ** зэрэг илүү нарийн globbing-ийг дэмждэг. Тиймээс rm **/*.py нь бүх .py файлуудыг рекурсив байдлаар устгах болно.

Урсгалууд

Бид дараах байдалтай програмуудын дарааллыг ажиллуулах үед:

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

grep програм нь cat болон uniq програмуудын аль алинтай нь харилцаж байгааг харж болно.

Энд анхаарах чухал зүйл бол бүх гурван програм нэгэн зэрэг ажиллаж байгаа явдал юм. Өөрөөр хэлбэл, shell эхлээд cat-ыг ажиллуулаад, дараа нь grep-ийг, дараа нь uniq-ийг ажиллуулдаггүй. Үүний оронд бүх гурван процесс нэгэн зэрэг үүсч, shell нь cat-ын гаралтыг grep-ийн оролттой, grep-ийн гаралтыг uniq-ийн оролттой холбодог. Пайп (|) операторыг ашиглах үед shell нь дарааллын дагуу нэг програмаас дараагийн програм руу урсах өгөгдлийн урсгал дээр ажилладаг.

Бид энэхүү зэрэгцээ ажиллагааг дараах байдлаар баталж болно, дамжуулах хоолой дахь бүх тушаалууд шууд эхэлдэг:

$ (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-аас бусад бүх процессууд шууд ажиллаж байгааг харж байна. Shell нь аль нэг процесс дуусахаас өмнө бүх процессуудыг үүсгэж, тэдгээрийн урсгалуудыг холбодог. cat нь зөвхөн sleep дууссаны дараа эхлэх бөгөөд cat-ын гаралт grep руу дамжих гэх мэтээр үргэлжилнэ.

Програм бүр standard input буюу stdin гэж нэрлэгддэг оролтын урсгалтай байдаг. Pipe хийх үед stdin автоматаар холбогддог. Скрипт дотор олон програмууд stdin-ээс уншихыг илэрхийлэхийн тулд файлын нэрний оронд - тэмдэгтийг хүлээн авдаг:

# Өгөгдөл пайпоор ирэх үед эдгээр нь ижил утгатай
echo "hello" | grep "hello"
echo "hello" | grep "hello" -

Үүнтэй адилаар програм бүр standard output буюу stdout, мөн standard error буюу stderr гэсэн хоёр гаралтын урсгалтай байдаг. stdout нь хамгийн түгээмэл хэрэглэгддэг урсгал бөгөөд програмын гаралтыг дараагийн команд руу pipe хийхэд ашиглагддаг. stderr нь програмын анхааруулга болон бусад алдааны мэдээллүүдийг хэвлэхэд зориулагдсан урсгал бөгөөд ингэснээр уг гаралт дараагийн команд руу дамжин боловсруулагдахаас сэргийлдэг.

$ 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-ийг пайп хийгээгүй
$ ls /nonexistent 2>/dev/null
# Гаралт байхгүй - stderr-ийг /dev/null руу чиглүүлсэн

Shell нь эдгээр урсгалуудыг чиглүүлэх (redirecting) синтаксийг санал болгодог. Жишээнүүд:

# stdout-ийг файл руу чиглүүлэх (дахин бичих)
echo "hello" > output.txt

# stdout-ийг файл руу чиглүүлэх (файл руу нэмж бичих)
echo "world" >> output.txt

# stderr-ийг файл руу чиглүүлэх
ls foobar 2> errors.txt

# stdout болон stderr-ийг хоёуланг нь ижил файл руу чиглүүлэх
ls foobar &> all_output.txt

# stdin-ийг файлаас унших
grep "pattern" < input.txt

# Гаралтыг /dev/null руу чиглүүлж хаях
cmd > /dev/null 2>&1

Unix философийг илтгэх өөр нэг хүчирхэг хэрэгсэл бол fzf fuzzy finder юм. Энэ нь stdin-ээс мөрүүдийг уншиж, шүүх болон сонгох боломжтой интерактив интерфейсийг үзүүлдэг:

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

fzf нь shell-ийн олон үйлдлүүдтэй нэгдэн ажиллах боломжтой. Shell-ийг өөрчлөх хэсэгт бид үүнийг хэрхэн ашиглах талаар дэлгэрэнгүй үзэх болно.

Орчны хувьсагчид

Bash-д хувьсагч оноохдоо foo=bar гэсэн синтакс ашиглах ба хувьсагчийн утгыг авахдаа $foo гэж бичнэ. foo = bar гэж бичих нь буруу синтакс бөгөөд shell үүнийг foo програмыг ['=', 'bar'] аргументуудтай дуудаж байна гэж задлан шинжлэх болно. Shell скрипт дээр хоосон зайны үүрэг нь аргументуудыг салгах явдал байдаг. Энэ ажиллагаа нь эхэндээ гэнэтийн алдаа дагуулж болох тул үүнийг анхаарна уу.

Shell хувьсагчид төрөл гэж байхгүй бөгөөд бүгд тэмдэгт мөр байдаг. Shell дээр тэмдэгт мөр бичихэд дан хашилт болон давхар хашилтын тэмдэгтүүд өөр өөр үүрэгтэй гэдгийг санаарай. '-оор хашсан тэмдэгт мөр нь хувьсагчийг задлахгүй, команд орлуулахгүй, мөн тусгай тэмдэгтүүдийг хөрвүүлэхгүйгээр шууд утгыг хадгалдаг бол "-оор хашсан тэмдэгт мөр нь эдгээрийг ажиллуулж хувиргадаг.

foo=bar
echo "$foo"
# bar гэж хэвлэнэ
echo '$foo'
# $foo гэж хэвлэнэ

Командын гаралтыг хувьсагчид хадгалахын тулд бид command substitution-ыг ашигладаг. Бид дараах кодыг ажиллуулахад:

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

ls-ийн гаралт (тодруулбал stdout) $files хувьсагчид хадгалагдах бөгөөд бид үүнийг дараа нь ашиглаж болно. $files хувьсагчийн агуулга нь ls-ийн гаралтын шинэ мөр шилжүүлэх тэмдэгтүүдийг хадгалдаг тул grep зэрэг програмууд мөр бүр дээр бие даан ажиллах боломжтой байдаг.

Үүнтэй төстэй, арай бага дурдагддаг өөр нэг боломж бол process substitution бөгөөд <( CMD ) нь CMD тушаалыг ажиллуулж, гаралтыг нь түр зуурын файлд хадгалаад уг түр файлын нэрийг <()-ийн оронд орлуулан тавьдаг. Энэ нь програмууд утгыг STDIN-ээр биш харин файлаар дамжуулан авахыг хүлээж байгаа үед маш хэрэгтэй байдаг. Жишээлбэл, diff <(ls src) <(ls docs) нь src болон docs хавтас доторх файлуудын ялгааг харуулах болно.

Shell-ээс өөр нэг програмыг ажиллуулах бүрд тэрээр environment variables (орчны хувьсагчид) гэж нэрлэгддэг хувьсагчдын багцыг дамжуулдаг. Бид shell дээр printenv тушаалыг ажиллуулан одоогийн environment variables-ийг харах боломжтой. Environment variable-ийг тодорхой програмд зориулан дамжуулахын тулд тушаалын өмнө хувьсагч оноох үйлдлийг бичнэ:

Орчны хувьсагчдыг ТОМ ҮСГЭЭР (жишээ нь, HOME, PATH, DEBUG) бичих журамтай. Энэ нь техникийн шаардлага биш боловч локал хувьсагчдаас (ихэвчлэн жижиг үсгээр бичигддэг) ялгахад тусалдаг.

TZ=Asia/Tokyo date  # Токиогийн одоогийн цагийг хэвлэнэ
echo $TZ  # TZ нь зөвхөн хүү тушаалд зориулан оноогдсон тул энэ нь хоосон байна

Үүний оронд бид одоогийн орчныг өөрчлөх export суурилагдсан функцийг ашиглаж болох бөгөөд ингэснээр дараах бүх хүү процессууд уг хувьсагчийг өвлөн авах болно:

export DEBUG=1
# Энэ мөчөөс эхлэн бүх програмуудын орчинд DEBUG=1 байх болно
bash -c 'echo $DEBUG'
# 1 гэж хэвлэнэ

Хувьсагчийг устгахын тулд unset суурилагдсан тушаалыг ашиглана, жишээ нь unset DEBUG.

Орчны хувьсагчид нь shell-ийн өөр нэгэн дүрэм юм. Тэдгээрийг олон програмын ажиллагааг шууд бусаар өөрчлөхөд ашиглаж болно. Жишээлбэл, shell нь одоогийн хэрэглэгчийн home хавтасны замыг $HOME орчны хувьсагчид хадгалдаг. Ингэснээр програмууд --home /home/alice гэж зааж өгөхийг шаардалгүйгээр энэ хувьсагчаас мэдээллийг авч чаддаг. Өөр нэг жишээ бол олон програмууд огноо, цагийг форматыг тодорхойлоход ашигладаг $TZ хувьсагч юм.

Гаралтын кодууд

Бидний өмнө үзсэнчлэн, shell програмын үндсэн гаралт нь stdout/stderr урсгалууд болон файлын систем дэх өөрчлөлтүүдээр илэрхийлэгддэг.

Анхны тохиргоогоор shell скрипт нь гаралтын код (exit code) тэг утгыг буцаадаг. Дүрмийн дагуу тэг гаралтын код нь бүх зүйл амжилттай болсныг илтгэдэг бол тэгээс өөр утга нь алдаа гарсан болохыг илэрхийлнэ. Тэгээс өөр код буцаахын тулд бид shell-ийн exit NUM суурилагдсан тушаалыг ашиглах ёстой. Хамгийн сүүлд ажилласан тушаалын гаралтын кодыг бид тусгай $? хувьсагчаар дамжуулан авч болно.

Shell-д AND болон OR үйлдлүүдийг гүйцэтгэх && болон || логик операторууд байдаг. Ердийн програмчлалын хэлнээс ялгаатай нь shell дэх эдгээр операторууд нь програмуудын гаралтын кодууд дээр ажилладаг. Тэд хоёулаа богино холболтын (short-circuiting) операторууд юм. Энэ нь өмнөх командын амжилттай (код 0) эсвэл амжилтгүй (тэгээс өөр код) болсон дээр үндэслэн дараагийн командуудыг нөхцөлтэйгөөр ажиллуулахад ашиглагдаж болно гэсэн үг юм. Жишээнүүд:

# grep амжилттай болсон тохиолдолд л (загварыг олбол) echo ажиллана
grep -q "pattern" file.txt && echo "Pattern found"

# grep алдаа заасан тохиолдолд л (загвар олдоогүй) echo ажиллана
grep -q "pattern" file.txt || echo "Pattern not found"

# true нь үргэлж амжилттай ажилладаг програм юм
true && echo "This will always print"

# харин false нь үргэлж алдаа заадаг програм юм
false || echo "This will always print"

Үүнтэй ижил зарчим if болон while нөхцөлүүдэд хамаарна, тэд хоёулаа гаралтын кодуудыг ашиглан шийдвэр гаргадаг:

# if нь нөхцөлт командын гаралтын кодыг ашигладаг (0 = үнэн, тэгээс өөр = худал)
if grep -q "pattern" file.txt; then
    echo "Found"
fi

# while давталт нь команд 0 код буцааж байгаа хугацаанд үргэлжилнэ
while read line; do
    echo "$line"
done < file.txt

Сигналууд

Зарим тохиолдолд та ажиллаж байгаа програмыг зогсоох шаардлагатай болдог, жишээлбэл тушаал хэтэрхий удаан ажиллах үед. Програмыг зогсоох хамгийн энгийн арга бол Ctrl-C дарах бөгөөд үүний дараа тушаал зогсох болно. Гэхдээ энэ нь цаанаа хэрхэн ажилладаг, яагаад заримдаа процессыг зогсоож чаддаггүй вэ?

$ sleep 100
^C
$

Энд ^C нь терминал дээр Ctrl-C дарах үед харагдах тэмдэглэгээ юм.

Цаанаа дараах үйл явц өрнөсөн:

  1. Бид Ctrl-C дарсан
  2. Shell энэхүү тусгай тэмдэгтүүдийг таньсан
  3. Shell процесс sleep процесс руу SIGINT сигналыг илгээсэн
  4. Сигнал нь sleep процессын ажиллагааг зогсоосон

Сигналууд (Signals) нь тусгай харилцааны механизм юм. Процесс сигнал хүлээн авах үедээ ажиллагаагаа зогсоож, сигналыг боловсруулаад, сигналын өгсөн мэдээлэлд үндэслэн ажиллагааны урсгалаа өөрчлөх боломжтой байдаг. Ийм учраас сигналуудыг програм хангамжийн тасалдал (software interrupts) гэж нэрлэдэг.

Бидний хувьг Ctrl-C дарах нь shell-ийг процесс руу SIGINT сигналыг илгээхэд хүргэдэг. SIGINT-ийг барьж аваад үл тоомсорлодог, ажиллагаагаа зогсоодоггүй Python програмын жижиг жишээг доор харуулав. Энэ програмыг зогсоохын тулд бид Ctrl-\ дарж SIGQUIT сигналыг илгээх хэрэгтэй.

#!/usr/bin/env python
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 илгээхэд юу болохыг доор харуулав. ^ нь терминал дээр 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 нь терминалтай холбоотой хүсэлтүүдэд ашиглагддаг бол процессыг аюулгүй, зөв зогсоох ерөнхий сигнал бол SIGTERM юм. Энэ сигналыг илгээхийн тулд бид kill командыг kill -TERM <PID> синтакс ашиглан дуудаж болно.

Сигнал нь процессыг устгахаас гадна өөр зүйлсийг хийж чадна. Жишээлбэл, SIGSTOP нь процессыг түр зогсооно. Терминал дээр Ctrl-Z дарах нь shell-ийг SIGTSTP (Terminal Stop - терминалын SIGSTOP хувилбар) сигнал илгээхэд хүргэдэг.

Бид түр зогссон ажлыг fg эсвэл bg тушаалуудаар харгалзан урд талд (foreground) эсвэл ард (background) үргэлжлүүлэн ажиллуулж болно.

jobs команд нь одоогийн терминалын сесстэй холбоотой дуусаагүй байгаа ажлуудыг жагсаадаг. Та эдгээр ажлуудыг pid-ээр нь зааж өгч болно (pid-ийг олохын тулд pgrep-ийг ашиглана). Мөн процессыг хувийн тэмдэгт % болон ажлын дугаараар (jobs командын харуулдаг дугаар) зааж өгөх боломжтой. Хамгийн сүүлд ард ажиллуулсан ажлыг тусгай $! параметрээр авна.

Командын төгсгөлд & залгаж бичвэл командыг шууд ард ажиллуулж, prompt-ийг танд буцааж өгөх болно. Гэхдээ энэ нь shell-ийн STDOUT-ийг ашигласаар байх тул гаралт хэвлэгдээд төвөгтэй байж болно (үүнийг шийдэхийн тулд гаралтыг чиглүүлнэ үү). Үүнтэй ижил үр дүнд хүрэхийн тулд ажиллаж байгаа програмыг Ctrl-Z дарж зогсоогоод дараа нь bg гэж ажиллуулж болно.

Ард ажиллаж байгаа процессууд нь таны терминалын хүү процессууд хэвээр байх бөгөөд хэрэв та терминалыг хаавал тэд устгагдах болно (терминал хаагдахад тэдэнд SIGHUP сигнал очих болно). Үүнээс сэргийлэхийн тулд програмыг nohup (SIGHUP сигналыг үл тоомсорлогч wrapper) ашиглан ажиллуулах эсвэл ажиллаж эхэлсэн процессыг disown тушаалаар салгаж болно. Эсвэл дараагийн хэсэгт үзэх терминал үржүүлэгчийг ашиглаж болно.

Доорх жишээнд эдгээр ойлголтуудыг харуулав.

$ 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 нь SIGHUP-ээс хамгаална

$ jobs
[2]  + running    nohup sleep 2000

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

SIGKILL нь тусгай сигнал бөгөөд үүнийг процесс барьж авч чадахгүй тул процессыг шууд зогсоодог. Гэхдээ энэ нь системд өнчин хүү процессуудыг үлдээх зэрэг сөрөг үр дагавартай байж болно.

Та эдгээр болон бусад сигналуудын талаар эндээс эсвэл man signal, kill -l тушаалаар дэлгэрүүлж үзнэ үү.

Shell скрипт дотор сигнал хүлээн авах үед тушаалуудыг ажиллуулахын тулд trap суурилагдсан тушаалыг ашиглаж болно. Энэ нь цэвэрлэгээ хийх үйлдлүүдэд маш хэрэгтэй байдаг:

#!/usr/bin/env bash
cleanup() {
    echo "Cleaning up temporary files..."
    rm -f /tmp/mytemp.*
}
trap cleanup EXIT  # Скрипт дуусах үед цэвэрлэгээг ажиллуулах
trap cleanup SIGINT SIGTERM  # Мөн Ctrl-C болон kill хийх үед ажиллуулах

Алсын серверүүд

Өнөө үед програмчид өдөр тутмын ажилдаа алсын серверүүдтэй ажиллах нь улам бүр түгээмэл болж байна. Үүний тулд хамгийн түгээмэл ашиглагддаг хэрэгсэл бол SSH (Secure Shell) бөгөөд энэ нь алсын сервертэй холбогдож танил shell интерфейсийг олгодог. Бид сервертэй дараах тушаалаар холбогдоно:

ssh alice@server.mit.edu

Энд бид server.mit.edu сервер рүү alice хэрэглэгчээр нэвтрэхийг оролдож байна.

ssh-ийн ихэвчлэн анзаарагддаггүй нэг давуу тал бол тушаалуудыг интерактив бус байдлаар ажиллуулах боломж юм. ssh нь тушаалын stdin-ийг дамжуулж, stdout-ийг хүлээн авах үйлдлийг зөв гүйцэтгэдэг тул үүнийг бусад тушаалуудтай хослуулах боломжтой:

# Энд ls нь алсын сервер дээр, харин wc нь локал дээр ажиллана
ssh alice@server ls | wc -l

# Энд ls болон wc хоёулаа алсын сервер дээр ажиллана
ssh alice@server 'ls | wc -l'

Сүлжээ тасрах, унтах горимд шилжих, сүлжээ солигдох болон сүлжээний хоцролт ихтэй үед найдвартай ажилладаг SSH-ийг орлох Mosh хэрэгслийг суулгаж ашиглахыг зөвлөж байна.

ssh нь алсын сервер дээр тушаал ажиллуулахын тулд таныг зөвшөөрөлтэй хэрээгч мөн болохыг нотлохыг шаарддаг. Үүнийг нууц үг эсвэл ssh түлхүүр ашиглан хийж болно. Түлхүүрт суурилсан баталгаажуулалт нь нийтийн түлхүүрийн криптограф ашиглан нууц хувийн түлхүүрийг задлахгүйгээр серверт өөрийгөө таниулдаг. Түлхүүрт суурилсан баталгаажуулалт нь илүү хялбар бөгөөд илүү аюулгүй тул үүнийг сонгох нь зүйтэй. Хувийн түлхүүр (ихэвчлэн ~/.ssh/id_rsa эсвэл сүүлийн үед ~/.ssh/id_ed25519) нь таны нууц үгтэй ижил үүрэгтэй тул түүнийг маш сайн нууцалж, хэзээ ч бусадтай хуваалцаж болохгүй.

Түлхүүрийн хослолыг үүсгэхийн тулд ssh-keygen ажиллуулна.

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

Хэрэв та GitHub руу SSH түлхүүр ашиглан ханддаг бол энд дурдсан алхмуудыг аль хэдийн хийсэн бөгөөд хүчинтэй түлхүүрийн хослолтой байгаа байх. Түлхүүрийн нууц үгийг шалгаж, баталгаажуулахын тулд ssh-keygen -y -f /path/to/key тушаалыг ажиллуулж болно.

Сервер тал дээр ssh нь аль үйлчлүүлэгчийг нэвтрүүлэхээ шийдэхийн тулд .ssh/authorized_keys файлыг шалгадаг. Өөрийн нийтийн түлхүүрийг сервер рүү хуулахын тулд:

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

# эсвэл илүү хялбар (хэрэв ssh-copy-id боломжтой бол)

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

Тушаал ажиллуулахаас гадна ssh-ийн үүсгэсэн холболтыг ашиглан файлыг сервер рүү болон серверээс найдвартай дамжуулж болно. scp нь хамгийн уламжлалт хэрэгсэл бөгөөд синтакс нь scp path/to/local_file remote_host:path/to/remote_file байна. rsync нь локал болон алсын сервер дээрх ижил файлуудыг таньж, дахин хуулахаас сэргийлдэг давуу талтай тул scp-ээс илүү сайн байдаг. Энэ нь мөн симлинкүүд, зөвшөөрлийг удирдах болон өмнө тасарсан хуулах үйлдлийг үргэлжлүүлэх --partial туг зэрэг нэмэлт боломжуудыг олгодог. rsync нь scp-тэй ижил синтакс ашигладаг.

SSH үйлчлүүлэгчийн тохиргоо ~/.ssh/config файлд байрлах бөгөөд энд бид серверүүд болон тэдгээрийн анхны тохиргоог тодорхойлж болно. Энэхүү тохиргооны файлыг зөвхөн ssh бус, мөн scp, rsync, mosh зэрэг бусад програмууд уншиж ашигладаг.

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

# Тохиргоонд мөн глоб ашиглаж болно
Host *.mit.edu
    User alice

Терминал үржүүлэгчид

Командын мөрийн интерфейсийг ашиглах үед та нэгэн зэрэг олон зүйл ажиллуулах шаардлагатай болдог. Жишээлбэл, код засварлагч болон програмаа зэрэгцүүлж харахыг хүсэж болно. Үүнийг олон терминалын цонх нээж хийж болох ч terminal multiplexer ашиглах нь илүү тохиромжтой шийдэл юм.

tmux зэрэг terminal multiplexers нь терминалын цонхыг хэсгүүд (panes) болон таб (windows) болгон хувааж ашиглах боломжийг олгосноор олон shell сессүүдтэй үр дүнтэй ажиллах нөхцөлийг бүрдүүлдэг. Түүнчлэн, terminal multiplexer нь одоогийн терминалын сессийг detach хийгээд, дараа нь reattach хийх боломжийг олгодог. Үүнээс шалтгаалан terminal multiplexers нь алсын сервертэй ажиллах үед nohup зэрэг аргуудыг ашиглах шаардлагагүй болгодог тул маш тохиромжтой байдаг.

Өнөө үед хамгийн алдартай terminal multiplexer бол tmux юм. tmux нь тохируулах өндөр боломжтой бөгөөд холбогдох товчны хослолуудыг ашиглан олон таб, хэсгүүдийг үүсгэж тэдгээрийн хооронд хурдан шилжиж болно.

tmux-ийг ашиглахын тулд түүний товчны хослолуудыг мэдэх шаардлагатай бөгөөд тэдгээр нь бүгд <C-b> x хэлбэртэй байна: (1) Ctrl+b дарж, (2) Ctrl+b-ийг суллаад, (3) x товчлуурыг дарна. tmux нь дараах шатлал бүхий объектуудтай:

tmux-ийн талаар илүү iихийг мэдэхийн тулд энэ богино заавар болон энэ дэлгэрэнгүй тайлбарыг уншина уу.

tmux болон SSH ашиглаж сурсны дараа та өөрийн ажиллах орчныг дурын машин дээр өөрийнхөөрөө байлгахыг хүсэх болно. Энд shell тохиргоо (shell customization) хэрэг болно.

Бүрхүүлийг өөртөө тааруулан өөрчлөх

Олон тооны командын мөрийн програмууд нь dotfiles гэж нэрлэгддэг plain-text файлуудаар тохируулагддаг (файлын нэр нь .-оор эхэлдэг тул, жишээ нь ~/.vimrc, ийм файлууд ls хийхэд харагддаггүй нуугдмал байдаг).

Dotfiles нь shell-ийн өөр нэгэн дүрэм юм. Эхэнд байгаа цэг нь тэдгээрийг жагсаалтад харуулахгүй нуух зорилготой байдаг.

Shell-үүд нь ийм файлуудаар тохируулагддаг програмуудын нэг жишээ юм. Ажиллаж эхлэхдээ shell нь тохиргоогоо ачаалахын тулд олон файл уншдаг. Shell-ийн төрөл болон нэвтрэх сесс (login session) эсвэл интерактив сессээс хамааран энэ үйл явц нь нэлээд нарийн төвөгтэй байж болно. Сэдвийн талаарх маш сайн нөөцийг эндээс үзнэ үү.

bash-ийн хувьд .bashrc эсвэл .bash_profile файлыг засварлах нь ихэнх системд тохирдог. Dotfiles ашиглан тохируулж болох зарим хэрэгслүүд:

Түгээмэл хийгддэг тохиргооны өөрчлөлт бол shell програм хайх шинэ байршлуудыг нэмэх явдал юм. Програм хангамж суулгах үед та энэхүү загвартай таарах болно:

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

Энд бид shell-д $PATH хувьсагчийн утгыг одоогийн утга дээр шинэ замыг нэмж оноож, бүх хүү процессууд PATH-ийн энэхүү шинэ утгыг өвлөн авахыг зааж байна. Энэ нь хүү процессуудад path/to/append хавтсан доор байрлах програмуудыг олох боломжийг олгоно.

Shell-ээ өөрчлөх нь ихэвчлэн шинэ командын мөрийн хэрэгслүүдийг суулгахыг шаарддаг. Багц удирдагчид (package managers) үүнийг хялбар болгодог. Тэд програм татах, суулгах, шинэчлэх үйлдлүүдийг хариуцдаг. Үйлдлийн систем бүр өөр өөрийн багц удирдагчтай байдаг: macOS нь Homebrew, Ubuntu/Debian нь apt, Fedora нь dnf, харин Arch нь pacman ашигладаг. Бид багц удирдагчдын талаар кодын тээвэрлэлт лекцээр дэлгэрэнгүй үзэх болно.

macOS дээр Homebrew ашиглан хоёр хэрэгтэй програмыг хэрхэн суулгахыг доор харуулав:

# ripgrep: илүү сайн тохиргоотой, илүү хурдан grep
brew install ripgrep

# fd: ашиглахад хялбар бөгөөд илүү хурдан find
brew install fd

Эдгээрийг суулгаснаар та grep-ийн оронд rg, харин find-ын оронд fd тушаалыг ашиглах боломжтой болно.

curl | bash анхааруулга: Та curl -fsSL https://example.com/install.sh | bash гэх мэт суулгах зааврыг олноор харах болно. Энэ арга нь скриптийг татаж аваад шууд ажиллуулдаг тул маш хялбар боловч эрсдэлтэй; та шалгаагүй кодоо шууд ажиллуулж байна гэсэн үг юм. Илүү аюулгүй арга бол эхлээд татаж аваад, кодыг шалгаад дараа нь ажиллуулах явдал юм:

curl -fsSL https://example.com/install.sh -o install.sh
less install.sh  # кодыг шалгах
bash install.sh

Зарим суулгацууд арай аюулгүй хувилбарыг ашигладаг: /bin/bash -c "$(curl -fsSL https://url)" нь дор хаяж одоогийн shell-ээс биш харин bash-аар скриптийг ажиллуулахыг баталгаажуулдаг.

Суулгаагүй командыг ажиллуулахыг оролдох үед shell command not found алдааг заах болно. command-not-found.com вэбсайт нь дурын командыг хайж олон, түүнийг үйлдлийн систем болон багц удирдагч бүр дээр хэрхэн суулгах зааврыг харахад маш хэрэгтэй нөөц юм.

Өөр нэгэн хэрэгтэй хэрэгсэл бол маш товч бөгөөд жишээн дээр суурилсан man хуудсуудыг харуулдаг tldr юм. Маш урт баримт бичгийг уншихын оронд та түүний түгээмэл хэрэглэгддэг хэлбэрүүдийг хурдан харах боломжтой:

$ 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

Заримдаа танд шинэ програм хэрэггүй бөгөөд зөвхөн бэлэн байгаа командуудыг тодорхой flags-тай ашиглах товчлол хэрэгтэй байдаг. Энд алиасууд (aliases) хэрэг болно.

Бид shell-ийн суурилагдсан alias тушаалыг ашиглан өөрсдийн командын товчлолыг үүсгэж болно. Shell alias нь өөр командын богино хэлбэр бөгөөд shell тушаалыг ажиллуулахаас өмнө автоматаар солих болно. Жишээлбэл, bash дээрх alias нь дараах бүтэцтэй байна:

alias alias_name="command_to_alias arg1 arg2"

Тэнцүүгийн тэмдэг = орчимд сул зай байж болохгүй, учир нь alias нь ганц аргумент авдаг shell команд юм.

Алиасууд нь маш олон давуу талтай:

# Түгээмэл flags-д зориулсан товчлол үүсгэх
alias ll="ls -lh"

# Байнга бичдэг урт командуудыг хурдан бичих
alias gs="git status"
alias gc="git commit"

# Буруу бичихээс сэргийлэх
alias sl=ls

# Програмуудыг илүү найдвартай тохиргоотой болгох
alias mv="mv -i"           # -i нь файлыг дахин бичихээс өмнө асууна
alias mkdir="mkdir -p"     # -p нь эцэг хавтас байхгүй бол үүсгэнэ
alias df="df -h"           # -h нь хэмжээг хүнд уншихад хялбар форматоор харуулна

# Алиасуудыг хослуулж болно
alias la="ls -A"
alias lla="la -l"

# Алиасыг үл тоомсорлон жинхэнэ тушаалыг ажиллуулахдаа өмнө нь \ тавина
\ls
# Эсвэл unalias ашиглан алиасыг бүрмөсөн идэвхгүй болгоно
unalias la

# Алиасын тодорхойлолтыг харахын тулд alias-ийг дуудна
alias ll
# ll='ls -lh' гэж хэвлэнэ

Алиасууд нь хязгаарлалттай: тэд тушаалын дундуур аргумент авч чаддаггүй. Илүү нарийн төвөгтэй үйлдлүүдэд та үүний оронд shell функцуудыг (shell functions) ашиглах хэрэгтэй.

Ихэнх shell-үүд түүхээс ухрааж хайх Ctrl-R товчны хослолыг дэмждэг. Ctrl-R дараад өмнө ажиллуулсан командынхаа хэсгийг бичиж хайна уу. Өмнө нь бид fzf-ийг fuzzy finder байдлаар танилцуулсан; fzf-ийн shell-тэй нэгдсэн тохиргоог хийснээр Ctrl-R нь таны түүхээс интерактив fuzzy хайлтыг хийдэг болох бөгөөд энэ нь анхны тохиргооноос хамаагүй хүчирхэг юм.

Өөрийн dotfiles-ийг хэрхэн зохион байгуулах вэ? Тэдгээрийг өөрийн гэсэн хавтаст хийж, хувилбарын хяналтад оруулан, скрипт ашиглан symlink (тэмдэгт холбоос) хийх нь зүйтэй. Энэ нь дараах давуу талуудтай:

Өөрийн dotfiles-д юу оруулах вэ? Та тохиргооны заавруудыг онлайн баримт бичиг эсвэл man хуудаснаас уншиж сурах боломжтой. Мөн өөр нэг сайн арга бол бусад хүмүүсийн dotfiles-ийг судлах явдал юм: та GitHub дээрээс маш олон dotfiles repositories олох боломжтой бөгөөд хамгийн алдартайг нь эндээс үзэж болно (гэхдээ тохиргоог сохроор хуулж болохгүй). Энд өөр нэгэн сайн нөөц бий.

Энэхүү хичээлийн багш нарын dotfiles нээлттэй байдаг: Anish, Jon, Jose.

Фрэймворк болон залгаасууд (frameworks and plugins) нь мөн таны shell-ийг сайжруулахад туслах болно. Түгээмэл хэрэглэгддэг фрэймворкууд гэвэл prezto эсвэл oh-my-zsh байдаг бол тодорхой боломжид чиглэсэн жижиг залгаасууд ч бий:

fish зэрэг shell-үүд эдгээр боломжуудыг анхнаасаа агуулсан байдаг.

Эдгээр боломжуудыг авахын тулд oh-my-zsh шиг том фрэймворк заавал шаардлагагүй. Тусдаа залгаасуудыг суулгах нь илүү хурдан бөгөөд танд илүү их хяналтыг олгоно. Том фрэймворкууд нь shell-ийн ачааллах хугацааг мэдэгдэхүйц удаашруулдаг тул зөвхөн ашигладаг зүйлсээ суулгахыг анхаарна уу.

Бүрхүүл дэх хиймэл оюун ухаан (AI)

Бүрхүүлд хиймэл оюун ухааны (AI) хэрэгслийг ашиглах олон арга бий. Дараах хэдэн жишээг харуулъя:

Тушаал үүсгэх: simonw/llm зэрэг хэрэгслүүд нь байгалийн хэлээр бичсэн тайлбараас shell тушаал үүсгэхэд тусална:

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

Pipeline integration: LLM загваруудыг өгөгдөл боловсруулах болон хөрвүүлэхэд shell pipes-тай нэгтгэж болно. Тогтмол бус форматаас мэдээллийг салгахад 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

Бид хувьсагч дотор хоосон зай байгаа тул "$INSTRUCTIONS" (хашилттай) ашиглаж байгааг болон файлын агуулгыг stdin рүү чиглүүлэхийн тулд < users.txt-ийг ашиглаж байгааг анхаарна уу.

AI shell-үүд: Claude Code зэрэг хэрэгслүүд нь англи хэлээр өгсөн тушаалыг shell-ийн үйлдэл, файл засварлах болон олон алхамт нарийн төвөгтэй ажлууд болгон хөрвүүлдэг мета-бүрхүүлийн (meta-shell) үүргийг гүйцэтгэдэг.

Терминал эмуляторууд

Shell-ээ тохируулахаас гадна өөрийн ашиглах terminal emulator (терминал эмулятор) болон түүний тохиргоог сонгоход хэсэг хугацаа зарцуулах нь зүйтэй. Terminal emulator нь таны shell ажилладаг текстэн интерфейсийг олгодог GUI програм юм. Маш олон terminal emulators байдаг.

Терминал дээрээ олон зуун, магадгүй олон мянган цагийг зарцуулах тул түүний тохиргоог анхаарах нь үр өгөөжтэй байдаг. Та терминал дээрээ дараах зүйлсийг өөрчилж болно:

Дасгал ажил

Энэхүү хичээлийн бүх лекцүүд нь цуврал дасгал ажлуудтай ирдэг. Зарим нь тодорхой даалгавар өгөх бол зарим нь “X болон Y програмыг ашиглаад үзээрэй” гэх мэт нээлттэй байдаг. Бид таныг эдгээрийг туршиж үзэхийг зөвлөж байна.

Бид дасгал ажлын хариуг бичээгүй болно. Хэрэв танд хүндрэл гарвал Discord дээрх #missing-semester-forum сувагт асуух эсвэл хийсэн зүйлээ тайлбарлаж бидэнд имэйл илгээгээрэй, бид туслахыг хичээх болно. Эдгээр дасгалууд нь LLM-тэй хийх хэлэлцүүлэгт ч сайн сэдэв болох болно. Дасгал ажлын гол үнэ цэнэ нь хариултыг олох явцад суралцах бөгөөд зөвхөн хамгийн дөхөм замыг хайхаас илүү асуулт тавьж, гүнзгийрүүлэн судлахыг зөвлөж байна.

  1. Та заримдаа cmd --flag -- --notaflag гэх мэт тушаалуудыг харах болно. -- нь програмд тугуудыг задлан шинжлэхийг зогсоохыг заадаг тусгай аргумент юм. ---ийн дараа байгаа бүх зүйлийг байрлалын аргумент (positional argument) гэж үзнэ. Энэ нь яагаад хэрэгтэй байж болох вэ? touch -- -myfile гэж ажиллуулан файл үүсгээд, дараа нь -- ашиглахгүйгээр устгахыг оролдоно уу.

  2. man ls хуудсыг уншаад дараах байдлаар файлуудыг жагсаах ls тушаал бичнэ үү:
    • Нууц файлыг оролцуулан бүх файлыг харуулна
    • Хэмжээг хүнд уншихад хялбар форматоор харуулна (жишээ нь 454279954-ийн оронд 454M)
    • Файлуудыг сүүлд өөрчлөгдсөн хугацаагаар нь эрэмбэлнэ
    • Гаралт нь өнгөтэй байна

    Жишээ гаралт иймэрхүү харагдана:

     -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) <( CMD ) нь тушаалын гаралтыг файл шиг ашиглах боломжийг олгодог. Процесс орлуулалттай diff ашиглан printenv болон export-ийн гаралтыг харьцуулна уу. Тэд юугаараа ялгаатай вэ? (Сэжүүр: diff <(printenv | sort) <(export | sort) ажиллуулна уу).

  4. Дараах үйлдлийг гүйцэтгэх bash функцууд болох marco болон polo-г бичнэ үү: таныг marco ажиллуулах бүрд одоогийн ажиллаж буй хавтас хадгалагдах бөгөөд таныг ямар ч хавтас дотор байсан polo ажиллуулахад cd хийж таныг marco-г ажиллуулсан хавтас руу буцаах болно. Алдааг хялбар засахын тулд кодыг marco.sh файлд бичиж, source marco.sh ажиллуулан өөрийн shell-д ачаалж туршина уу.

  5. Танд хааяа нэг алдаа заадаг тушаал байна гэж бодъё. Алдааг илрүүлэхийн тулд гаралтыг хадгалах шаардлагатай боловч алдаа заах хүртэл гараар ажиллуулах нь цаг их үрнэ. Дараах скриптийг алдаа заах хүртэл нь ажиллуулах бөгөөд түүний стандарт гаралт болон алдааны урсгалуудыг файлд хадгалж, төгсгөлд нь бүх гаралтыг хэвлэдэг bash скрипт бичнэ үү. Хэрэв скрипт алдаа заах хүртэл нийт хэдэн удаа ажилласныг хэвлэж чадвал бонус оноо болно.

     #!/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"
    
  6. Терминал дээр sleep 10000 ажлыг эхлүүлээд, Ctrl-Z дарж түр зогсоон bg тушаалаар ард ажиллуулна уу. Одоо pgrep ашиглан түүний pid-ийг олж, pkill ашиглан pid-ийг гараар бичихгүйгээр устгана уу. (Сэжүүр: -af аргументуудыг ашиглана уу).

  7. Та нэг процессыг дуустал өөр процессыг ажиллуулахгүй хүлээхийг хүсэж байна гэж бодъё. Үүнийг хэрхэн шийдэх вэ? Энэ дасгалд хүлээх процесс маань үргэлж sleep 60 & байна. Үүнд хүрэх нэг арга бол wait командыг ашиглах явдал юм. Sleep командыг ажиллуулаад, ls командыг арын процесс дуустал хүлээдэг болгон туршина уу.

    Гэвч, хэрэв бид өөр багц сессээс эхлүүлбэл энэ арга ажиллахгүй, учир нь wait нь зөвхөн хүү процессууд дээр ажилладаг. Тэмдэглэлд дурдаагүй нэг зүйл бол kill командын гаралтын төлөв амжилттай бол 0, үгүй бол тэгээс өөр утга буцаадаг явдал юм. kill -0 нь сигнал илгээдэггүй боловч процесс байхгүй тохиолдолд тэгээс өөр код буцаана. PID авдаг бөгөөд уг процесс дуусах хүртэл хүлээдэг pidwait нэртэй bash функц бичнэ үү. CPU-ийг дэмий үрэхээс сэргийлэхийн тулд sleep ашиглах хэрэгтэй.

  8. (Гүнзгийрүүлсэн) Хавтас дотроос хамгийн сүүлд өөрчлөгдсөн 파일을 рекурсив байдлаар олдог тушаал эсвэл скрипт бичнэ үү. Илүү ерөнхий байдлаар, бүх файлыг өөрчлөгдсөн хугацаагаар нь жагсааж чадах уу?

  9. Энэхүү tmux зааврыг дагаж суралцаад, дараа нь эдгээр алхмуудын дагуу үндсэн тохиргоонуудыг хийж сурна уу.

  10. Командыг буруу бичих үед зориулан cd рүү хөрвөх dc алиасыг (alias) үүсгэнэ үү.

  11. history | awk '{$1="";print substr($0,2)}' | sort | uniq -c | sort -n | tail -n 10 ажиллуулан өөрийн хамгийн их ашигласан 10 командыг олж, тэдгээрт зориулсан богино алиас бичих талаар бодно уу. Тэмдэглэл: энэ нь Bash дээр ажиллана; хэрэв та ZSH ашиглаж байгаа бол зүгээр history биш history 1 ашиглана уу.

  12. Өөрийн dotfiles-д зориулж хавтас үүсгэн хувилбарын хяналтад (Git) оруулна уу.

  13. Ядаж нэг програмын тохиргоог (эхлэхдээ бүрхүүлийн prompt-ийг өөрчлөх $PS1 тохиргоо байж болно) өөрийн dotfiles-д нэмнэ үү.

  14. Өөрийн dotfiles-ыг шинэ машин дээр хурдан (мөн механик ажиллагаагүй) суулгах аргыг бэлтгэнэ үү. Энэ нь файл бүрд ln -s хийдэг энгийн скрипт эсвэл тусгай хэрэгсэл ашигласан байж болно.

  15. Суулгах скриптээ шинэ виртуал машин дээр туршиж үзнэ үү.

  16. Өөрийн одоогийн ашиглаж буй хэрэгслүүдийн бүх тохиргоог dotfiles сан руугаа шилжүүлнэ үү.

  17. Өөрийн dotfiles-оо GitHub дээр нийтэлнэ үү.

  18. ~/.ssh/ хавтас руу орж SSH түлхүүр байгаа эсэхийг шалгана уу. Хэрэв байхгүй бол ssh-keygen -a 100 -t ed25519 ашиглан үүсгэнэ үү. Нууц үг ашиглах болон ssh-agent ашиглахыг зөвлөж байна, дэлгэрэнгүй мэдээлэл энд бий.

  19. .ssh/config файлыг дараах байдлаар засварлана уу:

     Host vm
         User username_goes_here
         HostName ip_goes_here
         IdentityFile ~/.ssh/id_ed25519
         LocalForward 9999 localhost:8888
    
  20. ssh-copy-id vm ажиллуулан өөрийн ssh нийтийн түлхүүрийг сервер рүү хуулна уу.

  21. Өөрийн VM дотор python -m http.server 8888 ажиллуулан вэб сервер эхлүүлнэ үү. Өөрийн локал машин дээр хөтчөөрөө http://localhost:9999 хаягаар орон VM-ийн вэб сервер рүү хандана уу.

  22. Өөрийн SSH серверийн тохиргоог sudo vim /etc/ssh/sshd_config тушаалаар нээж PasswordAuthentication утгыг өөрчлөн нууц үгээр нэвтрэхийг хаана уу. Мөн PermitRootLogin утгыг өөрчлөн root хэрэглэгчээр нэвтрэхийг хаана уу. SSH үйлчилгээг sudo service sshd restart ажиллуулан дахин ачаалж, дахин нэвтэрч туршина уу.

  23. (Асуулт) VM дотор mosh-ийг суулгаж холболт үүсгэнэ үү. Дараа нь серверийн сүлжээний холболтыг салгана уу. Mosh холболтыг буцааж сэргээж чадаж байна уу?

  24. (Асуулт) ssh командын -N болон -f тугуудын үүргийг судалж, арын горимд портыг чиглүүлэх тушаалыг олно уу.

Энэ хуудсыг засварлах.

Лиценз: CC BY-NC-SA.