Bash skriptlarining eng yaxshi amaliyotlari: ishonchli va samarali Bash skriptlari bo'yicha tezkor qo'llanma

Bash skriptlarining eng yaxshi amaliyotlari: ishonchli va samarali Bash skriptlari bo'yicha tezkor qo'llanma
Manapi tomonidan qobiq fon rasmi

Bash skriptlarini disk raskadrovka qilish, ayniqsa, yangi qo'shimchalar mavjud kodlar bazasida tuzilish, jurnallar va ishonchlilik masalalarini o'z vaqtida ko'rib chiqmasdan paydo bo'lganda, pichan ichida igna izlashga o'xshaydi. Siz o'zingizning xatolaringiz tufayli yoki murakkab skriptlar to'plamini boshqarishda bunday vaziyatlarga duch kelishingiz mumkin.

komanda Mail.ru bulutli echimlar skriptlaringizni yaxshiroq yozish, disk raskadrovka qilish va saqlashga yordam beradigan tavsiyalar bilan maqolani tarjima qildi. Xoh ishoning, xoh ishonmang, har safar ishlaydigan toza, foydalanishga tayyor bash kodini yozishdan qoniqish hech narsaga yetmaydi.

Maqolada muallif so‘nggi bir necha yil davomida o‘rganganlari bilan o‘rtoqlashdi, shuningdek, o‘zini befarq qoldirgan ba’zi keng tarqalgan xatolari bilan o‘rtoqlashadi. Bu juda muhim, chunki har bir dasturiy ta'minot ishlab chiqaruvchisi o'z karerasining ma'lum bir bosqichida muntazam ish vazifalarini avtomatlashtirish uchun skriptlar bilan ishlaydi.

Tuzoq ishlovchilar

Men duch kelgan bash skriptlarining ko'pchiligi skriptni bajarish paytida kutilmagan narsa yuz berganda hech qachon samarali tozalash mexanizmidan foydalanmaydi.

Tashqaridan kutilmagan hodisalar paydo bo'lishi mumkin, masalan, yadrodan signal olish. Bunday holatlarni ko'rib chiqish skriptlarning ishlab chiqarish tizimlarida ishlashi uchun etarlicha ishonchli bo'lishini ta'minlash uchun juda muhimdir. Men tez-tez quyidagi stsenariylarga javob berish uchun chiqish ishlov beruvchilaridan foydalanaman:

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 har qanday signallar paydo bo'lganda chaqiriladigan tozalash funksiyasini ro'yxatdan o'tkazishga yordam beradigan qobiq o'rnatilgan buyrug'idir. Biroq, kabi ishlov beruvchilarga alohida e'tibor berish kerak SIGINT, bu skriptni bekor qilishga olib keladi.

Bundan tashqari, ko'p hollarda siz faqat qo'lga olishingiz kerak EXIT, lekin g'oya shundaki, siz har bir alohida signal uchun skriptning xatti-harakatlarini sozlashingiz mumkin.

O'rnatilgan funktsiyalar to'plami - xato bo'yicha tezkor tugatish

Xatolar paydo bo'lishi bilanoq ularga javob berish va ijroni tezda to'xtatish juda muhimdir. Bu kabi buyruqni bajarishni davom ettirishdan ko'ra yomonroq narsa bo'lishi mumkin emas:

