Wykonanie exitw podpowłoce to jedna pułapka:
#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)
Skrypt wypisuje 42, wychodzi z podpowłoki z kodem powrotu 1i kontynuuje wykonywanie skryptu. Nawet zastąpienie wywołania echo $(CALC) || exit 1nie pomaga, ponieważ kod powrotu echowynosi 0 niezależnie od kodu powrotu calc. I calcjest wykonywany przed echo.
Jeszcze więcej zagadek niweczy efekt exitzawijania go do localwbudowanego, jak w poniższym skrypcie. Natknąłem się na problem, gdy napisałem funkcję do weryfikacji wartości wejściowej. Przykład:
Chcę utworzyć plik o nazwie „rok miesiąc dzień.log”, tj. 20141211.logNa dziś. Data jest wprowadzana przez użytkownika, który może nie podać rozsądnej wartości. Dlatego w mojej funkcji fnamesprawdzam wartość zwracaną w datecelu sprawdzenia poprawności danych wejściowych użytkownika:
#!/bin/bash
doit ()
{
local FNAME=$(fname "$1") || exit 1
touch "${FNAME}"
}
fname ()
{
date +"%Y%m%d.log" -d"$1" 2>/dev/null
if [ "$?" != 0 ] ; then
echo "fname reports \"Illegal Date\"" >&2
exit 1
fi
}
doit "$1"
Wygląda dobrze. Niech skrypt ma nazwę s.sh. Jeśli użytkownik wywoła skrypt za pomocą ./s.sh "Thu Dec 11 20:45:49 CET 2014", plik 20141211.logzostanie utworzony. Jeśli jednak użytkownik wpisze ./s.sh "Thu hec 11 20:45:49 CET 2014", skrypt wyświetli:
fname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory
Linia fname…mówi, że w podpowłoce wykryto złe dane wejściowe. Ale exit 1koniec local …linii nigdy się nie uruchamia, ponieważ localdyrektywa zawsze powraca 0. Wynika to z faktu, że localjest wykonywany po, $(fname) a tym samym zastępuje swój kod powrotu. Z tego powodu skrypt jest kontynuowany i wywołuje się touchz pustym parametrem. Ten przykład jest prosty, ale zachowanie bash może być mylące w prawdziwej aplikacji. Wiem, prawdziwi programiści nie używają miejscowych
Aby było to jasne: bez localskryptu zostanie przerwany zgodnie z oczekiwaniami po wprowadzeniu niepoprawnej daty.
Rozwiązaniem jest podzielenie linii jak
local FNAME
FNAME=$(fname "$1") || exit 1
Dziwne zachowanie jest zgodne z dokumentacją strony localpodręcznika bash: „Zwracany status to 0, chyba że lokalny jest używany poza funkcją, podana jest niepoprawna nazwa lub nazwa jest zmienną tylko do odczytu”.
Chociaż nie jestem błędem, uważam, że zachowanie bash jest sprzeczne z intuicją. Zdaję sobie sprawę z sekwencji wykonania, localnie powinien jednak maskować zepsutego zadania.
Moja wstępna odpowiedź zawierała pewne niedokładności. Po odkrywczej i dogłębnej dyskusji z mikeserv (dziękuję za to) postanowiłem je naprawić.