У меня есть скрипт, который не выходит, когда я этого хочу.
Пример сценария с той же ошибкой:
#!/bin/bash
function bla() {
return 1
}
bla || ( echo '1' ; exit 1 )
echo '2'
Я хотел бы увидеть вывод:
:~$ ./test.sh
1
:~$
Но я на самом деле вижу:
:~$ ./test.sh
1
2
:~$
Создает ли ()
цепочка команд какую-либо область видимости? Из чего exit
выходит, если не сценарий?
shell-script
exit
subshell
Minix
источник
источник
Ответы:
()
запускает команды в подоболочке, поэтомуexit
вы выходите из подоболочки и возвращаетесь в родительскую оболочку. Используйте фигурные скобки,{}
если вы хотите запускать команды в текущей оболочке.Из руководства Bash:
Стоит отметить, что синтаксис оболочки является вполне согласованным, и подоболочка участвует также в других
()
конструкциях, таких как подстановка команд (также с`..`
синтаксисом старого стиля ) или подстановка процессов, поэтому следующее не выйдет из текущей оболочки:Хотя может быть очевидным, что подоболочки участвуют, когда команды размещаются внутри явно
()
, менее заметным является тот факт, что они также создаются в этих других структурах:команда началась в фоновом режиме
не выходит из текущей оболочки, потому что (после
man bash
)трубопровод
по-прежнему выходит только из подоболочки.
Однако разные оболочки ведут себя по-разному в этом отношении. Например,
bash
все компоненты конвейера помещаются в отдельные подоболочки (если только вы не используетеlastpipe
опцию в вызовах, где управление заданиями не включено), но выполняйте AT & Tksh
иzsh
запускайте последнюю часть внутри текущей оболочки (оба поведения разрешены POSIX). таким образомв bash ничего не делает, но выходит из zsh из-за последнего
exit
.coproc exit
также работаетexit
в подоболочке.источник
{
и}
не синтаксис, они являются зарезервированными словами и должны быть окружены пробелами, и список должен заканчиваться командой терминатора (точка с запятой, символ новой строки, амперсанд)(echo $$)
печатает идентификатор родительской оболочки, потому что$$
он раскрывается еще до создания подоболочки. На самом деле печать подоболочку идентификатор процесса может быть сложно, см stackoverflow.com/questions/9119885/...$$
развернуто перед созданием подоболочки и$BASHPID
покажет правильное значение для подоболочки?Выполнение
exit
в подоболочке является одной ловушкой:Сценарий печатает 42, выходит из подоболочки с кодом возврата
1
и продолжает выполнение сценария. Даже замена вызова наecho $(CALC) || exit 1
не помогает, потому что код возвратаecho
равен 0 независимо от кода возвратаcalc
. Иcalc
выполняется доecho
.Еще большее недоумение сводит на нет эффект
exit
его оборачивания воlocal
встроенный, как в следующем скрипте. Я наткнулся на проблему, когда написал функцию для проверки входного значения. Пример:Я хочу создать файл с именем "year month day.log", т.е.
20141211.log
на сегодня. Дата вводится пользователем, который не может предоставить разумное значение. Поэтому в моей функцииfname
я проверяю возвращаемое значение,date
чтобы проверить правильность ввода пользователя:Выглядит неплохо. Пусть сценарий будет назван
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…
говорит, что в подоболочке были обнаружены неверные входные данные. Ноexit 1
в концеlocal …
строки никогда не срабатывает, потому чтоlocal
директива всегда возвращается0
. Это потому, чтоlocal
выполняется после$(fname)
и, следовательно, перезаписывает свой код возврата. И из-за этого скрипт продолжается и запускаетсяtouch
с пустым параметром. Этот пример прост, но поведение bash может привести к путанице в реальном приложении. Я знаю, что настоящие программисты не используют местных жителей.Чтобы было понятно: без
local
, сценарий прерывается, как и ожидалось, когда вводится недопустимая дата.Исправление состоит в том, чтобы разделить линию как
Странное поведение соответствует документации
local
на странице руководства bash: «Статус возврата равен 0, если local не используется вне функции, указано неверное имя или name является переменной только для чтения».Хотя это и не ошибка, но я чувствую, что поведение bash нелогично. Я в курсе последовательности выполнения, тем
local
не менее не следует маскировать нарушенное назначение.Мой первоначальный ответ содержал некоторые неточности. После откровенного и глубокого обсуждения с mikeserv (спасибо за это) я пошел на их исправление.
источник
doit()
.Актуальное решение:
Группировка ошибок будет выполняться только в том случае, если
bla
возвращается состояние ошибки, аexit
не в подоболочке, поэтому весь сценарий останавливается.источник
Квадратные скобки запускают подоболочку, а выход - только из этой скорлупы.
Вы можете прочитать код выхода с помощью
$?
и добавить его в свой скрипт, чтобы выйти из скрипта, если была завершена подоболочка:источник