Неявный возврат в функции bash?

11

Скажем, у меня есть функция bash, например:

gmx(){
  echo "foo";
}

будет ли эта функция неявно возвращать выходное значение echoкоманды, или необходимо использовать return?

gmx(){
  echo "foo";
  return $?
}

Я предполагаю, что, как работает bash, состояние выхода последней команды функции bash - это то, которое возвращается, но не на 100% определено.

Александр Миллс
источник

Ответы:

10

returnвыполняет явный возврат из функции оболочки или «точечного сценария» (исходного сценария). Если returnне выполняется, неявный возврат выполняется в конце функции оболочки или точечного скрипта.

Если returnвыполняется без параметра, это эквивалентно возвращению статуса завершения последней выполненной команды.

Так returnработает во всех оболочках POSIX.

Например,

gmx () {
  echo 'foo'
  return "$?"
}

следовательно, эквивалентно

gmx () {
  echo 'foo'
  return
}

который так же, как

gmx () {
  echo 'foo'
}

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

Кусалананда
источник
2
Недостатком использования returnявляется то, что для функций, определенных как f() (...; cmd; return), это предотвращает оптимизацию, которую выполняют несколько оболочек cmdв том же процессе, что и подоболочка. Для многих оболочек это также означает, что состояние выхода функции не несет информацию, cmdкоторая была убита, когда она есть (большинство оболочек не могут получить эту информацию в любом случае).
Стефан Шазелас
1
Обратите внимание, что для pdksh и некоторых его производных (таких как OpenBSD shили posh) вам понадобится, return -- "$?"если есть вероятность, что последняя команда была функцией, возвращающей отрицательное число. mksh(также основанный на pdksh) запрещает функциям возвращать отрицательные значения.
Стефан Шазелас
спасибо, это помогает, наверное, я не понимаю, как return xфункционирует иначе, чем exit x... единственное, что я знаю, это то, return xчто не выйдет из текущего процесса.
Александр Миллс
@AlexanderMills Ну, это то, что я сказал: returnиспользуется для возврата из функции или точечного скрипта. exitделает что-то совершенно другое (завершает процесс).
Кусалананда
да, это имеет смысл, я думаю, что я начинаю лучше справляться с этим
Александр Миллс
7

Со bash(1)страницы руководства :

При выполнении состояние выхода функции является состоянием выхода последней команды, выполненной в теле.

Игнасио Васкес-Абрамс
источник
правильно, и следствием может быть то, что оператор return является не чем иным, как статусом выхода?
Александр Миллс
Я думаю, returnчто это встроенная команда - хотя return 1она отличается от других exit 1и так далее
Александр Миллс
1
msgstr "return [n]: Заставляет функцию прекратить выполнение и вернуть значение, указанное в n, вызывающей стороне. Если n опущено, возвращается статус последней команды, выполненной в теле функции." (ibid) Таким образом, returnпринудительно устанавливает состояние выхода функции в конкретное значение, если оно указано.
Игнасио Васкес-Абрамс
1
@AlexandwrMills Да, returnи exitоба являются встроенными, за исключением того, что returnмогут использоваться только внутри функции. Вы не можете завершить сценарий с return. Состояние выхода - это значение, которое возвращает команда. returnэто команда, которая возвращает это значение. Так что «заявление о возврате - не более, чем статус выхода», просто не совсем точно. Один - это значение, другой - команда плюс значение.
Сергей Колодяжный
1
@AlexanderMills, returnвозвращает из функции, exitвыходит из всей оболочки. Это точно так же, как, скажем, в C с returnvs. exit(n)или returnпротив sys.exit()в Python.
ilkkachu
2

