Bash skriptu paraugprakse: īsa rokasgrāmata par uzticamiem un veiktspējas Bash skriptiem

Bash skriptu paraugprakse: īsa rokasgrāmata par uzticamiem un veiktspējas Bash skriptiem
Manapi apvalka tapetes

Bash skriptu atkļūdošana ir kā adatas meklēšana siena kaudzē, it īpaši, ja esošajā kodu bāzē parādās jauni papildinājumi, laicīgi neapsverot struktūras, reģistrēšanas un uzticamības problēmas. Jūs varat nonākt šādās situācijās vai nu savu kļūdu dēļ, vai arī, pārvaldot sarežģītas skriptu kaudzes.

Komanda Mail.ru mākoņa risinājumi iztulkojis rakstu ar ieteikumiem, kas palīdzēs labāk rakstīt, atkļūdot un uzturēt skriptus. Ticiet vai nē, nekas nepārspēj gandarījumu, rakstot tīru, lietošanai gatavu bash kodu, kas darbojas katru reizi.

Rakstā autors dalās ar to, ko viņš ir iemācījies pēdējo gadu laikā, kā arī dažas izplatītas kļūdas, kas viņu pieķērušas nejauši. Tas ir svarīgi, jo katrs programmatūras izstrādātājs kādā savas karjeras posmā strādā ar skriptiem, lai automatizētu ikdienas darba uzdevumus.

Slazdu apstrādātāji

Lielākā daļa bash skriptu, ar kuriem esmu saskāries, nekad neizmanto efektīvu tīrīšanas mehānismu, ja skripta izpildes laikā notiek kaut kas neparedzēts.

Pārsteigumi var rasties no ārpuses, piemēram, saņemot signālu no kodola. Šādu gadījumu apstrāde ir ārkārtīgi svarīga, lai nodrošinātu, ka skripti ir pietiekami uzticami, lai tie darbotos ražošanas sistēmās. Es bieži izmantoju izejas apdarinātājus, lai reaģētu uz šādiem scenārijiem:

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 ir čaulā iebūvēta komanda, kas palīdz reģistrēt tīrīšanas funkciju, kas tiek izsaukta jebkādu signālu gadījumā. Tomēr īpaši uzmanīgiem jābūt tādiem apstrādātājiem kā SIGINT, kas izraisa skripta darbības pārtraukšanu.

Turklāt vairumā gadījumu vajadzētu tikai noķert EXIT, taču ideja ir tāda, ka jūs faktiski varat pielāgot skripta darbību katram atsevišķam signālam.

Iebūvētās komplekta funkcijas - ātra pārtraukšana kļūdas gadījumā

Ir ļoti svarīgi reaģēt uz kļūdām, tiklīdz tās rodas, un ātri apturēt izpildi. Nekas nevar būt sliktāks par šādas komandas palaišanu:

