Jak wyłapać błędy w skryptach Bash w systemie Linux?

Opublikowany: 2022-08-27
Laptop z systemem Linux wyświetlający monit o bash
fatmawati achmad zaenuri/Shutterstock.com

Domyślnie skrypt Bash w systemie Linux zgłosi błąd, ale będzie działał dalej. Pokażemy Ci, jak samemu radzić sobie z błędami, abyś mógł zdecydować, co musi się wydarzyć dalej.

Obsługa błędów w skryptach

Obsługa błędów jest częścią programowania. Nawet jeśli piszesz bezbłędny kod, nadal możesz napotkać błędy. Środowisko na komputerze zmienia się w czasie, gdy instalujesz i odinstalowujesz oprogramowanie, tworzysz katalogi oraz przeprowadzasz uaktualnienia i aktualizacje.

Dostosowywanie błędów walidacji parametrów w PowerShell
POWIĄZANE Dostosowywanie błędów walidacji parametrów w PowerShell

Na przykład skrypt, który działał bez problemów, może napotkać trudności, jeśli zmienią się ścieżki katalogów lub zmienią się uprawnienia do pliku. Domyślną akcją powłoki Bash jest wydrukowanie komunikatu o błędzie i kontynuowanie wykonywania skryptu. To jest niebezpieczne domyślnie.

Jeśli akcja, która się nie powiodła, jest krytyczna dla innego przetwarzania lub akcji, która ma miejsce później w skrypcie, ta krytyczna akcja nie powiedzie się. Jak katastrofalne okazuje się to, zależy od tego, co próbuje zrobić twój skrypt.

Bardziej niezawodny schemat wykrywałby błędy i pozwalał skryptowi działać, gdyby musiał się zamknąć lub spróbować naprawić stan błędu. Na przykład, jeśli brakuje katalogu lub pliku, wystarczy, że skrypt je odtworzy.

Jeśli skrypt napotkał problem, którego nie może rozwiązać, może się zamknąć. Jeśli skrypt musi zostać zamknięty, może mieć możliwość wykonania dowolnego wymaganego czyszczenia, takiego jak usunięcie plików tymczasowych lub zapisanie stanu błędu i przyczyny zamknięcia w pliku dziennika.

Wykrywanie statusu wyjścia

Polecenia i programy generują wartość, która jest wysyłana do systemu operacyjnego po ich zakończeniu. Nazywa się to ich statusem wyjścia. Ma wartość zero, jeśli nie było błędów, lub pewną wartość niezerową, jeśli wystąpił błąd.

Możemy sprawdzić status wyjścia — znany również jako kod powrotu — poleceń używanych przez skrypt i określić, czy polecenie zakończyło się powodzeniem, czy nie.

W Bash zero równa się prawdzie. Jeśli odpowiedź z polecenia jest inna niż prawda, wiemy, że wystąpił problem i możemy podjąć odpowiednie działania.

Skopiuj ten skrypt do edytora i zapisz go w pliku o nazwie „bad_command.sh”.

 #!/kosz/bash

if ( ! bad_command ); następnie
  echo "bad_command oznaczyło błąd."
  wyjście 1
fi

Będziesz musiał uczynić skrypt wykonywalnym za pomocą polecenia chmod . Jest to krok, który jest wymagany, aby każdy skrypt był wykonywalny, więc jeśli chcesz wypróbować skrypty na własnym komputerze, pamiętaj, aby zrobić to dla każdego z nich. W każdym przypadku zastąp nazwę odpowiedniego skryptu.

 chmod +x bad_command.sh 

Tworzenie skryptu wykonywalnego za pomocą chmod

Po uruchomieniu skryptu widzimy oczekiwany komunikat o błędzie.

 ./złe_polecenie.sh 

Sprawdzanie statusu wyjścia polecenia w celu ustalenia, czy wystąpił błąd

Nie ma takiego polecenia jak „złe_polecenie”, ani nie jest to nazwa funkcji w skrypcie. Nie można go wykonać, więc odpowiedź nie jest równa zero. Jeśli odpowiedź nie jest równa zero — wykrzyknik jest tutaj używany jako logiczny operator NOT — wykonywana jest treść instrukcji if .

W rzeczywistym skrypcie może to zakończyć skrypt, co robi nasz przykład, lub może spróbować naprawić stan błędu.

Może wyglądać na to, że linia exit 1 jest zbędna. W końcu w skrypcie nie ma nic więcej, a i tak się zakończy. Ale użycie polecenia exit pozwala nam przekazać kod wyjścia z powrotem do powłoki. Jeśli nasz skrypt zostanie kiedykolwiek wywołany z drugiego skryptu, ten drugi skrypt będzie wiedział, że ten skrypt napotkał błędy.

Możesz użyć logicznego operatora OR z kodem zakończenia polecenia i wywołać inne polecenie lub funkcję w swoim skrypcie, jeśli odpowiedź od pierwszego polecenia jest niezerowa.

 polecenie_1 || polecenie_2

Działa to, ponieważ albo pierwsze polecenie działa OR drugie. Pierwsze polecenie jest uruchamiane po lewej stronie. Jeśli się powiedzie, drugie polecenie nie zostanie wykonane. Ale jeśli pierwsze polecenie się nie powiedzie, wykonywane jest drugie polecenie. Możemy więc ustrukturyzować kod w ten sposób. To jest „logiczne-lub./sz”.

 #!/kosz/bash

obsługa_błędów()
{
  echo "Błąd: ($?) $1"
  wyjście 1
}

złe_polecenie || error_handler "bad_command nie powiodło się, wiersz: ${LINENO}"

Zdefiniowaliśmy funkcję o nazwie error_handler . To wypisuje kod zakończenia nieudanego polecenia, przechowywanego w zmiennej $? oraz wiersz tekstu, który jest do niego przekazywany, gdy funkcja jest wywoływana. Odbywa się to w zmiennej $1 . Funkcja kończy skrypt z kodem zakończenia jeden.

Skrypt próbuje uruchomić bad_command , co oczywiście kończy się niepowodzeniem, więc polecenie na prawo od logicznego operatora OR , || , jest wykonywany. Wywołuje to funkcję error_handler i przekazuje ciąg, który nazywa polecenie, które się nie powiodło, i zawiera numer wiersza polecenia, które się nie powiodło.

Uruchomimy skrypt, aby zobaczyć komunikat obsługi błędu, a następnie sprawdzimy status wyjścia skryptu za pomocą echo.

 ./logiczny-lub.sh
 echo $? 

Używanie logicznego operatora OR do wywołania procedury obsługi błędów w skrypcie

Nasza mała funkcja error_handler podaje kod zakończenia próby uruchomienia bad_command , nazwę polecenia i numer wiersza. Jest to przydatna informacja podczas debugowania skryptu.

Kod wyjścia skryptu to jeden. Status wyjścia 127 zgłoszony przez error_handler oznacza „nie znaleziono polecenia”. Gdybyśmy chcieli, moglibyśmy użyć tego jako kodu wyjścia skryptu, przekazując go do komendy exit .

Innym podejściem byłoby rozwinięcie error_handler , aby sprawdzić różne możliwe wartości statusu wyjścia i odpowiednio wykonać różne akcje, używając tego typu konstrukcji:

 kod_wyjścia=$?

if [ $ kod_wyjścia -eq 1 ]; następnie
  echo "Operacja niedozwolona"

elif [ $ kod_wyjścia -eq 2 ]; następnie
  echo "Niewłaściwe użycie wbudowanych powłok"
.
.
.
elif [ $status -eq 128 ]; następnie
  echo "Nieprawidłowy argument"
fi

Używanie zestawu do wymuszenia wyjścia

Jeśli wiesz, że chcesz, aby skrypt kończył się za każdym razem, gdy wystąpi błąd, możesz go do tego wymusić. oznacza to, że rezygnujesz z szansy na jakiekolwiek czyszczenie — lub dalsze szkody — ponieważ twój skrypt kończy działanie, gdy tylko wykryje błąd.

Aby to zrobić, użyj polecenia set z opcją -e (błąd). To mówi skryptowi, aby zakończył działanie, gdy polecenie nie powiedzie się lub zwróci kod zakończenia większy niż zero. Ponadto użycie opcji -E zapewnia, że ​​wykrywanie błędów i wyłapywanie działa w funkcjach powłoki.

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

Aby przechwycić również niezainicjowane zmienne, dodaj opcję -u (unset). Aby upewnić się, że błędy są wykrywane w sekwencjach potokowych, dodaj opcję -o pipefail . Bez tego kod zakończenia sekwencji poleceń w potoku jest kodem zakończenia ostatniego polecenia w sekwencji. Błędne polecenie w środku sekwencji potoku nie zostanie wykryte. Opcja -o pipefail musi znajdować się na liście opcji.

Sekwencja do dodania na początek skryptu to:

 set -Eeuo pipefail

Oto krótki skrypt o nazwie „unset-var.sh”, zawierający nieustawioną zmienną.

 #!/kosz/bash

set -Eeou pipefail

echo "$unset_variable"

echo "Czy widzimy tę linię?"

Kiedy uruchamiamy skrypt, unset_variable jest rozpoznawana jako niezainicjowana zmienna i skrypt zostaje zakończony.

 ./unset-var.sh 

Używanie polecenia set w skrypcie do zakończenia skryptu w przypadku wystąpienia błędu

Drugie polecenie echo nigdy nie jest wykonywane.

Korzystanie z pułapki z błędami

Polecenie Bash trap pozwala wyznaczyć polecenie lub funkcję, która powinna zostać wywołana, gdy zostanie podniesiony określony sygnał. Zwykle służy to do przechwytywania sygnałów, takich jak SIGINT , które są wywoływane po naciśnięciu kombinacji klawiszy Ctrl+C. Ten skrypt to „signint.sh”.

 #!/kosz/bash

trap "echo -e '\nZakończone przez Ctrl+c'; wyjście" SIGINT

licznik=0

podczas gdy prawda
robić 
  echo "Numer pętli:" $((++counter))
  spać 1
Gotowe

Polecenie trap zawiera polecenie echo i polecenie exit . Zostanie wywołany, gdy zostanie podniesiony SIGINT . Reszta skryptu to prosta pętla. Jeśli uruchomisz skrypt i naciśniesz Ctrl+C, zobaczysz komunikat z definicji trap , a skrypt zakończy działanie.

 ./signt.sh 

Używanie pułapki w skrypcie do przechwytywania Ctrl+c

Możemy użyć trap z sygnałem ERR , aby wyłapać pojawiające się błędy. Można je następnie wprowadzić do polecenia lub funkcji. To jest „trap.sh”. Wysyłamy powiadomienia o błędach do funkcji o nazwie error_handler .

 #!/kosz/bash

pułapka 'obsługa_błędów $? $LINENO'BŁĄD

obsługa_błędów() {
  echo "Błąd: ($1) wystąpił w dniu $2"
}

Główny() {
  echo "Wewnątrz funkcji main()"
  złe_polecenie
  druga
  trzeci
  wyjść $?
}

druga() {
  echo "Po wywołaniu funkcji main()"
  echo "Wewnątrz funkcji second()"
}

trzeci () {
  echo "Wewnątrz funkcji trzeciej()"
}

Główny

Większość skryptu znajduje się wewnątrz funkcji main , która wywołuje second i third funkcję. W przypadku napotkania błędu — w tym przypadku, ponieważ bad_command nie istnieje — instrukcja trap kieruje błąd do funkcji error_handler . Przekazuje kod wyjścia z polecenia zakończonego niepowodzeniem i numer wiersza do funkcji error_handler .

 ./trap.sh 

Używanie pułapki z ERR do wyłapywania błędów w skrypcie

Nasza funkcja error_handler po prostu wyświetla szczegóły błędu w oknie terminala. Jeśli chcesz, możesz dodać polecenie exit do funkcji, aby skrypt się zakończył. Możesz też użyć serii instrukcji if/elif/fi , aby wykonać różne działania dla różnych błędów.

Niektóre błędy można naprawić, inne mogą wymagać zatrzymania skryptu.

Ostatnia wskazówka

Wyłapywanie błędów często oznacza uprzedzanie rzeczy, które mogą pójść nie tak, i wprowadzanie kodu, aby poradzić sobie z tymi ewentualnościami, jeśli się pojawią. To oprócz upewnienia się, że przepływ wykonywania i wewnętrzna logika skryptu są poprawne.

Jeśli użyjesz tego polecenia do uruchomienia skryptu, Bash wyświetli wynik śledzenia podczas wykonywania skryptu:

 bash -x twój-skrypt.sh

Bash zapisuje dane wyjściowe śledzenia w oknie terminala. Pokazuje każde polecenie z jego argumentami — jeśli jakieś ma. Dzieje się tak po rozwinięciu poleceń, ale przed ich wykonaniem.

Może to być ogromna pomoc w tropieniu nieuchwytnych błędów.

POWIĄZANE: Jak sprawdzić poprawność składni skryptu Linux Bash przed jego uruchomieniem?