rm -rf ${directory_name}/*

E'tibor bering, o'zgaruvchi directory_name aniqlanmagan.

Bunday stsenariylarni boshqarish uchun o'rnatilgan funktsiyalardan foydalanish muhimdir set, kabi set -o errexit, set -o pipefail yoki set -o nounset skript boshida. Ushbu funktsiyalar skriptingiz nolga teng bo'lmagan chiqish kodi, aniqlanmagan o'zgaruvchilardan foydalanish, quvur orqali o'tkazilgan noto'g'ri buyruqlar va hokazolarga duch kelganda darhol chiqib ketishini ta'minlaydi:

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

Eslatma: kabi o'rnatilgan funktsiyalar set -o errexit, "xom" qaytarish kodi (noldan tashqari) bo'lishi bilanoq skriptdan chiqadi. Shuning uchun, masalan, xatolarni maxsus ishlov berishni joriy qilish yaxshiroqdir:

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

Skriptlarni shu tarzda yozish sizni skriptdagi barcha buyruqlarning xatti-harakati haqida ko'proq ehtiyot bo'lishga va sizni ajablantirmasdan oldin xatolik ehtimolini kutishingizga majbur qiladi.

Rivojlanish jarayonida xatolarni aniqlash uchun ShellCheck

Bu kabi narsalarni birlashtirishga arziydi Shell tekshiruvi Bash kodingizni eng yaxshi amaliyotlar bilan tekshirish uchun ishlab chiqish va sinov quvurlaringizga kiriting.

Men uni mahalliy ishlab chiqish muhitimda sintaksis, semantika va ishlab chiqishda o'tkazib yuborgan koddagi ba'zi xatolar haqida hisobot olish uchun foydalanaman. Bu sizning bash skriptlaringiz uchun statik tahlil vositasi va men undan foydalanishni tavsiya qilaman.

O'zingizning chiqish kodlaringizdan foydalanish

POSIX-da qaytarish kodlari shunchaki nol yoki bitta emas, balki nol yoki nolga teng bo'lmagan qiymatdir. Turli xil xato holatlari uchun maxsus xato kodlarini (201-254 oralig'ida) qaytarish uchun ushbu xususiyatlardan foydalaning.

Keyinchalik bu ma'lumotdan qaysi turdagi xatolik yuz berganini aniq tushunish va shunga mos ravishda javob berish uchun sizni o'rab turgan boshqa skriptlar foydalanishi mumkin:

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

Eslatma: Iltimos, muhit o'zgaruvchilari tasodifan bekor qilinmasligi uchun siz aniqlagan o'zgaruvchilar nomlari bilan ayniqsa ehtiyot bo'ling.

Ro'yxatga olish funktsiyalari

Chiroyli va tuzilgan jurnallar skriptingiz natijalarini osongina tushunish uchun muhimdir. Boshqa yuqori darajadagi dasturlash tillarida bo'lgani kabi, men har doim bash skriptlarimda mahalliy logging funktsiyalaridan foydalanaman, masalan __msg_info, __msg_error va hokazo.

Bu faqat bitta joyda o'zgartirishlar kiritish orqali standartlashtirilgan jurnal tuzilmasini ta'minlashga yordam beradi:

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

Men odatda skriptlarimda qandaydir mexanizmga ega bo'lishga harakat qilaman __init, bu erda bunday logger o'zgaruvchilari va boshqa tizim o'zgaruvchilari ishga tushiriladi yoki standart qiymatlarga o'rnatiladi. Ushbu o'zgaruvchilar skriptni chaqirish paytida buyruq qatori parametrlaridan ham o'rnatilishi mumkin.

Masalan, shunga o'xshash narsa:

$ ./run-script.sh --debug

Bunday skript bajarilganda, agar kerak bo'lsa, tizim bo'ylab sozlamalar standart qiymatlarga o'rnatilishini yoki hech bo'lmaganda kerak bo'lganda mos keladigan narsaga ishga tushirilishini ta'minlaydi.

Men odatda nimani ishga tushirish va nima qilmaslikni tanlashni foydalanuvchi interfeysi va foydalanuvchi o'rganishi mumkin bo'lgan konfiguratsiyalar tafsilotlari o'rtasidagi kelishuvga asoslayman.

Qayta foydalanish uchun arxitektura va tizim holatini tozalash

Modulli/qayta ishlatiladigan kod

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

Men ishlab chiqmoqchi bo'lgan yangi loyiha/bash skriptini ishga tushirish uchun foydalanishim mumkin bo'lgan alohida omborni saqlayman. Qayta foydalanish mumkin bo'lgan har qanday narsa omborda saqlanishi va ushbu funksiyadan foydalanmoqchi bo'lgan boshqa loyihalar tomonidan olinishi mumkin. Loyihalarni shu tarzda tashkil qilish boshqa skriptlarning hajmini sezilarli darajada kamaytiradi, shuningdek, kodlar bazasi kichik va oson sinovdan o'tkazilishini ta'minlaydi.

Yuqoridagi misolda bo'lgani kabi, barcha logging funktsiyalari, masalan __msg_info, __msg_error va boshqalar, masalan, Slack hisobotlari alohida joylashgan common/* va boshqa stsenariylarda dinamik ravishda ulanish daily_database_operation.sh.

Toza tizimni ortda qoldiring

Agar siz skript ishlayotgan vaqtda biron bir manbani yuklayotgan bo'lsangiz, barcha bunday ma'lumotlarni tasodifiy nom bilan umumiy katalogda saqlash tavsiya etiladi, masalan. /tmp/AlRhYbD97/*. Katalog nomini tanlash uchun tasodifiy matn generatorlaridan foydalanishingiz mumkin:

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

Ish tugagandan so'ng, yuqorida muhokama qilingan kanca ishlov beruvchilarida bunday kataloglarni tozalash ta'minlanishi mumkin. Vaqtinchalik kataloglarga g'amxo'rlik qilinmasa, ular to'planib qoladi va qaysidir bosqichda xostda to'liq disk kabi kutilmagan muammolarni keltirib chiqaradi.

Bloklangan fayllardan foydalanish

Ko'pincha skriptning faqat bitta nusxasi istalgan vaqtda xostda ishlayotganiga ishonch hosil qilishingiz kerak. Bu qulflangan fayllar yordamida amalga oshirilishi mumkin.

Men odatda qulflangan fayllarni yarataman /tmp/project_name/*.lock va skript boshida ularning mavjudligini tekshiring. Bu skriptni chiroyli tarzda tugatishga yordam beradi va parallel ravishda ishlaydigan boshqa skript tomonidan tizim holatidagi kutilmagan o'zgarishlarni oldini oladi. Berilgan xostda parallel ravishda bajarilishi uchun bir xil skript kerak bo'lsa, fayllarni bloklash kerak emas.

O'lchash va yaxshilash

Biz ko'pincha uzoq vaqt davomida ishlaydigan skriptlar bilan ishlashimiz kerak, masalan, kundalik ma'lumotlar bazasi operatsiyalari. Bunday operatsiyalar odatda bosqichlar ketma-ketligini o'z ichiga oladi: ma'lumotlarni yuklash, anomaliyalarni tekshirish, ma'lumotlarni import qilish, holat hisobotlarini yuborish va hokazo.

Bunday hollarda, men har doim skriptni alohida kichik skriptlarga ajratishga harakat qilaman va ularning holati va bajarilish vaqtini quyidagi usullardan foydalanib hisobot qilaman:

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

Keyinchalik men ijro vaqtini ko'rishim mumkin:

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

Bu menga optimallashtirishga muhtoj bo'lgan skriptlardagi muammoli/sekin joylarni aniqlashga yordam beradi.

Omad tilaymiz!

Yana nimani o'qish kerak:

  1. Go va GPU keshlari.
  2. Mail.ru Cloud Solutions ning S3 ob'yekt xotirasidagi veb-huklarga asoslangan voqealarga asoslangan dasturga misol.
  3. Raqamli transformatsiya haqida bizning telegram kanalimiz.

Manba: www.habr.com

a Izoh qo'shish