Разница между возвратом и выходом в функциях Bash

429

В чем разница между оператором returnand exitв функциях Bash относительно кодов выхода?

lecodesportif
источник
55
Подсказка: введите help <command>вашу оболочку, чтобы получить информацию о том, что будет делать встроенная оболочка. В вашем случае help returnиhelp exit
SiegeX

Ответы:

318

С man bashна return [n];

Заставляет функцию прекратить выполнение и вернуть значение, указанное в n, вызывающей стороне. Если n опущено, возвращается статус последней команды, выполненной в теле функции.

... на exit [n]:

Заставить оболочку выйти со статусом n. Если n опущено, состояние выхода соответствует статусу последней выполненной команды. Ловушка EXIT выполняется перед завершением оболочки.

РЕДАКТИРОВАТЬ:

Согласно вашему редактированию вопроса, относительно кодов выхода, не returnимеет ничего общего с кодами выхода. Коды выхода предназначены для приложений / скриптов , а не функций. Таким образом, в этом отношении единственным ключевым словом, которое устанавливает код завершения скрипта (то, которое может быть перехвачено вызывающей программой с помощью $?переменной оболочки), является exit.

РЕДАКТИРОВАТЬ 2:

Мое последнее обращение со ссылкой exitвызывает некоторые комментарии. Он был сделан для того, чтобы различать returnи exitпонимать ОП, и фактически в любой точке скрипта программы / оболочки exitэто единственный способ завершить скрипт кодом выхода в вызывающий процесс.

Каждая команда выполняется в оболочке производит локальный «код выхода»: она устанавливает $?переменную к этому коду, и может быть использована с if, &&и другими операторами условно выполнять другие команды.

Эти коды выхода (и значение $?переменной) сбрасываются при каждом выполнении команды.

Между прочим, код выхода последней команды, выполняемой сценарием, используется в качестве кода выхода самого сценария, видимого вызывающим процессом.

Наконец, функции при вызове действуют как команды оболочки относительно кодов выхода. Код выхода функции ( внутри функции) устанавливается с помощью return. Поэтому, когда в функции return 0выполняется, выполнение функции прекращается, давая код выхода 0.

Диего Севилья
источник
10
Не совсем. Он всегда возвращает значение из текущей оболочки. Неважно, находитесь ли вы внутри функции или нет.
Диего Севилья
10
Прокомментируйте ваши изменения: я могу путать значения возврата и коды выхода, но func(){ return 50; };func;echo $?повторяет 50. Таким образом, $?переменная оболочки, похоже, не ограничена exit.
lecodesportif
8
« $?Расширяется до состояния выхода последнего выполненного переднего конвейера». Этот выход может быть из оболочки в форме вызова exit(или попадания в конец скрипта) или в форме вызова returnвнутри функции.
SiegeX
11
@lecodesportif: $? Текущий процесс / сценарий ограничен exitили последним результатом команды, выполненной этим сценарием. Итак, если ваша последняя строка скрипта является вызовом этой функции, и эта функция возвращает 50, да, то, $?что вы производите для вызывающего вас процесса, равно 50. Однако это не имеет отношения к return, потому что это ограничено текущим сценарием. Возвращается только в том случае, если этот вызов функции является последним предложением скрипта. exitОднако всегда завершайте сценарий и возвращайте это значение как $? вызывающему процессу .
Диего Севилья
11
-1 за то, что я перепутал со строкой «не returnимеет никакого отношения к кодам выхода». Эксперимент показывает, что между кодом возврата функции и кодом выхода скрипта нет функциональной разницы.
Джек,
296

returnприведет к тому, что текущая функция выйдет из области видимости, а exitсценарий завершится в том месте, где она вызывается. Вот пример программы, чтобы помочь объяснить это:

#!/bin/bash

retfunc()
{
    echo "this is retfunc()"
    return 1
}

exitfunc()
{
    echo "this is exitfunc()"
    exit 1
}

retfunc
echo "We are still here"
exitfunc
echo "We will never see this"

Вывод

$ ./test.sh
this is retfunc()
We are still here
this is exitfunc()
SiegeX
источник
17
Хороший пример. Вы также можете показать значение выхода 1 в $?.
Диего Севилья
48
Обратите внимание, что эта функция НЕ будет печатать «Мы все еще здесь», если вы добавите «set -e» перед вызовом «retfunc».
Майкл
4
Тем не менее, echo fnord | while read x; do exitfunc; done; echo "still here"будет печатать «все еще здесь». Похоже, что whileв этом сценарии происходит выход только из суб-оболочки.
tripleee
Примерный обходной путь, done || exit $?но это некрасиво и не совсем эквивалентно.
tripleee
2
+1 Может быть полезно добавить: `` ` returnприведет к тому, что текущая функция или исходный скрипт выйдут из области видимости```.
Исаак
56

Я не думаю, что кто-то действительно полностью ответил на вопрос, потому что они не описывают, как они используются. Хорошо, я думаю, что мы знаем, что выход убивает скрипт, где бы он ни вызывался, и вы также можете присвоить ему статус, такой как выход или выход 0 или выход 7 и так далее. Это может быть использовано для определения того, как сценарий был принудительно остановлен, если вызван другим сценарием и т. Д. Достаточно при выходе.

return при вызове вернет указанное значение, чтобы указать поведение функции, обычно 1 или 0. Например:

    #!/bin/bash
    isdirectory() {
      if [ -d "$1" ]
      then
        return 0
      else
        return 1
      fi
    echo "you will not see anything after the return like this text"
    }

проверьте вот так:

    if isdirectory $1; then echo "is directory"; else echo "not a directory"; fi

или вот так:

    isdirectory || echo "not a directory"

В этом примере тест может использоваться, чтобы указать, был ли найден каталог. обратите внимание, что что-либо после возврата не будет выполнено в функции. 0 верно, но false равно 1 в оболочке, в отличие от других прог-языков.

Для получения дополнительной информации о функциях: http://www.linuxjournal.com/content/return-values-bash-functions

ПРИМЕЧАНИЕ. Функция isdirectory предназначена только для ознакомительных целей. Это не должно быть то, как вы выполняете такую ​​опцию в реальном скрипте.

Майк К
источник
3
Или просто использовать test -d $1для достижения того же результата. Никогда не делай if <check> return else return. <check>один будет делать то же самое на всех языках, которые я знаю, по крайней мере.
erikbwork
4
Чтобы быть еще более ясным в отношении того, что говорит Эрик: он isdirectory() { [ -d "$1" ]; }будет вести себя точно так же, как и здесь: возвращаемое по умолчанию значение функции оболочки, достигая конца ее кода или returnаргумента без аргументов, - это значение самая последняя команда.
Чарльз Даффи
11
Другие комментаторы здесь критикуют стиль примера Майка Кью, когда он действительно говорит о поведении returnутверждения. Это правда, что его пример является упрощенным и не должен использоваться в производстве. Но это просто, так что он отлично справляется со своей задачей. Ничего плохого в этом нет.
Майк С,
Спасибо, Майк С., да, я согласен, что самый простой пример лучше всего объясняет выход против возврата. Другие комментарии, безусловно, действительны, и их следует учитывать для более продвинутых bash-кодеров ;-)
Mike Q
1
Кто-то может сказать, что он не совсем связан с вопросом, но он связан с проблемой, с которой я столкнулся, и ответил на нее, что привело меня к поиску этого вопроса. :-)
Джесси Стил
33

Помните, что функции являются внутренними для скрипта и обычно возвращаются из того места, откуда они были вызваны с помощью оператора return. Вызов внешнего сценария - это совсем другое дело, и сценарии обычно заканчиваются оператором выхода.

Разница «между оператором возврата и выхода в функциях BASH относительно кодов выхода» очень мала. Оба возвращают статус, а не значения как таковые. Нулевое состояние указывает на успех, а любое другое состояние (от 1 до 255) указывает на сбой. Оператор return вернется в сценарий с того места, где он был вызван, а оператор выхода завершит весь сценарий, где бы он ни встречался.

return 0  # returns to where the function was called.  $? contains 0 (success).

return 1  # returns to where the function was called.  $? contains 1 (failure).

exit 0  # exits the script completely.  $? contains 0 (success).

exit 1  # exits the script completely.  $? contains 1 (failure).

Если ваша функция просто заканчивается без оператора возврата, статус последней выполненной команды возвращается как код состояния (и будет помещен в $?).

Помните, что для возврата и выхода необходимо вернуть код состояния от 0 до 255, доступный в $? . Вы не можете вставить что-либо еще в код состояния (например, вернуть «кот»); он не будет работать. Но скрипт может передать 255 различных причин сбоя с помощью кодов состояния.

Вы можете устанавливать переменные, содержащиеся в вызывающем скрипте, или выводить результаты в функции и использовать подстановку команд в вызывающем скрипте; но цель возврата и выхода состоит в том, чтобы передать коды состояния, а не значения или результаты вычислений, как можно было бы ожидать в языке программирования, таком как C.

user2100135
источник
25

Иногда вы запускаете скрипт, используя .или source.

. a.sh

Если вы включите exitв a.sh, он не только завершит работу сценария, но и завершит сеанс оболочки.

Если вы включите returnв a.sh, он просто прекратит обработку скрипта.

Юрай
источник
1
Но когда я просто запускаю a.sh, я получаю сообщение об ошибке return: can only 'return' from a function or sourced script, что делает его непригодным для общего сценария.
Питер - Восстановить Монику
На верхнем уровне сценария ни один из них не подходит в allситуациях. Использование .или sourceзапуск сценария в текущей оболочке, а не создание вложенной оболочки. Сценарий должен знать, как его использовать. Горе тому, кто делает это наоборот. Лично я рекомендую прочитать сценарии, прежде чем запускать их в первый раз.
Джесси Чисхолм
3
Удивительный трюк, с которым я столкнулся, состоит в том, чтобы использовать trapфункцию для, ERR EXITа затем сначала сохранить код завершения неудачной команды, errCode=$?а затем выйти из сценария (с источником или нет), return $errCode || exit $errCodeиспользуя ||средство «если я не могу вернуться, потому что я не был получен» просто выйдите вместо ".
dragon788
6

Простыми словами (в основном для новичков в кодировании), мы можем сказать,

`return` : exits the function,
`exit()` : exits the program(called as process while running)

Также, если вы заметили, это очень просто, но ...,

`return` : is the keyword
`exit()` : is the function
Jyo The Whiff
источник
1
В bash-скрипте exitэто не более или менее функция, чем return. Это встроенные команды. Они даже не зарезервированные слова.
Питер - Восстановить Монику
6
  • exitпрекратить текущий процесс ; с кодом выхода или без него, считайте, что это система, а не программная функция. Обратите внимание, что при поиске exitбудет завершена оболочка, однако при запуске останется только exitскрипт.

  • returnиз функции вернитесь к инструкции после вызова с кодом возврата или без него. returnявляется необязательным и неявным в конце функции. returnможет использоваться только внутри функции.

Я хочу добавить, что, несмотря на то, что exitсценарий получен изнутри, нелегко выполнить функцию без уничтожения оболочки. Я думаю, пример лучше на «тестовом» скрипте

#!/bin/bash
function die(){
   echo ${1:=Something terrible wrong happen}
   #... clean your trash
   exit 1
}

[ -f /whatever/ ] || die "whatever is not available"
# now we can proceed
echo "continue"

делать следующее:

user$ ./test
Whatever is not available
user$

test и оболочка закроется.

user$ . ./test
Whatever is not available

только test закончится и подсказка покажет.

Решение состоит в том, чтобы заключить потенциально процедуру (и)

#!/bin/bash
function die(){
   echo $(1:=Something terrible wrong happen)
   #... clean your trash
   exit 1
}

( # added        
    [ -f /whatever/ ] || die "whatever is not available"
    # now we can proceed
    echo "continue"
) # added

теперь в обоих случаях только testвыйдет.

ТСМ
источник
Добавление (и )помещает этот блок в под-оболочку, фактически не выполняя команду .(источник), как если бы вы обычно выполняли тестовый скрипт, который находится в под-оболочке. Если сценарий не запущен .или у sourceвас фактически есть 2 вложенных оболочки.
Джесси Чисхолм
3

Вопрос ОП: В чем разница между оператором возврата и выхода в функциях BASH относительно кодов выхода?

Во-первых, требуется пояснение:

  • Оператор (return | exit) не требуется для прекращения выполнения (function | shell). Функция (функция | shell) завершится, когда достигнет конца списка кодов, даже без оператора (return | exit).
  • Оператор (return | exit) не требуется для передачи значения обратно из завершенного (function | shell). Каждый процесс имеет встроенную переменную $? который всегда имеет числовое значение. Это специальная переменная, которая не может быть установлена ​​как «? = 1», но устанавливается только специальными способами (см. Ниже *). Значение $? после последней команды, которая должна быть выполнена в (вызываемой функции | вложенной оболочке) - значение, которое передается обратно (вызывающая функция | родительская оболочка). Это верно независимо от того, является ли последняя выполненная команда («return [n]» | «exit [n]»)) или простой («return», или что-то еще, что оказывается последней командой в коде вызываемых функций).

В приведенном выше списке маркеров выберите из «(x | y)» либо всегда первый элемент, либо всегда второй элемент, чтобы получить инструкции о функциях & return или shells & exit соответственно.

Что ясно, так это то, что они оба часто используют специальную переменную $? передать значения вверх после того, как они заканчиваются.

* Теперь о специальных способах, которые $? можно установить:

  • Когда вызываемая функция завершается и возвращается к своему вызывающему, тогда $? в звонилке будет равно итоговое значение $? в завершенной функции.
  • Когда родительская оболочка имплицитно или явно ожидает единственную вложенную оболочку и освобождается по окончании этой вспомогательной оболочки, тогда $? в родительской оболочке будет равно итоговое значение $? в завершенном субоболочке.
  • Некоторые встроенные функции могут изменять $? в зависимости от их результата. Но некоторые этого не делают.
  • Встроенные функции «возврат» и «выход», когда после числового аргумента указывается $? с аргументом и прекратить выполнение.

Стоит отметить, что $? можно присвоить значение, вызвав exit в вложенной оболочке, например так:

# (exit 259)
# echo $?
3  
Крейг Хикс
источник
4
В случае, если некоторые пропустили это, exit 259выводится как, 3потому что конечное значение выхода - один байт. 259 % 256 = 3
Джесси Чисхолм
0

Прежде всего, returnэто ключевое слово, а exitмой друг - это функция.

Тем не менее, вот простейшие объяснения.

return Возвращает значение из функции.

exit Он выходит из текущей оболочки или покидает ее.

Ахмад Авайс
источник
На самом деле, нет! Вы логически не правы. Выход - это функция, а returnключевое слово. Возврат - это гораздо больше, чем просто коды выхода, поэтому сравнение несправедливо.
Ахмад
Я изменил его, чтобы прояснить суть, которую я пытался прояснить. Спасибо за помощь в этом.
Ахмад
4
Ни , exitни returnв «ключевые слова», или, как Баш ручными называет их, «зарезервированные слова». Ни один из них не является «функцией», в смысле функции bash. Обе команды являются встроенными, в bash lingo. (Там является функция C стандартной библиотеки называется exit(), и язык программирования Си имеет зарезервированное слово return, но те , которые не следует путать с командами Баша, даже если их семантика удивительно похожа.)
Питер - восстановит Монику