Cum să utilizați set și pipefail în scripturile Bash pe Linux

Publicat: 2022-06-25
Terminal Linux pe un ecran de laptop pe un fundal albastru.
fatmawati achmad zaenuri/Shutterstock.com

Comenzile Linux set și pipefail dictează ce se întâmplă atunci când apare o eroare într-un script Bash. Este mai mult de gândit decât ar trebui să se oprească sau să continue.

LEGATE: Ghidul pentru începători pentru scriptarea Shell: elementele de bază

Scripturi Bash și condiții de eroare

Scripturile shell Bash sunt grozave. Sunt rapid de scris și nu au nevoie de compilare. Orice acțiune repetitivă sau în mai multe etape pe care trebuie să o efectuați poate fi înglobată într-un script convenabil. Și pentru că scripturile pot apela oricare dintre utilitățile standard Linux, nu sunteți limitat la capacitățile limbajului shell în sine.

Dar problemele pot apărea atunci când apelați un utilitar sau un program extern. Dacă nu reușește, utilitarul extern se va închide și va trimite un cod de retur către shell și ar putea chiar să imprime un mesaj de eroare pe terminal. Dar scriptul tău va continua procesarea. Poate că nu asta ți-ai dorit. Dacă apare o eroare la începutul execuției scriptului, aceasta poate duce la probleme mai grave dacă restul scriptului este permis să ruleze.

Ce este Bash Shell și de ce este atât de important pentru Linux?
RELATE Ce este Bash Shell și de ce este atât de important pentru Linux?

Puteți verifica codul de returnare de la fiecare proces extern pe măsură ce se completează, dar acest lucru devine dificil atunci când procesele sunt conectate la alte procese. Codul de returnare va fi din procesul de la capătul conductei, nu cel din mijloc care a eșuat. Desigur, pot apărea erori și în interiorul scriptului, cum ar fi încercarea de a accesa o variabilă neinițializată.

Comenzile set și pipefile vă permit să decideți ce se întâmplă atunci când apar erori ca acestea. De asemenea, vă permit să detectați erorile chiar și atunci când acestea apar în mijlocul unui lanț de țevi.

Iată cum să le folosiți.

Demonstrarea problemei

Iată un script Bash banal. Trimite două linii de text către terminal. Puteți rula acest script dacă copiați textul într-un editor și îl salvați ca „script-1.sh”.

 #!/bin/bash

ecou Acest lucru se va întâmpla mai întâi 
ecou Acest lucru se va întâmpla în al doilea rând

Pentru a-l face executabil, va trebui să utilizați chmod :

 chmod +x script-1.sh

Va trebui să rulați acea comandă pe fiecare script dacă doriți să le executați pe computer. Să rulăm scriptul:

 ./script-1.sh 

Rularea unui script simplu, fără erori.

Cele două rânduri de text sunt trimise în fereastra terminalului așa cum era de așteptat.

Să modificăm ușor scriptul. Îi vom cere lui ls să listeze detaliile unui fișier care nu există. Acest lucru va eșua. L-am salvat ca „script-2.sh” și l-am făcut executabil.

 #!/bin/bash

ecou Acest lucru se va întâmpla mai întâi
ls imaginary-filename
ecou Acest lucru se va întâmpla în al doilea rând

Când rulăm acest script, vedem mesajul de eroare de la ls .

 ./script-2.sh 

Rularea unui script și generarea unei condiții de eșec.

Deși comanda ls a eșuat, scriptul a continuat să ruleze. Și chiar dacă a existat o eroare în timpul execuției scriptului, codul de returnare de la script la shell este zero, ceea ce indică succesul. Putem verifica acest lucru folosind echo și $? variabilă care deține ultimul cod de returnare trimis către shell.

 eco $? 

Verificarea codului de returnare pentru ultimul script executat.

