Si të kapni gabimet në skriptet Bash në Linux


Si parazgjedhje, një skript Bash në Linux do të raportojë një gabim, por do të vazhdojë të funksionojë. Ne ju tregojmë se si t'i trajtoni vetë gabimet, në mënyrë që të vendosni se çfarë duhet të ndodhë më pas.

Trajtimi i gabimeve në skriptet

Trajtimi i gabimeve është pjesë e programimit. Edhe nëse shkruani kod pa të meta, prapë mund të hasni në kushte gabimi. Mjedisi në kompjuterin tuaj ndryshon me kalimin e kohës, ndërsa instaloni dhe çinstaloni softuerin, krijoni drejtori dhe kryeni përmirësime dhe përditësime.

Për shembull, një skript që përdoret për të ekzekutuar pa problem mund të hasë në vështirësi nëse shtigjet e drejtorisë ndryshojnë ose lejet ndryshohen në një skedar. Veprimi i paracaktuar i guaskës Bash është të printojë një mesazh gabimi dhe të vazhdojë të ekzekutojë skriptin. Ky është një parazgjedhje e rrezikshme.

Nëse veprimi që dështoi është kritik për ndonjë përpunim ose veprim tjetër që ndodh më vonë në skenarin tuaj, ai veprim kritik nuk do të jetë i suksesshëm. Sa katastrofike rezulton të jetë, varet nga ajo që skenari juaj po përpiqet të bëjë.

Një skemë më e fortë do të zbulonte gabimet dhe do ta lejonte skriptin të funksiononte nëse do të duhej të mbyllej ose të përpiqej të korrigjonte gjendjen e defektit. Për shembull, nëse një drejtori ose skedar mungon, mund të jetë e kënaqshme që skripti t'i rikrijojë ato.

Nëse skripti ka hasur në një problem nga i cili nuk mund të rikuperohet, ai mund të mbyllet. Nëse skripti duhet të mbyllet, ai mund të ketë mundësinë të kryejë çdo pastrim që kërkohet, si p.sh. heqja e skedarëve të përkohshëm ose shkrimi i gjendjes së gabimit dhe arsyes së mbylljes në një skedar log.

Zbulimi i statusit të daljes

Komandat dhe programet gjenerojnë një vlerë që i dërgohet sistemit operativ kur ato përfundojnë. Ky quhet statusi i tyre i daljes. Ka një vlerë zero nëse nuk ka pasur gabime, ose një vlerë jo zero nëse ka ndodhur një gabim.

Ne mund të kontrollojmë statusin e daljes - i njohur gjithashtu si kod kthimi - i komandave që përdor skripti dhe të përcaktojmë nëse komanda ishte e suksesshme apo jo.

Në Bash, zero barazohet me të vërtetën. Nëse përgjigja nga komanda është diçka tjetër përveçse e vërtetë, ne e dimë se ka ndodhur një problem dhe mund të ndërmarrim veprimet e duhura.

Kopjojeni këtë skript në një redaktues dhe ruajeni në një skedar të quajtur bad_command.sh.

#!/bin/bash

if ( ! bad_command ); then
  echo "bad_command flagged an error."
  exit 1
fi

Do t'ju duhet ta bëni skriptin të ekzekutueshëm me komandën chmod. Ky është një hap që kërkohet për të bërë çdo skript të ekzekutueshëm, kështu që nëse doni të provoni skriptet në kompjuterin tuaj, mos harroni ta bëni këtë për secilën prej tyre. Zëvendësoni emrin e skenarit të duhur në secilin rast.

chmod +x bad_command.sh

Kur ekzekutojmë skriptin shohim mesazhin e pritshëm të gabimit.

./bad_command.sh

Nuk ka asnjë komandë të tillë si bad_command, as nuk është emri i një funksioni brenda skriptit. Nuk mund të ekzekutohet, kështu që përgjigja është jo zero. Nëse përgjigja nuk është zero - pikëçuditja përdoret këtu si operatori logjik NOT - ekzekutohet trupi i deklaratës if.

Në një skenar të botës reale, kjo mund të përfundojë skriptin, gjë që e bën shembulli ynë, ose mund të përpiqet të korrigjojë gjendjen e defektit.