Я просто добавлю несколько предостережений к уже предоставленным ответам:

  • Несмотря на то, что она returnимеет особое значение для оболочки, с синтаксической точки зрения это встроенная команда оболочки, и оператор return анализируется, как и любая другая простая команда. Таким образом, это означает, что, как в аргументе любой другой команды, без $?кавычек, будет подвергаться split + glob

    Поэтому вам нужно процитировать это, $?чтобы избежать этого:

    return "$?"
  • returnкак правило , не принимает никаких опций ( ksh93«s принимает обычный --help, --man, --author... хотя). Единственный ожидаемый аргумент (необязательный) - это код возврата. Диапазон принятых кодов возврата варьируется от оболочки к оболочке, и то, правильно ли отражено любое значение вне 0..255, $?также зависит от оболочки к оболочке. См. Код завершения по умолчанию, когда процесс завершается? для деталей об этом.

    Большинство оболочек принимают отрицательные числа ( в конце концов, аргумент передается _exit()/ exitgroup()системным вызовом является int, поэтому со значениями , охватывающими , по меньшей мере -2 31 до 2 31 -1, так что имеет смысл только в том , что раковины принимают один и тот же диапазон для своих функций) ,

    Большинство оболочек используют waitpid()и. API для извлечения этого состояния выхода, однако в этом случае он усекается до числа от 0 до 255 при сохранении в $?. Даже несмотря на то, что для вызова функции не требуется порождать процесс и использовать его waitpid()для извлечения его состояния завершения, поскольку все выполняется в одном и том же процессе, многие оболочки также имитируют это waitpid()поведение при вызове функций. Это означает, что даже если вы позвоните returnс отрицательным значением, $?будет содержать положительное число.

    Теперь, среди тех оболочек, которые returnпринимают отрицательные числа (ksh88, ksh93, bash, zsh, pdksh и производные, отличные от mksh, yash), есть несколько (pdksh и yash), которым нужно это записать так, return -- -123как иначе, -123которое принимается за три -1, -2, -3неверные параметры.

    Поскольку pdksh и его производные (например, OpenBSD shили posh) сохраняют отрицательное число в $?, это означает, что выполнение не return "$?"будет выполнено , если оно $?содержит отрицательное число (что произойдет, когда последняя команда запуска была функцией, которая возвратила отрицательное число).

    Так return -- "$?"было бы лучше в этих снарядах. Однако обратите внимание, что хотя синтаксис поддерживается большинством оболочек, он не является POSIX и на практике не поддерживается производными mkshи ash.

    Итак, чтобы подвести итог, с оболочками на основе pdksh, вы можете использовать отрицательные числа в аргументах функций, но если вы это сделаете, return "$@"не будет работать. В других оболочках return "$@"будет работать, и вы должны избегать использования отрицательных чисел (или чисел за пределами 0..255) в качестве аргументов return.

  • Во всех известных мне оболочках вызов returnизнутри подоболочки, выполняемой внутри функции, приведет к выходу подоболочки (с указанным состоянием выхода, если какая-либо из команд последней команды выполнялась), но не вызовет возврата из функции ( мне неясно, дает ли вам POSIX такую ​​гарантию, некоторые утверждают, что exitследует использовать вместо выходных субоболочек внутри функций). Например

    f() {
      (return 3)
      echo "still inside f. Exit status: $?"
    }
    f
    echo "f exit status: $?"
    

    будет выводить:

    still inside f. Exit status: 3
    f exit status: 0
    
Стефан Шазелас
источник
0

Да, неявное возвращаемое значение функции - это состояние выхода последней выполненной команды. Это также верно в любой точке любого сценария оболочки. В любой точке последовательности выполнения скрипта текущее состояние выхода является состоянием выхода последней выполненной команды. Даже команда , выполняемая в рамках присваивание переменной: var=$(exit 34). Разница с функциями заключается в том, что функция может изменить состояние выхода в конце выполнения функции.

Альтернативный способ изменить «текущий статус выхода» - запустить вспомогательную оболочку и выйти из нее с любым необходимым статусом выхода:

$ $(exit 34)
$ echo "$?"
34

И да, расширение статуса выхода нужно заключать в кавычки:

$ IFS='123'
$ $(exit 34)
$ echo $?
4

А (exit 34)также работа.
Некоторые могут возразить, что более надежная конструкция должна быть $(return 34)и что выход должен «выйти» из выполняемого сценария. Но $(return 34)не работает ни с одной версией bash. Так что это не портативно.

Самый безопасный способ установить статус выхода - использовать его так, как он был разработан для работы, определения и returnиспользования функции:

exitstatus(){ return "${1:-"$?"}"; }

Итак, в конце функции. это абсолютно равносильно тому, чтобы иметь ничего или returnили return "$?". Конец функции не обязательно должен означать «последнюю строку кода функции».

#!/bin/sh
exitstatus(){ a="${1:-"$?"}"; return "$a"; }
gmx(){
    if     [ "$1" = "one" ]; then
           printf 'foo ';
           exitstatus 78
           return "$?"
    elif   [ "$1" = "two" ]; then
           printf 'baz ';
           exitstatus 89
           return
    else
           printf 'baz ';
           exitstatus 90
    fi
}  

Распечатает:

$ ./script
foo 78
baz 89
baz 90

Единственное практическое применение для "$?"- это либо напечатать его значение: echo "$?"либо сохранить его в переменной (так как это эфемерное значение и изменяться при каждой выполненной команде): exitstatus=$?(не забывайте заключать переменную в кавычки, например export EXITSTATUS="$?".

В returnкоманде допустимый диапазон значений обычно составляет от 0 до 255, но следует понимать, что значения 126 + nиспользуются некоторыми оболочками для сигнализации о специальном состоянии выхода, поэтому общая рекомендация - использовать 0-125.

Исаак
источник