Jak używać set i pipefail w skryptach Bash w systemie Linux

Opublikowany: 2022-06-25
Terminal Linux na ekranie laptopa na niebieskim tle.
fatmawati achmad zaenuri/Shutterstock.com

Linuksowe polecenia set i pipefail dyktują, co się stanie, gdy w skrypcie Bash wystąpi awaria. Trzeba myśleć o czymś więcej niż o tym, czy powinien się zatrzymać, czy kontynuować.

POWIĄZANE: Przewodnik dla początkujących do skryptów powłoki: podstawy

Skrypty Bash i warunki błędów

Skrypty powłoki Bash są świetne. Piszą szybko i nie potrzebują kompilacji. Każdą powtarzalną lub wieloetapową czynność, którą musisz wykonać, można umieścić w wygodnym skrypcie. A ponieważ skrypty mogą wywoływać dowolne standardowe narzędzia Linuksa, nie jesteś ograniczony do możliwości samego języka powłoki.

Ale mogą pojawić się problemy, gdy dzwonisz do zewnętrznego narzędzia lub programu. Jeśli to się nie powiedzie, zewnętrzne narzędzie zamknie się i wyśle ​​kod powrotu do powłoki, a nawet może wydrukować komunikat o błędzie na terminalu. Ale twój skrypt będzie kontynuował przetwarzanie. Może nie tego chciałeś. Jeśli błąd wystąpi na początku wykonywania skryptu, może to prowadzić do gorszych problemów, jeśli reszta skryptu zostanie uruchomiona.

Co to jest powłoka Bash i dlaczego jest tak ważna dla Linuksa?
Czym jest powłoka Bash i dlaczego jest tak ważna dla Linuksa?

Możesz sprawdzić kod powrotu z każdego procesu zewnętrznego po jego zakończeniu, ale staje się to trudne, gdy procesy są przesyłane do innych procesów. Kod powrotu będzie pochodził z procesu na końcu potoku, a nie z tego, który się nie powiódł w środku. Oczywiście w skrypcie mogą również wystąpić błędy, takie jak próba uzyskania dostępu do niezainicjowanej zmiennej.

Polecenia set i pipefile pozwalają zdecydować, co się stanie, gdy wystąpią takie błędy. Pozwalają również wykrywać błędy, nawet jeśli występują w środku łańcucha rur.

Oto jak z nich korzystać.

Demonstracja problemu

Oto banalny skrypt Bash. Wysyła do terminala dwie linijki tekstu. Możesz uruchomić ten skrypt, jeśli skopiujesz tekst do edytora i zapiszesz go jako „script-1.sh”.

 #!/kosz/bash

echo To się stanie najpierw 
echo Stanie się to za drugim razem

Aby był wykonywalny, musisz użyć chmod :

 chmod +x script-1.sh

Musisz uruchomić to polecenie w każdym skrypcie, jeśli chcesz uruchomić je na swoim komputerze. Uruchommy skrypt:

 ./skrypt-1.sh 

Uruchamianie prostego skryptu bez błędów.

Dwie linie tekstu są wysyłane do okna terminala zgodnie z oczekiwaniami.

Zmodyfikujmy nieco skrypt. Poprosimy ls o podanie szczegółów pliku, który nie istnieje. To się nie powiedzie. Zapisaliśmy to jako „script-2.sh” i stworzyliśmy plik wykonywalny.

 #!/kosz/bash

echo To się stanie najpierw
ls wyimaginowana nazwa-pliku
echo Stanie się to za drugim razem

Kiedy uruchamiamy ten skrypt, widzimy komunikat o błędzie z ls .

 ./skrypt-2.sh 

Uruchamianie skryptu i generowanie warunku niepowodzenia.

Chociaż polecenie ls nie powiodło się, skrypt nadal działał. I chociaż wystąpił błąd podczas wykonywania skryptu, kod powrotu ze skryptu do powłoki wynosi zero, co oznacza sukces. Możemy to sprawdzić za pomocą echa i $? zmienna przechowująca ostatni kod powrotu wysłany do powłoki.

 echo $? 

Sprawdzanie kodu powrotu dla ostatniego wykonanego skryptu.