Mund të duket sikur linja dalja 1 është e tepërt. Në fund të fundit, nuk ka asgjë tjetër në skenar dhe ai do të përfundojë gjithsesi. Por përdorimi i komandës dalje na lejon të kalojmë statusin e daljes përsëri në shell. Nëse skripti ynë thirret ndonjëherë nga brenda një skripti të dytë, ai skript i dytë do të dijë se ky skript ka hasur gabime.

Mund të përdorni operatorin logjik OR me statusin e daljes së një komande dhe të thërrisni një komandë ose një funksion në skriptin tuaj nëse ka një përgjigje jo zero nga komanda e parë.

command_1 || command_2

Kjo funksionon sepse ose komanda e parë ekzekuton OR e dyta. Komanda majtas ekzekutohet së pari. Nëse ka sukses, komanda e dytë nuk ekzekutohet. Por nëse komanda e parë dështon, komanda e dytë ekzekutohet. Pra, ne mund ta strukturojmë kodin si ky. Kjo është logjike-or./sh.

#!/bin/bash

error_handler()
{
  echo "Error: ($?) $1"
  exit 1
}

bad_command || error_handler "bad_command failed, Line: ${LINENO}"

Ne kemi përcaktuar një funksion të quajtur error_handler. Kjo printon statusin e daljes së komandës së dështuar, të mbajtur në variablin $? dhe një rresht teksti që i kalohet kur thirret funksioni. Kjo mbahet në variablin $1. Funksioni përfundon skriptin me një status daljeje prej një.

Skripti përpiqet të ekzekutojë bad_command i cili padyshim dështon, kështu që komanda në të djathtë të operatorit logjik OR, ||, ekzekutohet. Kjo thërret funksionin error_handler dhe kalon një varg që emërton komandën që dështoi dhe përmban numrin e linjës së komandës së dështuar.

Ne do të ekzekutojmë skriptin për të parë mesazhin e trajtuesit të gabimit dhe më pas do të kontrollojmë statusin e daljes nga skripti duke përdorur echo.

./logical-or.sh
echo $?

Funksioni ynë i vogël error_handler ofron statusin e daljes së përpjekjes për të ekzekutuar bad_command, emrin e komandës dhe numrin e linjës. Ky është informacion i dobishëm kur jeni duke korrigjuar një skript.

Statusi i daljes së skenarit është një. Statusi i daljes 127 i raportuar nga error_handler do të thotë komandë nuk u gjet. Nëse dëshironim, mund ta përdornim atë si statusin e daljes së skriptit duke e kaluar te komanda dalje.

Një qasje tjetër do të ishte zgjerimi i error_handler për të kontrolluar vlerat e ndryshme të mundshme të statusit të daljes dhe për të kryer veprime të ndryshme në përputhje me rrethanat, duke përdorur këtë lloj konstrukti:

exit_code=$?

if [ $exit_code -eq 1 ]; then
  echo "Operation not permitted"

elif [ $exit_code -eq 2 ]; then
  echo "Misuse of shell builtins"
.
.
.
elif [ $status -eq 128 ]; then
  echo "Invalid argument"
fi

Përdorimi i setit Për të detyruar një dalje

Nëse e dini se dëshironi që skripti juaj të dalë sa herë që ka një gabim, mund ta detyroni atë ta bëjë këtë. kjo do të thotë që ju hiqni dorë nga mundësia e ndonjë pastrimi - ose ndonjë dëmtimi të mëtejshëm, gjithashtu - sepse skripti juaj përfundon sapo zbulon një gabim.

Për ta bërë këtë, përdorni komandën set me opsionin -e (gabim). Kjo i thotë skriptit të dalë sa herë që një komandë dështon ose kthen një kod daljeje më të madh se zero. Gjithashtu, përdorimi i opsionit -E siguron që zbulimi dhe kapja e gabimit funksionon në funksionet e guaskës.

Për të kapur gjithashtu variablat e pa inicializuar, shtoni opsionin -u (i pavendosur). Për t'u siguruar që gabimet janë zbuluar në sekuencat e tubacioneve, shtoni opsionin -o pipefail. Pa këtë, statusi i daljes së një sekuence komandash me tubacione është statusi i daljes së komandës përfundimtare në sekuencë. Një komandë e dështuar në mes të sekuencës së tubacionit nuk do të zbulohej. Opsioni -o pipefail duhet të vijë në listën e opsioneve.

Sekuenca për të shtuar në krye të skenarit tuaj është:

set -Eeuo pipefail

Këtu është një skenar i shkurtër i quajtur unset-var.sh, me një ndryshore të pacaktuar në të.

#!/bin/bash

set -Eeou pipefail

echo "$unset_variable"

echo "Do we see this line?"

Kur ekzekutojmë skriptin unset_variable njihet si një variabël e pa inicializuar dhe skripti përfundon.

./unset-var.sh

Komanda e dytë echo nuk ekzekutohet kurrë.

Përdorimi i kurthit me gabime

Komanda Bash trap ju lejon të emëroni një komandë ose një funksion që duhet të thirret kur ngrihet një sinjal i veçantë. Zakonisht kjo përdoret për të kapur sinjale të tilla si SIGINT që ngrihet kur shtypni kombinimin e tastit Ctrl+C. Ky skenar është signt.sh.

#!/bin/bash

trap "echo -e '\nTerminated by Ctrl+c'; exit" SIGINT

counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done

Komanda trap përmban një komandë echo dhe komandën dalje. Do të aktivizohet kur të ngrihet SIGINT. Pjesa tjetër e skenarit është një lak i thjeshtë. Nëse ekzekutoni skriptin dhe shtypni Ctrl+C, do të shihni mesazhin nga përkufizimi trap dhe skripti do të përfundojë.

./sigint.sh

Mund të përdorim trap me sinjalin ERR për të kapur gabimet kur ato ndodhin. Këto më pas mund të futen në një komandë ose funksion. Ky është trap.sh. Ne po dërgojmë njoftime për gabime te një funksion i quajtur error_handler.

#!/bin/bash

trap 'error_handler $? $LINENO' ERR

error_handler() {
  echo "Error: ($1) occurred on $2"
}

main() {
  echo "Inside main() function"
  bad_command
  second
  third
  exit $?
}

second() {
  echo "After call to main()"
  echo "Inside second() function"
}

third() {
  echo "Inside third() function"
}

main

Pjesa më e madhe e skriptit është brenda funksionit main, i cili thërret funksionet e dyta dhe tretë. Kur haset një gabim - në këtë rast, për shkak se bad_command nuk ekziston, deklarata trap e drejton gabimin te funksioni error_handler. Ai kalon statusin e daljes nga komanda e dështuar dhe numrin e linjës te funksioni error_handler.

./trap.sh

Funksioni ynë error_handler thjesht rendit detajet e gabimit në dritaren e terminalit. Nëse dëshironi, mund të shtoni një komandë dalje në funksion për të përfunduar skriptin. Ose mund të përdorni një seri deklaratash if/elif/fi për të kryer veprime të ndryshme për gabime të ndryshme.

Mund të jetë e mundur të korrigjohen disa gabime, të tjera mund të kërkojnë që skripti të ndalet.

Një Këshillë Përfundimtare

Kapja e gabimeve shpesh nënkupton parandalimin e gjërave që mund të shkojnë keq dhe vendosjen e kodit për të trajtuar ato eventualitete nëse ato lindin. Kjo është përveç sigurimit që rrjedha e ekzekutimit dhe logjika e brendshme e skenarit tuaj janë të sakta.

Nëse përdorni këtë komandë për të ekzekutuar skriptin tuaj, Bash do t'ju tregojë një dalje gjurmësh ndërsa skripti ekzekutohet:

bash -x your-script.sh

Bash shkruan daljen e gjurmës në dritaren e terminalit. Ai tregon çdo komandë me argumentet e tij - nëse ka ndonjë. Kjo ndodh pasi komandat janë zgjeruar, por para se të ekzekutohen.

Mund të jetë një ndihmë e jashtëzakonshme në gjurmimin e gabimeve të pakapshme.