Zero care este raportat este codul de întoarcere de la al doilea ecou din script. Deci, există două probleme cu acest scenariu. Primul este că scriptul a avut o eroare, dar a continuat să ruleze. Acest lucru poate duce la alte probleme dacă restul scriptului se așteaptă sau depinde de acțiunea care a eșuat a reușit efectiv. Și al doilea este că, dacă un alt script sau proces trebuie să verifice succesul sau eșecul acestui script, va primi o citire falsă.

Opțiunea set -e

Opțiunea set -e (ieșire) determină ieșirea unui script dacă oricare dintre procesele pe care le apelează generează un cod de returnare diferit de zero. Orice lucru diferit de zero este considerat un eșec.

Adăugând opțiunea set -e la începutul scriptului, îi putem schimba comportamentul. Acesta este „script-3.sh”.

 #!/bin/bash 
set -e

ecou Acest lucru se va întâmpla mai întâi
ls imaginary-filename
ecou Acest lucru se va întâmpla în al doilea rând

Dacă rulăm acest script, vom vedea efectul set -e .

 ./script-3.sh
 eco $? 

Terminarea unui script la o condiție de eroare și setarea corectă a codului de returnare.

Scriptul este oprit și codul de returnare trimis către shell este o valoare diferită de zero.

Tratarea defecțiunilor la conducte

Conductele adaugă mai multă complexitate problemei. Codul de întoarcere care iese dintr-o secvență de comenzi canalizată este codul de returnare de la ultima comandă din lanț. Dacă există un eșec cu o comandă în mijlocul lanțului, ne întoarcem la punctul unu. Acest cod de returnare este pierdut și scriptul va continua procesarea.

Putem vedea efectele comenzilor de canalizare cu diferite coduri de returnare folosind integrate shell true și false . Aceste două comenzi nu fac altceva decât să genereze un cod returnat de zero sau, respectiv, unu.

 Adevărat
 eco $?
 fals
 eco $? 

Comenzile integrate bash shell true și false.

Dacă introducem false în true - cu false reprezentând un proces care eșuează - obținem codul de returnare al lui true de zero.

 fals | Adevărat
 eco $? 

Transformarea falsului în adevărat.

Bash are o variabilă matrice numită PIPESTATUS și aceasta captează toate codurile de returnare de la fiecare program din lanțul de conducte.

 fals | adevărat | fals | Adevărat
 echo „${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]} ${PIPESTATUS[3]}” 

Folosind PIPESTATUS pentru a vedea codul de retur al tuturor programelor dintr-un lanț de conducte.

PIPESTATUS reține codurile de returnare numai până când următorul program rulează, iar încercarea de a determina ce cod de returnare se potrivește cu care program poate deveni foarte dezordonat foarte repede.

Aici intervin set -o (opțiuni) și pipefail . Acesta este „script-4.sh”. Aceasta va încerca să canalizeze conținutul unui fișier care nu există în wc .

 #!/bin/bash 
set -e

ecou Acest lucru se va întâmpla mai întâi
cat script-99.sh | wc -l
ecou Acest lucru se va întâmpla în al doilea rând

Acest lucru eșuează, așa cum ne-am aștepta.

 ./script-4.sh
 eco $? 

Rularea unui script cu o eroare într-un lanț de conducte.

Primul zero este rezultatul de la wc , spunându-ne că nu a citit nicio linie pentru fișierul lipsă. Al doilea zero este codul de întoarcere de la a doua comandă echo .

Vom adăuga -o pipefail , îl vom salva ca „script-5.sh” și îl vom face executabil.

 #!/bin/bash 
set -eo pipefail

ecou Acest lucru se va întâmpla mai întâi
cat script-99.sh | wc -l
ecou Acest lucru se va întâmpla în al doilea rând

Să rulăm asta și să verificăm codul de returnare.

 ./script-5.sh
 eco $? 

Rularea unui script care captează erorile în lanțurile de conducte și setează corect codul de returnare.

Scriptul se oprește și a doua comandă echo nu este executată. Codul de returnare trimis către shell este unul, indicând corect o eroare.

LEGATE: Cum să utilizați comanda Echo pe Linux

Captarea variabilelor neinițializate

Variabilele neinițializate pot fi dificil de identificat într-un script din lumea reală. Dacă încercăm să echo echo și simplu imprimă o linie goală. Nu afișează un mesaj de eroare. Restul scriptului va continua să se execute.

Acesta este script-6.sh.

 #!/bin/bash 
set -eo pipefail

ecou „$notset” 
echo „O altă comandă ecou”

Îl vom rula și îi vom observa comportamentul.

 ./script-6.sh
 eco $? 

Rularea unui script care nu captează variabile neinițializate.

Scriptul trece peste variabila neinițializată și continuă să se execute. Codul de returnare este zero. Încercarea de a găsi o eroare ca aceasta într-un script foarte lung și complicat poate fi foarte dificilă.

Cum se lucrează cu variabile în Bash
LEGATE Cum să lucrați cu variabile în Bash

Putem prinde acest tip de eroare folosind opțiunea set -u (unset). Vom adăuga asta la colecția noastră tot mai mare de opțiuni setate din partea de sus a scriptului, îl vom salva ca „script-7.sh” și îl vom face executabil.

 #!/bin/bash 

set -eou pipefail

ecou „$notset” 

echo „O altă comandă ecou”

Să rulăm scriptul:

 ./script-7.sh
 eco $? 

Rularea unui script care captează variabile neinițializate.

Variabila neinițializată este detectată, scriptul se oprește și codul de returnare este setat la unul.

Opțiunea -u (unset) este suficient de inteligentă pentru a nu fi declanșată de situații în care puteți interacționa în mod legitim cu o variabilă neinițializată.

În „script-8.sh”, scriptul verifică dacă variabila New_Var este inițializată sau nu. Nu doriți ca scenariul să se oprească aici, într-un scenariu din lumea reală, veți efectua procesări ulterioare și veți rezolva singur situația.

Rețineți că am adăugat opțiunea -u ca a doua opțiune în instrucțiunea set. Opțiunea -o pipefail trebuie să fie ultima.

 #!/bin/bash 

set -euo pipefail

dacă [ -z "${New_Var:-}" ]; apoi 

echo "New_Var nu are nicio valoare atribuită." 

fi

În „script-9.sh”, variabila neinițializată este testată și, dacă este neinițializată, este furnizată o valoare implicită.

 #!/bin/bash
set -euo pipefail

default_value=484
Valoare=${New_Var:-$default_value}
echo "New_Var=$Value"

Scripturile au voie să ruleze până la finalizarea lor.

 ./script-8.sh
 ./script-9.sh 

Rularea a două scripturi în care variabilele neinițializate sunt gestionate intern și opțiunea -u nu se declanșează.

Sigilat cu topor

O altă opțiune la îndemână de utilizat este opțiunea set -x (execuție și imprimare). Când scrieți scenarii, acest lucru poate fi o salvare. imprimă comenzile și parametrii acestora pe măsură ce sunt executate.

Vă oferă o formă rapidă de urmărire a execuției „aspră și gata”. Izolarea defectelor logice și identificarea erorilor devine mult, mult mai ușoară.

Vom adăuga opțiunea set -x la „script-8.sh”, o vom salva ca „script-10.sh” și o vom face executabilă.

 #!/bin/bash
set -euxo pipefail

dacă [ -z "${New_Var:-}" ]; apoi
  echo "New_Var nu are nicio valoare atribuită."
fi

Rulați-l pentru a vedea liniile de urmărire.

 ./script-10.sh 

Rularea unui script cu linii de urmărire -x scrise pe terminal.

Identificarea erorilor în aceste exemple de scripturi banale este ușoară. Când începeți să scrieți scripturi mai implicate, aceste opțiuni își vor dovedi valoarea.