Bash скриптийн шилдэг туршлагууд: Найдвартай, гүйцэтгэлтэй Bash скриптүүдийн хурдан гарын авлага

Bash скриптийн шилдэг туршлагууд: Найдвартай, гүйцэтгэлтэй Bash скриптүүдийн хурдан гарын авлага
Shell ханын цаасыг manapi

Bash скриптүүдийг дибаг хийх нь ялангуяа бүтэц, бүртгэл, найдвартай байдлын асуудлыг цаг тухайд нь авч үзэхгүйгээр одоо байгаа кодын санд шинэ нэмэлтүүд гарч ирэх үед өвсөн дотроос зүү хайхтай адил юм. Та өөрийн алдаанаас болж эсвэл нарийн төвөгтэй скриптүүдийг удирдахдаа ийм нөхцөл байдалд орж болно.

баг Mail.ru үүлэн шийдэл скриптүүдээ илүү сайн бичих, дибаг хийх, хадгалахад туслах зөвлөмж бүхий нийтлэлийг орчуулав. Итгэнэ үү, үгүй ​​юу, хэзээ ч ажилладаг цэвэрхэн, хэрэглэхэд бэлэн bash код бичихийн сэтгэл ханамжийг юу ч гүйцэхгүй.

Нийтлэлд зохиолч сүүлийн хэдэн жилийн хугацаанд сурсан зүйлээ хуваалцаж, түүнийг сэрэмжлүүлсэн нийтлэг алдаануудаа хуваалцжээ. Програм хангамж хөгжүүлэгч бүр карьерынхаа аль нэг үед ердийн ажлын даалгавруудыг автоматжуулахын тулд скриптүүдтэй ажилладаг тул энэ нь чухал юм.

Хавх зохицуулагчид

Миний тулгарч байсан ихэнх bash скриптүүд скриптийг гүйцэтгэх явцад гэнэтийн зүйл тохиолдоход үр дүнтэй цэвэрлэх механизмыг хэзээ ч ашигладаггүй.

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

function handle_exit() {
  // Add cleanup code here
  // for eg. rm -f "/tmp/${lock_file}.lock"
  // exit with an appropriate status code
}
  
// trap <HANDLER_FXN> <LIST OF SIGNALS TO TRAP>
trap handle_exit 0 SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM

trap нь ямар нэгэн дохио гарсан тохиолдолд дуудагдах цэвэрлэх функцийг бүртгэх бүрхүүлд суулгасан тушаал юм. Гэсэн хэдий ч, гэх мэт зохицуулагчдад онцгой анхаарал хандуулах хэрэгтэй SIGINT, энэ нь скриптийг зогсооход хүргэдэг.

Үүнээс гадна ихэнх тохиолдолд та зөвхөн барих хэрэгтэй EXIT, гэхдээ гол санаа нь та дохио тус бүрийн хувьд скриптийн зан төлөвийг өөрчлөх боломжтой юм.

Баригдсан багц функцууд - алдаа гарсан тохиолдолд хурдан дуусгах

Алдаа гарсан даруйд нь хариу үйлдэл үзүүлж, гүйцэтгэлийг хурдан зогсоох нь маш чухал юм. Ийм тушаалыг үргэлжлүүлэн ажиллуулахаас илүү муу зүйл гэж юу байхав:

rm -rf ${directory_name}/*

хувьсагч гэдгийг анхаарна уу directory_name тодорхойгүй байна.

Ийм нөхцөл байдлыг зохицуулахын тулд суулгасан функцуудыг ашиглах нь чухал юм set, жишээ нь set -o errexit, set -o pipefail буюу set -o nounset скриптийн эхэнд. Эдгээр функцууд нь таны скрипт ямар нэгэн тэгээс өөр гарах код, тодорхойгүй хувьсагч ашиглах, хоолойгоор дамжуулсан хүчингүй тушаал гэх мэттэй тулгарах үед шууд гарах болно.

#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

function print_var() {
  echo "${var_value}"
}

print_var

$ ./sample.sh
./sample.sh: line 8: var_value: unbound variable

Тайлбар: зэрэг суурилуулсан функцууд set -o errexit, "түүхий" буцах код (тэгээс бусад) байвал скриптээс гарах болно. Тиймээс, алдаатай ажиллах горимыг нэвтрүүлэх нь илүү дээр юм, жишээлбэл:

#!/bin/bash
error_exit() {
  line=$1
  shift 1
  echo "ERROR: non zero return code from line: $line -- $@"
  exit 1
}
a=0
let a++ || error_exit "$LINENO" "let operation returned non 0 code"
echo "you will never see me"
# run it, now we have useful debugging output
$ bash foo.sh
ERROR: non zero return code from line: 9 -- let operation returned non 0 code

Ийм маягаар скрипт бичих нь таныг скрипт дэх бүх командын үйл ажиллагаанд илүү болгоомжтой хандаж, алдаа гарах магадлалыг таныг гайхшруулахаас өмнө урьдчилан таамаглахад хүргэдэг.

Хөгжлийн явцад гарсан алдааг илрүүлэхийн тулд ShellCheck

гэх мэт зүйлийг нэгтгэх нь зүйтэй юм Бүрхүүл шалгах Өөрийнхөө bash кодыг шилдэг туршлагын дагуу шалгахын тулд хөгжүүлэлт, туршилтын шугам руу оруулна уу.

Би үүнийг өөрийн орон нутгийн хөгжлийн орчинд ашигладаг бөгөөд синтакс, семантик болон кодыг боловсруулах явцад орхигдсон байж болзошгүй зарим алдааны талаар тайлан авдаг. Энэ бол таны bash скриптүүдийн статик шинжилгээний хэрэгсэл бөгөөд үүнийг ашиглахыг зөвлөж байна.

Өөрийн гарах кодыг ашиглах

POSIX дахь буцаах кодууд нь зөвхөн тэг эсвэл нэг биш, харин тэг эсвэл тэгээс өөр утга юм. Төрөл бүрийн алдааны тохиолдлуудад захиалгат алдааны кодыг (201-254 хооронд) буцаахын тулд эдгээр функцийг ашиглана уу.

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

#!/usr/bin/env bash

SUCCESS=0
FILE_NOT_FOUND=240
DOWNLOAD_FAILED=241

function read_file() {
  if ${file_not_found}; then
    return ${FILE_NOT_FOUND}
  fi
}

Тайлбар: Орчны хувьсагчдыг санамсаргүйгээр дарж бичихээс зайлсхийхийн тулд тодорхойлсон хувьсагчийн нэрэндээ онцгой болгоомжтой хандана уу.

Бүртгэлийн функцууд

Сайхан, бүтэцтэй бүртгэл нь таны скриптийн үр дүнг хялбархан ойлгоход чухал юм. Бусад өндөр түвшний програмчлалын хэлнүүдийн нэгэн адил би bash скриптүүддээ төрөлх бүртгэлийн функцуудыг үргэлж ашигладаг. __msg_info, __msg_error гэх мэт.

Энэ нь зөвхөн нэг газарт өөрчлөлт оруулах замаар стандартчилсан бүртгэлийн бүтцийг бий болгоход тусалдаг:

#!/usr/bin/env bash

function __msg_error() {
    [[ "${ERROR}" == "1" ]] && echo -e "[ERROR]: $*"
}

function __msg_debug() {
    [[ "${DEBUG}" == "1" ]] && echo -e "[DEBUG]: $*"
}

function __msg_info() {
    [[ "${INFO}" == "1" ]] && echo -e "[INFO]: $*"
}

__msg_error "File could not be found. Cannot proceed"

__msg_debug "Starting script execution with 276MB of available RAM"

Би ихэвчлэн скрипт дээрээ ямар нэгэн механизмтай байхыг хичээдэг __init, ийм логгер хувьсагч болон бусад системийн хувьсагчдыг эхлүүлсэн эсвэл анхдагч утгуудад тохируулсан байдаг. Эдгээр хувьсагчийг мөн скрипт дуудах үед тушаалын мөрийн сонголтуудаас тохируулж болно.

Жишээ нь:

$ ./run-script.sh --debug

Ийм скриптийг ажиллуулах үед энэ нь шаардлагатай бол системийн хэмжээнд тохиргоог анхдагч утгаар нь тохируулах эсвэл шаардлагатай бол хамгийн багадаа тохирох зүйлийг эхлүүлэх боломжийг олгодог.

Би юуг эхлүүлэх, юуг хийхгүй байх сонголтыг ихэвчлэн хэрэглэгчийн интерфэйс болон хэрэглэгчийн судалж болох/шийдэх ёстой тохиргооны нарийвчилсан мэдээлэлд үндэслэн хийдэг.

Дахин ашиглах, цэвэр системийн төлөв байдалд зориулсан архитектур

Модульчлагдсан/дахин ашиглах боломжтой код

├── framework
│   ├── common
│   │   ├── loggers.sh
│   │   ├── mail_reports.sh
│   │   └── slack_reports.sh
│   └── daily_database_operation.sh

Би хөгжүүлэхийг хүсч буй шинэ төсөл/bash скриптийг эхлүүлэхэд ашиглаж болох тусдаа репозиторыг хадгалдаг. Дахин ашиглах боломжтой бүх зүйлийг хадгалах санд хадгалж, энэ функцийг ашиглахыг хүссэн бусад төслүүдээс олж авах боломжтой. Төслүүдийг ингэж зохион байгуулснаар бусад скриптүүдийн хэмжээг эрс багасгаж, кодын бааз нь жижиг, шалгахад хялбар байх болно.

Дээрх жишээн дээрх шиг бүх бүртгэлийн функцууд __msg_info, __msg_error болон бусад, тухайлбал Slack тайлангуудыг тусад нь оруулсан болно common/* гэх мэт бусад хувилбаруудад динамикаар холбогдоно daily_database_operation.sh.

Цэвэр системийг ардаа орхи

Хэрэв та скрипт ажиллаж байх үед ямар нэгэн эх сурвалжийг ачаалж байгаа бол эдгээр бүх өгөгдлийг санамсаргүй нэрээр хуваалцсан директорт хадгалахыг зөвлөж байна, жишээлбэл. /tmp/AlRhYbD97/*. Та лавлах нэрийг сонгохдоо санамсаргүй текст үүсгэгчийг ашиглаж болно:

rand_dir_name="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"

Ажил дууссаны дараа эдгээр сангуудыг дээр дурдсан дэгээ зохицуулагчаар цэвэрлэж болно. Хэрэв түр зуурын лавлахуудыг анхаарч үзэхгүй бол тэдгээр нь хуримтлагдаж, зарим үе шатанд хост дээр бүрэн диск гэх мэт гэнэтийн асуудал үүсгэдэг.

Түгжих файлуудыг ашиглах

Ихэнхдээ та ямар ч үед скриптийн зөвхөн нэг жишээ нь хост дээр ажиллаж байгаа эсэхийг шалгах хэрэгтэй. Үүнийг түгжих файлуудыг ашиглан хийж болно.

Би ихэвчлэн түгжээтэй файлуудыг үүсгэдэг /tmp/project_name/*.lock мөн скриптийн эхэнд байгаа эсэхийг шалгана уу. Энэ нь скриптийг аятайхан дуусгаж, зэрэгцээ ажиллаж байгаа өөр скриптээр системийн төлөвт гэнэтийн өөрчлөлт гарахаас зайлсхийхэд тусалдаг. Хэрэв танд ижил скриптийг тухайн хост дээр зэрэгцүүлэн ажиллуулах шаардлагатай бол файлуудыг түгжих шаардлагагүй.

Хэмжих, сайжруулах

Бид ихэвчлэн өгөгдлийн сангийн өдөр тутмын үйл ажиллагаа гэх мэт урт хугацаанд ажилладаг скриптүүдтэй ажиллах шаардлагатай болдог. Ийм үйлдлүүд нь ихэвчлэн дараалсан алхмуудыг багтаадаг: өгөгдлийг ачаалах, хэвийн бус байдлыг шалгах, өгөгдөл импортлох, төлөв байдлын тайланг илгээх гэх мэт.

Ийм тохиолдолд би скриптийг тусдаа жижиг скрипт болгон хувааж, тэдгээрийн төлөв байдал, гүйцэтгэлийн хугацааг дараах байдлаар мэдээлэхийг үргэлж хичээдэг.

time source "${filepath}" "${args}">> "${LOG_DIR}/RUN_LOG" 2>&1

Дараа нь би гүйцэтгэлийн хугацааг дараах байдлаар харж болно:

tac "${LOG_DIR}/RUN_LOG.txt" | grep -m1 "real"

Энэ нь оновчлол хийх шаардлагатай скриптүүдийн асуудал/удаан хэсгийг тодорхойлоход тусалдаг.

Амжилт хүсье!

Өөр юу унших вэ:

  1. Go болон GPU кэшүүд.
  2. Mail.ru Cloud Solutions-ийн S3 объектын сан дахь вэб дэгээ дээр суурилсан үйл явдалд суурилсан програмын жишээ.
  3. Дижитал өөрчлөлтийн тухай манай телеграм суваг.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх