Виконання exit
в підпакеті є одним із недоліків:
#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)
Сценарій роздруковує 42, виходить із допоміжної оболонки із кодом повернення 1
та продовжує сценарій. Навіть заміна дзвінка на echo $(CALC) || exit 1
не допомагає, оскільки код повернення echo
дорівнює 0 незалежно від коду повернення calc
. І calc
виконується до echo
.
Ще більш дивовижним є завада ефекту exit
, вкладаючи його у local
вбудований, як у наступному сценарії. Я наткнувся на проблему, коли написав функцію перевірки вхідного значення. Приклад:
Я хочу створити файл з назвою "year month day.log", тобто 20141211.log
на сьогодні. Дату вводить користувач, який може не надати розумного значення. Тому в своїй функції fname
я перевіряю повернене значення, date
щоб перевірити достовірність введення користувача:
#!/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"
Виглядає добре. Нехай сценарій буде названий s.sh
. Якщо користувач викликає сценарій за допомогою ./s.sh "Thu Dec 11 20:45:49 CET 2014"
, файл 20141211.log
створюється. Якщо ж користувач вводить ./s.sh "Thu hec 11 20:45:49 CET 2014"
, сценарій виводить:
fname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory
У рядку fname…
йдеться про те, що в підпакеті виявлено погані вхідні дані. Але exit 1
кінець local …
рядка ніколи не спрацьовує, оскільки local
директива завжди повертається 0
. Це тому local
, що виконується після $(fname)
і, таким чином, перезаписує свій код повернення. І через це сценарій продовжується і викликає touch
порожній параметр. Цей приклад простий, але поведінка bash може бути дуже заплутаною в реальному застосуванні. Я знаю, справжні програмісти не використовують місцевих жителів.☺
Щоб зрозуміти: без цього local
сценарій скасовує, як очікувалося, коли вводиться недійсна дата.
Виправлення полягає в тому, щоб поділити лінію на зразок
local FNAME
FNAME=$(fname "$1") || exit 1
Дивна поведінка відповідає документації local
в розділі manh на сторінці bash: "Стан повернення 0, якщо не використовується локальна поза функцією, недійсне ім'я або ім'я - змінна, що читається".
Хоча я не є клопотом, але я вважаю, що поведінка баш протикоректно. Я знаю послідовність виконання, все local
ж не слід маскувати порушене завдання.
Моя початкова відповідь містила деякі неточності. Після виявлення та поглибленої дискусії з mikeserv (дякую за це) я пішов їх виправити.