У меня были некоторые странные проблемы с Bash в последнее время. Пытаясь упростить свой сценарий, я разработал небольшой фрагмент кода:
$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1
return
должен был выйти из функции без печати $?
, не так ли? Ну, тогда я проверил, могу ли я вернуться из трубы в одиночку:
$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
То же самое происходит без while
цикла:
$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.
Есть что-то, чего я здесь не хватает? Поиск Google не принес ничего об этом! Моя версия bash - 4.2.37 (1) -релиз на Debian Wheezy.
bash
shell-script
pipe
subshell
Тереза и Джуниор
источник
источник
while
не нужны для воспроизведения? Это отвлекает от сути.while
Цикл - это очень распространенное использование для канала сreturn
. Второй пример более прямолинеен, но я не верю, что кто-то когда-либо будет его использовать ...Ответы:
Связанный: /programming//a/7804208/4937930
Это не ошибка, что вы не можете выйти из скрипта или вернуться из функции в
exit
илиreturn
в подоболочки. Они выполняются в другом процессе и не влияют на основной процесс.Кроме того, я полагаю, вы видите недокументированное поведение bash в (возможно) неопределенной спецификации. В функции не допускаются ошибки
return
на верхнем уровне команд подоболочки, и она просто ведет себя какexit
.ИМХО, это ошибка bash для противоречивого поведения в
return
зависимости от того, находится главный оператор в функции или нет.Выход:
источник
return
он не работает из последовательности команд верхнего уровня в подоболочке и, в частности, не выходит из подоболочки, это то, что уже заставило меня ожидать существующие документы. ОП может использоватьexit 1 || return 1
там, где они пытаются использоватьreturn
, и затем должен получить ожидаемое поведение. РЕДАКТИРОВАТЬ: @ ответ Герберта указывает, что верхний уровеньreturn
в подоболочке функционирует какexit
(но только из подоболочки).return
в виду, что в простой последовательности подоболочек следует утверждать, что в любом случае это ошибка времени выполнения , но на самом деле это не так, когда это происходит в функции. Эта проблема также обсуждалась в gnu.bash.bug , но пока нет никаких выводов.return
оператор находится в функции и, следовательно, является законным. Получающееся поведение однако не определено.Это не ошибка,
bash
а ее документированное поведение :return
Инструкция действительно существо внутри определения функции , но будучи в субоболочке , а также, это не влияет на его родительскую оболочку так , следующую инструкцию,echo
, выполняются независимо. Тем не менее, это непереносимая конструкция оболочки, поскольку стандарт POSIX позволяет командам, составляющим конвейер, выполняться либо в подоболочке (по умолчанию), либо в верхней (разрешенное расширение).Надеюсь, вы можете сказать,
bash
чтобы вести себя так, как вы ожидаете, с помощью нескольких вариантов:источник
return
не выйдет из функции, не будет ли больше смысла, если оболочка только что напечатанаbash: return: can only `return' from a function or sourced script
, вместо того, чтобы дать пользователю ложное представление о том, что функция могла вернуться?return
команду из подоболочки. В конце концов, он знает, что находится в подоболочке, о чем свидетельствует$BASH_SUBSHELL
переменная. Самая большая проблема заключается в том, что это может привести к ложным срабатываниям; пользователь, который понимает, как работают подоболочки, мог бы написать сценарии, которые используютсяreturn
вместоexit
завершения подоболочки. (И, конечно же, существуют допустимые случаи, когда кто-то может захотеть установить переменные или сделать этоcd
в подоболочке.)return
возвращается из подоболочки вместо сбоя, поскольку он находится внутри фактической функции. Проблема заключается в том, чтоhelp return
конкретно говорится:Causes a function or sourced script to exit with the return value specified by N.
читая документацию, любой пользователь может ожидать, что она хотя бы потерпит неудачу или напечатает предупреждение, но никогда не будет вести себя какexit
.return
подоболочке в функции вернется из функции (в основном процессе оболочки), не очень хорошо понимает подоболочки. И наоборот, я ожидал бы, что читатель, понимающий подоболочки, ожидает, чтоreturn
в подоболочке функция завершит подоболочку, как иexit
раньше.Согласно документации POSIX, использование
return
вне функции или сценария не определено . Таким образом, это зависит от вашей оболочки для обработки.Оболочка SystemV будет сообщать об ошибке, в то время как внутри
ksh
,return
вне функции или исходного сценария ведут себя какexit
. Большинство других POSIX-оболочек и osh schily также ведут себя так:ksh
иzsh
не выводил, потому что последняя часть конвейера в этих оболочках была выполнена в текущей оболочке вместо subshell. Оператор return повлиял на текущую оболочку, которая вызвала функцию, поэтому функция возвращается немедленно, ничего не печатая.В интерактивном сеансе
bash
только сообщите об ошибке, но не завершил работу оболочки,schily's osh
сообщил об ошибке и завершил работу оболочки:(
zsh
В интерактивном режиме и выходе терминала не прекращается,bash
,yash
иschily's osh
сообщает об ошибке , но не прекращает оболочку)источник
return
что используется внутри функции здесь.return
было использование внутри subshell внутри функции , кромеksh
иzsh
.Я думаю, что вы получили ожидаемое поведение, в bash, каждая команда в конвейере выполняется в подоболочке. Вы можете убедиться, попытавшись изменить глобальную переменную вашей функции:
Кстати, возврат работает, но он возвращается из подоболочки. Опять вы можете проверить это:
Будет выводить следующее:
Так что оператор возврата правильно вышел из подоболочки
,
источник
foo(){ : | return 1 || return 2; echo$?; echo "This should not be printed.";}; foo; echo $?
и вы получите результат2
. Но для ясности я хотел бы сделатьreturn 1
БЭexit 1
.Более общий ответ заключается в том, что bash и некоторые другие оболочки обычно помещают все элементы конвейера в отдельные процессы. Это разумно, когда командная строка
поскольку программы все равно обычно запускаются в отдельных процессах (если вы не говорите ). Но это может стать сюрпризом для
exec program
где некоторые или все команды являются встроенными командами. Тривиальные примеры включают в себя:
Немного более реалистичный пример
где все
while
...do
...done
петля помещается в подпроцесс, и поэтому ее изменения вt
не видны основной оболочку после концов петли. И это именно то, что вы делаете - добавление вwhile
цикл, запуск цикла как подоболочки, а затем попытка возврата из подоболочки.источник