Arferion Gorau Sgriptio Bash: Canllaw Cyflym i Sgriptiau Bash Dibynadwy a Pherfformiad

Arferion Gorau Sgriptio Bash: Canllaw Cyflym i Sgriptiau Bash Dibynadwy a Pherfformiad
Papur wal cregyn gan manapi

Mae dadfygio sgriptiau bash yn debyg i chwilio am nodwydd mewn tas wair, yn enwedig pan fydd ychwanegiadau newydd yn ymddangos yn y sylfaen cod presennol heb ystyried materion strwythur, logio a dibynadwyedd yn amserol. Gallwch gael eich hun mewn sefyllfaoedd o'r fath naill ai oherwydd eich camgymeriadau eich hun neu wrth reoli pentyrrau cymhleth o sgriptiau.

Tîm Atebion Cwmwl Mail.ru cyfieithu erthygl gydag argymhellion a fydd yn eich helpu i ysgrifennu, dadfygio a chynnal eich sgriptiau yn well. Credwch neu beidio, does dim byd yn curo'r boddhad o ysgrifennu cod bash glân, parod i'w ddefnyddio sy'n gweithio bob tro.

Yn yr erthygl, mae'r awdur yn rhannu'r hyn y mae wedi'i ddysgu dros y blynyddoedd diwethaf, yn ogystal â rhai camgymeriadau cyffredin sydd wedi ei ddal yn wyliadwrus. Mae hyn yn bwysig oherwydd bod pob datblygwr meddalwedd, ar ryw adeg yn eu gyrfa, yn gweithio gyda sgriptiau i awtomeiddio tasgau gwaith arferol.

Trinwyr trapiau

Nid yw'r rhan fwyaf o sgriptiau bash yr wyf wedi dod ar eu traws byth yn defnyddio mecanwaith glanhau effeithiol pan fydd rhywbeth annisgwyl yn digwydd wrth gyflawni sgript.

Gall syndod godi o'r tu allan, megis derbyn signal o'r craidd. Mae trin achosion o'r fath yn hynod bwysig i sicrhau bod y sgriptiau'n ddigon dibynadwy i redeg ar systemau cynhyrchu. Byddaf yn aml yn defnyddio trinwyr allanfa i ymateb i senarios fel hyn:

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 yn gragen adeiledig yn gorchymyn sy'n eich helpu i gofrestru swyddogaeth glanhau a elwir rhag ofn y bydd unrhyw signalau. Fodd bynnag, dylid cymryd gofal arbennig gyda thrinwyr fel SIGINT, sy'n achosi i'r sgript erthylu.

Yn ogystal, yn y rhan fwyaf o achosion dim ond dylech ddal EXIT, ond y syniad yw y gallwch chi mewn gwirionedd addasu ymddygiad y sgript ar gyfer pob signal unigol.

Swyddogaethau gosod adeiledig - terfyniad cyflym ar gamgymeriad

Mae'n bwysig iawn ymateb i wallau cyn gynted ag y byddant yn digwydd a rhoi'r gorau i'w gweithredu'n gyflym. Ni allai dim fod yn waeth na pharhau i redeg gorchymyn fel hyn:

rm -rf ${directory_name}/*

Sylwch fod y newidyn directory_name ddim yn benderfynol.

Mae'n bwysig defnyddio swyddogaethau adeiledig i ymdrin â senarios o'r fath setmegis set -o errexit, set -o pipefail neu set -o nounset ar ddechrau'r sgript. Mae'r swyddogaethau hyn yn sicrhau y bydd eich sgript yn gadael cyn gynted ag y bydd yn dod ar draws unrhyw god ymadael nad yw'n sero, defnydd o newidynnau heb eu diffinio, gorchmynion annilys a drosglwyddir dros bibell, ac ati:

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

Nodyn: swyddogaethau adeiledig fel set -o errexit, yn gadael y sgript cyn gynted ag y bydd cod dychwelyd "amrwd" (heblaw am sero). Felly mae'n well cyflwyno trin gwallau arferol, er enghraifft:

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

Mae ysgrifennu sgriptiau fel hyn yn eich gorfodi i fod yn fwy gofalus am ymddygiad yr holl orchmynion yn y sgript a rhagweld y posibilrwydd o gamgymeriad cyn iddo eich synnu.

ShellCheck i ganfod gwallau yn ystod datblygiad

Mae'n werth integreiddio rhywbeth fel Gwiriad Cragen i mewn i'ch piblinellau datblygu a phrofi i wirio'ch cod bash yn erbyn arferion gorau.

Rwy'n ei ddefnyddio yn fy amgylcheddau datblygu lleol i gael adroddiadau ar gystrawen, semanteg, a rhai gwallau yn y cod y gallwn fod wedi'u methu wrth ddatblygu. Offeryn dadansoddi statig yw hwn ar gyfer eich sgriptiau bash ac rwy'n argymell yn fawr ei ddefnyddio.

Gan ddefnyddio'ch codau ymadael eich hun

Nid dim ond sero neu un yw'r codau dychwelyd yn POSIX, ond sero neu werth nad yw'n sero. Defnyddiwch y nodweddion hyn i ddychwelyd codau gwall arferol (rhwng 201-254) ar gyfer gwahanol achosion o wallau.

Gall y wybodaeth hon wedyn gael ei defnyddio gan sgriptiau eraill sy'n lapio'ch un chi i ddeall yn union pa fath o wall a ddigwyddodd ac ymateb yn unol â hynny:

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

Nodyn: byddwch yn arbennig o ofalus gyda'r enwau newidyn rydych chi'n eu diffinio er mwyn osgoi diystyru newidynnau amgylchedd yn ddamweiniol.

Swyddogaethau logio

Mae logio hardd a strwythuredig yn bwysig er mwyn deall canlyniadau eich sgript yn hawdd. Yn yr un modd ag ieithoedd rhaglennu lefel uchel eraill, rwyf bob amser yn defnyddio swyddogaethau logio brodorol yn fy sgriptiau bash, megis __msg_info, __msg_error ac yn y blaen.

Mae hyn yn helpu i ddarparu strwythur logio safonol trwy wneud newidiadau mewn un lle yn unig:

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

Fel arfer rwy'n ceisio cael rhyw fath o fecanwaith yn fy sgriptiau __init, lle mae newidynnau cofnodwr o'r fath a newidynnau system eraill yn cael eu cychwyn neu eu gosod i werthoedd rhagosodedig. Gellir gosod y newidynnau hyn hefyd o opsiynau llinell orchymyn yn ystod galw sgript.

Er enghraifft, rhywbeth fel:

$ ./run-script.sh --debug

Pan weithredir sgript o'r fath, mae'n sicrhau bod gosodiadau system gyfan yn cael eu gosod i werthoedd rhagosodedig os oes eu hangen, neu o leiaf eu cychwyn i rywbeth priodol os oes angen.

Rwyf fel arfer yn seilio'r dewis o beth i'w gychwyn a beth i beidio â'i wneud ar gyfaddawd rhwng y rhyngwyneb defnyddiwr a manylion y ffurfweddiadau y gall / y dylai'r defnyddiwr ymchwilio iddynt.

Pensaernïaeth ar gyfer ailddefnyddio a chyflwr system lân

Cod modiwlaidd/ailddefnyddiol

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

Rwy'n cadw ystorfa ar wahân y gallaf ei defnyddio i gychwyn sgript prosiect / bash newydd yr wyf am ei datblygu. Gall unrhyw beth y gellir ei ailddefnyddio gael ei storio mewn ystorfa a'i adfer gan brosiectau eraill sydd am ddefnyddio'r swyddogaeth honno. Mae trefnu prosiectau fel hyn yn lleihau maint sgriptiau eraill yn sylweddol a hefyd yn sicrhau bod sylfaen y cod yn fach ac yn hawdd ei brofi.

Fel yn yr enghraifft uchod, mae holl swyddogaethau logio megis __msg_info, __msg_error ac mae eraill, megis adroddiadau Slack, wedi'u cynnwys ar wahân yn common/* a chysylltu'n ddeinamig mewn senarios eraill fel daily_database_operation.sh.

Gadewch system lân ar ôl

Os ydych yn llwytho unrhyw adnoddau tra bod y sgript yn rhedeg, argymhellir storio'r holl ddata o'r fath mewn cyfeiriadur a rennir gydag enw ar hap, e.e. /tmp/AlRhYbD97/*. Gallwch ddefnyddio generaduron testun ar hap i ddewis enw'r cyfeiriadur:

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

Ar ôl cwblhau'r gwaith, gellir glanhau cyfeirlyfrau o'r fath yn y trinwyr bachau a drafodwyd uchod. Os na chymerir gofal o gyfeiriaduron dros dro, maent yn cronni ac ar ryw adeg yn achosi problemau annisgwyl ar y gwesteiwr, megis disg lawn.

Defnyddio ffeiliau clo

Yn aml mae angen i chi sicrhau mai dim ond un enghraifft o sgript sy'n rhedeg ar westeiwr ar unrhyw adeg benodol. Gellir gwneud hyn gan ddefnyddio ffeiliau clo.

Fel arfer rwy'n creu ffeiliau cloi i mewn /tmp/project_name/*.lock a gwiriwch am eu presenoldeb ar ddechrau'r sgript. Mae hyn yn helpu'r sgript i derfynu'n osgeiddig ac osgoi newidiadau annisgwyl i gyflwr y system gan fod sgript arall yn rhedeg yn gyfochrog. Nid oes angen ffeiliau clo os oes angen yr un sgript arnoch i'w gweithredu ochr yn ochr â gwesteiwr penodol.

Mesur a gwella

Yn aml mae angen i ni weithio gyda sgriptiau sy'n rhedeg dros gyfnodau hir o amser, fel gweithrediadau cronfa ddata dyddiol. Mae gweithrediadau o'r fath fel arfer yn cynnwys dilyniant o gamau: llwytho data, gwirio am anghysondebau, mewnforio data, anfon adroddiadau statws, ac ati.

Mewn achosion o'r fath, byddaf bob amser yn ceisio torri'r sgript yn sgriptiau bach ar wahân ac adrodd ar eu statws a'u hamser gweithredu gan ddefnyddio:

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

Yn ddiweddarach gallaf weld yr amser dienyddio gyda:

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

Mae hyn yn fy helpu i nodi meysydd problem / araf mewn sgriptiau sydd angen eu hoptimeiddio.

Pob lwc!

Beth arall i'w ddarllen:

  1. Ewch a caches GPU.
  2. Enghraifft o raglen sy'n cael ei gyrru gan ddigwyddiad yn seiliedig ar webhooks yn storfa gwrthrych S3 Mail.ru Cloud Solutions.
  3. Ein sianel telegram am drawsnewid digidol.

Ffynhonnell: hab.com

Ychwanegu sylw