rm -rf ${directory_name}/*

Lūdzu, ņemiet vērā, ka mainīgais directory_name nav noteikts.

Lai apstrādātu šādus scenārijus, ir svarīgi izmantot iebūvētās funkcijas set, Piemēram, set -o errexit, set -o pipefail vai set -o nounset scenārija sākumā. Šīs funkcijas nodrošina, ka jūsu skripts tiks aizvērts, tiklīdz tas saskarsies ar jebkuru izejas kodu, kas nav nulle, tiek izmantoti nedefinēti mainīgie, nederīgas komandas, kas nodotas pa cauruli, un tā tālāk.

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

Piezīme: iebūvētas funkcijas, piemēram set -o errexit, izies no skripta, tiklīdz būs “neapstrādāts” atgriešanas kods (kas nav nulle). Tāpēc labāk ir ieviest pielāgotu kļūdu apstrādi, piemēram:

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

Šādi rakstot skriptus, jums jābūt uzmanīgākam pret visu skripta komandu darbību un jāparedz kļūda, pirms tā jūs pārsteidz.

ShellCheck, lai izstrādes laikā atklātu kļūdas

Ir vērts integrēt kaut ko līdzīgu ShellCheck savā izstrādes un testēšanas konveijerā, lai pārbaudītu, vai jūsu bash kods atbilst paraugpraksei.

Es to izmantoju savās vietējās izstrādes vidēs, lai iegūtu pārskatus par sintaksi, semantiku un dažām koda kļūdām, kuras, iespējams, palaidu garām izstrādes laikā. Šis ir statiskās analīzes rīks jūsu bash skriptiem, un es ļoti iesaku to izmantot.

Izmantojot savus izejas kodus

POSIX atgriešanas kodi nav tikai nulle vai viens, bet nulle vai nulle atšķirīga vērtība. Izmantojiet šīs funkcijas, lai dažādiem kļūdu gadījumiem atgrieztu pielāgotus kļūdu kodus (no 201 līdz 254).

Šo informāciju pēc tam var izmantot citi jūsu skripti, lai precīzi saprastu, kāda veida kļūda ir radusies, un attiecīgi reaģētu.

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

Piezīme: lūdzu, esiet īpaši uzmanīgs ar definētajiem mainīgo nosaukumiem, lai izvairītos no nejaušas vides mainīgo ignorēšanas.

Mežizstrādes funkcijas

Skaista un strukturēta reģistrēšana ir svarīga, lai viegli saprastu skripta rezultātus. Tāpat kā citās augsta līmeņa programmēšanas valodās, es vienmēr izmantoju vietējās reģistrēšanas funkcijas savos bash skriptos, piemēram, __msg_info, __msg_error un tā tālāk.

Tas palīdz nodrošināt standartizētu reģistrēšanas struktūru, veicot izmaiņas tikai vienā vietā:

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

Es parasti cenšos savos skriptos ieviest kādu mehānismu __init, kur šādi reģistrētāja mainīgie un citi sistēmas mainīgie ir inicializēti vai iestatīti uz noklusējuma vērtībām. Šos mainīgos var iestatīt arī no komandrindas opcijām skripta izsaukšanas laikā.

Piemēram, kaut kas līdzīgs:

$ ./run-script.sh --debug

Kad šāds skripts tiek izpildīts, tas nodrošina, ka visas sistēmas iestatījumi tiek iestatīti uz noklusējuma vērtībām, ja tie ir nepieciešami, vai vismaz inicializēti uz kaut ko piemērotu, ja nepieciešams.

Izvēloties, ko inicializēt un ko nedarīt, parasti balstu uz kompromisu starp lietotāja interfeisu un konfigurāciju detaļām, kurās lietotājs var/vajadzētu iedziļināties.

Arhitektūra atkārtotai izmantošanai un tīras sistēmas stāvoklim

Modulārs/atkārtoti lietojams kods

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

Es glabāju atsevišķu repozitoriju, ko varu izmantot, lai inicializētu jaunu projektu/bash skriptu, ko vēlos izstrādāt. Visu, ko var izmantot atkārtoti, var glabāt repozitorijā un izgūt citiem projektiem, kas vēlas izmantot šo funkcionalitāti. Projektu organizēšana šādā veidā ievērojami samazina citu skriptu lielumu, kā arī nodrošina, ka koda bāze ir maza un viegli pārbaudāma.

Tāpat kā iepriekš minētajā piemērā, visas reģistrēšanas funkcijas, piemēram, __msg_info, __msg_error un citi, piemēram, Slack pārskati, ir ietverti atsevišķi common/* un dinamiski izveidot savienojumu citos scenārijos, piemēram daily_database_operation.sh.

Atstājiet tīru sistēmu

Ja skripta darbības laikā ielādējat kādus resursus, visus šādus datus ieteicams glabāt koplietotā direktorijā ar nejaušu nosaukumu, piem. /tmp/AlRhYbD97/*. Varat izmantot izlases teksta ģeneratorus, lai atlasītu direktorija nosaukumu:

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

Pēc darba pabeigšanas šādu direktoriju tīrīšanu var nodrošināt iepriekš apspriestajos āķu apstrādātājos. Ja pagaidu direktoriji netiek aprūpēti, tie uzkrājas un kādā posmā rada neparedzētas problēmas resursdatorā, piemēram, pilnu disku.

Bloķēšanas failu izmantošana

Bieži vien jums ir jāpārliecinās, ka resursdatorā jebkurā laikā darbojas tikai viens skripta gadījums. To var izdarīt, izmantojot bloķēšanas failus.

Es parasti izveidoju bloķēšanas failus /tmp/project_name/*.lock un pārbaudiet to klātbūtni skripta sākumā. Tas palīdz skriptam graciozi izbeigt darbību un izvairīties no negaidītām sistēmas stāvokļa izmaiņām, ko izraisa cits skripts, kas darbojas paralēli. Bloķēšanas faili nav nepieciešami, ja jums ir nepieciešams, lai tas pats skripts tiktu izpildīts paralēli noteiktā resursdatorā.

Izmēriet un uzlabojiet

Mums bieži ir jāstrādā ar skriptiem, kas darbojas ilgu laiku, piemēram, ikdienas datu bāzes operācijās. Šādas darbības parasti ietver darbību secību: datu ielāde, anomāliju pārbaude, datu importēšana, statusa ziņojumu nosūtīšana utt.

Šādos gadījumos es vienmēr cenšos sadalīt skriptu atsevišķos mazos skriptos un ziņot par to statusu un izpildes laiku, izmantojot:

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

Vēlāk es varu redzēt izpildes laiku ar:

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

Tas man palīdz identificēt problēmu/lēnos apgabalus skriptos, kuriem nepieciešama optimizācija.

Good luck!

Ko vēl lasīt:

  1. Iet un GPU kešatmiņas.
  2. Piemērs uz notikumu balstītai lietojumprogrammai, kuras pamatā ir tīmekļa aizķeres Mail.ru Cloud Solutions S3 objektu krātuvē.
  3. Mūsu telegrammas kanāls par digitālo transformāciju.

Avots: www.habr.com

Pievieno komentāru