Zgłaszane zero jest kodem powrotu z drugiego echa w skrypcie. W tym scenariuszu są więc dwa problemy. Pierwszym z nich jest to, że skrypt miał błąd, ale nadal działał. Może to prowadzić do innych problemów, jeśli reszta skryptu oczekuje lub zależy od tego, czy akcja, która się nie powiodła, faktycznie się powiodła. Po drugie, jeśli inny skrypt lub proces będzie musiał sprawdzić powodzenie lub niepowodzenie tego skryptu, otrzyma fałszywy odczyt.

Zestaw -e Opcja

Opcja set -e (exit) powoduje zakończenie działania skryptu, jeśli którykolwiek z wywoływanych przez niego procesów wygeneruje niezerowy kod powrotu. Wszystko, co nie jest zerem, jest uważane za awarię.

Dodając opcję set -e na początku skryptu możemy zmienić jego zachowanie. To jest „script-3.sh”.

 #!/kosz/bash 
zestaw -e

echo To się stanie najpierw
ls wyimaginowana nazwa-pliku
echo Stanie się to za drugim razem

Jeśli uruchomimy ten skrypt, zobaczymy efekt set -e .

 ./skrypt-3.sh
 echo $? 

Zakończenie skryptu w przypadku wystąpienia błędu i poprawne ustawienie kodu powrotu.

Skrypt zostaje zatrzymany, a kod powrotu wysłany do powłoki jest wartością niezerową.

Radzenie sobie z awariami w rurach

Orurowanie zwiększa złożoność problemu. Kod powrotu, który pochodzi z sekwencji poleceń potoku, jest kodem powrotu z ostatniego polecenia w łańcuchu. Jeśli wystąpi awaria polecenia w środku łańcucha, wracamy do punktu pierwszego. Ten kod powrotu zostanie utracony, a skrypt będzie kontynuował przetwarzanie.

Możemy zobaczyć efekty poleceń potoku z różnymi kodami powrotu przy użyciu wbudowanych funkcji powłoki true i false . Te dwie komendy nie więcej niż generują kod powrotu równy zero lub jeden, odpowiednio.

 PRAWDA
 echo $?
 fałszywy
 echo $? 

Wbudowane polecenia powłoki bash true i false.

Jeśli przetoczymy false do true — gdzie false reprezentuje proces, który nie powiódł się — otrzymamy kod powrotu true równy zero.

 fałszywe | PRAWDA
 echo $? 

Fałsz do prawdy.

Bash ma zmienną tablicową o nazwie PIPESTATUS , która przechwytuje wszystkie kody powrotu z każdego programu w łańcuchu potoku.

 fałszywe | prawda | fałszywe | PRAWDA
 echo "${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]} ${PIPESTATUS[3]}" 

Użycie PIPESTATUS, aby zobaczyć kod powrotu wszystkich programów w łańcuchu potoku.

PIPESTATUS przechowuje kody powrotne tylko do czasu uruchomienia następnego programu, a próba określenia, który kod powrotny jest powiązany z którym programem, może bardzo szybko stać się bałaganem.

Tutaj wchodzą set -o (opcje) i pipefail . To jest „script-4.sh”. To spróbuje przesłać zawartość pliku, który nie istnieje, do wc .

 #!/kosz/bash 
zestaw -e

echo To się stanie najpierw
kot script-99.sh | wc-l
echo Stanie się to za drugim razem

To się nie udaje, jak się spodziewaliśmy.

 ./skrypt-4.sh
 echo $? 

Uruchamianie skryptu z błędem w łańcuchu potoków.

Pierwsze zero to wyjście z wc , informujące nas, że nie odczytał żadnych wierszy dla brakującego pliku. Drugie zero to kod powrotu z drugiego polecenia echo .

-o pipefail , zapiszemy ją jako „script-5.sh” i sprawimy, że będzie wykonywalny.

 #!/kosz/bash 
set -eo pipefail

echo To się stanie najpierw
kot script-99.sh | wc-l
echo Stanie się to za drugim razem

Uruchommy to i sprawdźmy kod powrotu.

 ./skrypt-5.sh
 echo $? 

Uruchamianie skryptu, który wyłapuje błędy w łańcuchach potoków i poprawnie ustawia kod powrotu.

Skrypt zatrzymuje się, a drugie polecenie echo nie jest wykonywane. Kod powrotu wysłany do powłoki to jeden, poprawnie wskazujący na awarię.

POWIĄZANE: Jak korzystać z polecenia Echo w systemie Linux

Wyłapywanie niezainicjowanych zmiennych

Niezainicjowane zmienne mogą być trudne do wykrycia w rzeczywistym skrypcie. Jeśli spróbujemy echo wartość niezainicjowanej zmiennej, echo po prostu wypisze pustą linię. Nie wyświetla komunikatu o błędzie. Reszta skryptu będzie nadal działać.

To jest skrypt-6.sh.

 #!/kosz/bash 
set -eo pipefail

echo "$notset" 
echo "Kolejne polecenie echo"

Uruchomimy go i będziemy obserwować jego zachowanie.

 ./skrypt-6.sh
 echo $? 

Uruchamianie skryptu, który nie przechwytuje niezainicjowanych zmiennych.

Skrypt przechodzi przez niezainicjowaną zmienną i kontynuuje wykonywanie. Kod powrotu to zero. Próba znalezienia takiego błędu w bardzo długim i skomplikowanym skrypcie może być bardzo trudna.

Jak pracować ze zmiennymi w Bash
POWIĄZANE Jak pracować ze zmiennymi w Bash

Możemy wyłapać ten rodzaj błędu za pomocą opcji set -u (unset). Dodamy to do naszej rosnącej kolekcji ustawień opcji u góry skryptu, zapisz go jako „script-7.sh” i spraw, aby był wykonywalny.

 #!/kosz/bash 

set -eou pipefail

echo "$notset" 

echo "Kolejne polecenie echo"

Uruchommy skrypt:

 ./skrypt-7.sh
 echo $? 

Uruchamianie skryptu, który przechwytuje niezainicjowane zmienne.

Wykryta zostaje niezainicjowana zmienna, skrypt zatrzymuje się, a kod powrotu zostaje ustawiony na jeden.

Opcja -u (unset) jest na tyle inteligentna, że ​​nie może być uruchamiana przez sytuacje, w których możesz legalnie wchodzić w interakcję z niezainicjowaną zmienną.

W „script-8.sh” skrypt sprawdza, czy zmienna New_Var jest zainicjowana, czy nie. Nie chcesz, aby skrypt się na tym zatrzymał, w prawdziwym skrypcie wykonasz dalsze przetwarzanie i sam zajmiesz się sytuacją.

Zauważ, że dodaliśmy opcję -u jako drugą opcję w instrukcji set. Opcja -o pipefail musi być ostatnia.

 #!/kosz/bash 

set -euo pipefail

if [ -z "${Nowa_zmienna:-}" ]; następnie 

echo "Nowa_zmienna nie ma przypisanej wartości." 

fi

W „script-9.sh” testowana jest niezainicjowana zmienna i jeśli jest niezainicjowana, zamiast niej podawana jest wartość domyślna.

 #!/kosz/bash
set -euo pipefail

wartość_domyślna=484
Wartość=${New_Var:-$default_value}
echo "Nowa_zmienna=$Wartość"

Skrypty mogą działać aż do ich zakończenia.

 ./skrypt-8.sh
 ./skrypt-9.sh 

Uruchamianie dwóch skryptów, w których niezainicjowane zmienne są obsługiwane wewnętrznie, a opcja -u nie jest wyzwalana.

Zapieczętowane siekierą

Inną przydatną opcją do użycia jest opcja set -x (wykonaj i drukuj). Kiedy piszesz skrypty, może to być ratunkiem. drukuje polecenia i ich parametry podczas ich wykonywania.

Daje Ci szybką, „grubą i gotową” formę śledzenia egzekucji. Izolowanie błędów logicznych i wykrywanie błędów staje się znacznie łatwiejsze.

Dodamy opcję set -x do „script-8.sh”, zapiszemy jako „script-10.sh” i sprawimy, że będzie wykonywalny.

 #!/kosz/bash
set -euxo pipefail

if [ -z "${Nowa_zmienna:-}" ]; następnie
  echo "Nowa_zmienna nie ma przypisanej wartości."
fi

Uruchom go, aby zobaczyć linie śledzenia.

 ./skrypt-10.sh 

Uruchamianie skryptu z -x linii śledzenia zapisanych na terminalu.

Wykrywanie błędów w tych trywialnych przykładowych skryptach jest łatwe. Kiedy zaczniesz pisać bardziej złożone skrypty, te opcje okażą się przydatne.