Bash сценарийін жасаудың ең жақсы тәжірибелері: сенімді және өнімділік Bash сценарийлеріне арналған жылдам нұсқаулық

Bash сценарийін жасаудың ең жақсы тәжірибелері: сенімді және өнімділік Bash сценарийлеріне арналған жылдам нұсқаулық
Қабық тұсқағаздары манапи

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

Бұл сияқты нәрсені біріктіруге тұрарлық 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

пікір қалдыру