Возвращаемое значение в функции Bash

Я работаю с bash script, и я хочу выполнить функцию для печати возвращаемого значения:

function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}

Когда я выполняю fun2, он не печатает "34". Почему это так?

Ответ 1

Несмотря на то, что bash имеет оператор return, единственное, что вы можете указать с его помощью, это собственный статус exit функции (значение от 0 до 255, 0 означает "успех"). Так что return не то, что вы хотите.

Возможно, вы захотите преобразовать ваш оператор return оператор echo - таким образом, вывод вашей функции может быть захвачен с помощью фигурных скобок $(), что кажется именно тем, что вы хотите.

Вот пример:

function fun1(){
  echo 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}

Другой способ получить возвращаемое значение (если вы просто хотите вернуть целое число 0-255) - это $? ,

function fun1(){
  return 34
}

function fun2(){
  fun1
  local res=$?
  echo $res
}

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

Ответ 2

$(...) захватывает текст, отправленный в stdout командой, содержащейся внутри. return не выводится на stdout. $? содержит код результата последней команды.

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}

Ответ 3

Функции в Bash не являются функциями, как на другом языке; они на самом деле являются командами. Таким образом, функции используются так, как если бы они были двоичными файлами или сценариями, извлеченными из вашего пути. С точки зрения вашей логики программы не должно быть никакой разницы.

Команды оболочки подключаются по каналам (aka streams), а не к фундаментальным или определяемым пользователем типам данных, как в "реальных" языках программирования. Нет такой вещи, как возвращаемое значение для команды, возможно, главным образом потому, что нет реального способа объявить ее. Это может произойти на man-странице или выходе --help команды, но обе они только читаются человеком и, следовательно, записываются на ветер.

Когда команда хочет получить вход, она считывает ее из своего входного потока или списка аргументов. В обоих случаях текстовые строки должны быть проанализированы.

Когда команда хочет что-то вернуть, она должна echo передать ее в выходной поток. Другим часто практикуемым способом является сохранение возвращаемого значения в выделенных глобальных переменных. Запись в выходной поток является более четкой и гибкой, поскольку она может принимать двоичные данные. Например, вы можете легко вернуть BLOB:

encrypt() {
    gpg -c -o- $1 # encrypt data in filename to stdout (asks for a passphrase)
}

encrypt public.dat > private.dat # write function result to file

Как написано в этом потоке, вызывающий может также использовать подстановку команд $() для захвата вывода.

Параллельно, функция "вернет" код выхода gpg (GnuPG). Подумайте о коде выхода в качестве бонуса, которого не имеют другие языки, или, в зависимости от вашего темперамента, как "Шмутцефф" функций оболочки. Этот статус, по соглашению, равен 0 по успеху или целому числу в диапазоне 1-255 для чего-то еще. Для этого ясно: return (например, exit) может принимать только значение от 0 до 255, а значения, отличные от 0, не обязательно являются ошибками, как это часто утверждается.

Если вы не указываете явное значение с помощью return, статус берется из последней команды в команде Bash statement/function/command и т.д. Таким образом, всегда есть статус, а return - простой способ его предоставить.

Ответ 4

Оператор return задает код выхода функции, то же самое, что и exit будет делать для всего script.

Код завершения для последней команды всегда доступен в переменной $?.

function fun1(){
  return 34
}

function fun2(){
  local res=$(fun1)
  echo $? # <-- Always echos 0 since the 'local' command passes.

  res=$(fun1)
  echo $?  #<-- Outputs 34
}

Ответ 5

Проблема с другими ответами заключается в том, что они либо используют глобальный, который может быть перезаписан, когда несколько функций находятся в цепочке вызовов, либо echo, что означает, что ваша функция не может выводить диагностическую информацию (вы забудете, что ваша функция это делает, и "результат") т.е. возвращаемое значение будет содержать больше информации, чем ожидает ваш вызывающий объект, что приведет к странной ошибке), либо eval, который слишком тяжелый и хакерский.

Правильный способ сделать это - поместить в функцию материал верхнего уровня и использовать local с правилом динамической видимости bash. Пример:

func1() 
{
    ret_val=hi
}

func2()
{
    ret_val=bye
}

func3()
{
    local ret_val=nothing
    echo $ret_val
    func1
    echo $ret_val
    func2
    echo $ret_val
}

func3

Это выводит

nothing
hi
bye

Динамическая область видимости означает, что ret_val указывает на другой объект в зависимости от абонента! Это отличается от лексической области видимости, которая используется большинством языков программирования. На самом деле это документированная функция, ее просто не хватает, и она не очень хорошо объяснена, вот документация к ней (выделение мое):

Переменные, локальные для функции, могут быть объявлены с локальными встроенный. Эти переменные видны только функции и команды это вызывает.

Для кого-то с фоном C/C++/Python/Java/С#/javascript это, вероятно, самое большое препятствие: функции в bash не являются функциями, они являются командами и ведут себя как таковые: они могут выводить в stdout/stderr, они могут передавать/выводить, они могут возвращать код выхода. По сути, нет никакой разницы между определением команды в сценарии и созданием исполняемого файла, который можно вызывать из командной строки.

Поэтому вместо того, чтобы писать свой сценарий так:

top-level code 
bunch of functions
more top-level code

напишите это так:

# define your main, containing all top-level code
main() 
bunch of functions
# call main
main  

где main() объявляет ret_val как local, а все другие функции возвращают значения через ret_val.

Смотрите также следующие Unix & Вопрос по Linux: Область действия локальных переменных в функциях оболочки.

Другое, возможно, даже лучшее решение, в зависимости от ситуации, - это , опубликованное ya.teck, в котором используется local -n.

Ответ 6

Еще один способ добиться этого - ссылки на имена (требуется Bash 4. 3+).

function example {
  local -n VAR=$1
  VAR=foo
}

example RESULT
echo $RESULT

Ответ 7

Мне нравится делать следующее, если выполняется в script, где определена функция:

POINTER= # used for function return values

my_function() {
    # do stuff
    POINTER="my_function_return"
}

my_other_function() {
    # do stuff
    POINTER="my_other_function_return"
}

my_function
RESULT="$POINTER"

my_other_function
RESULT="$POINTER"

Мне это нравится, потому что я могу включить в свои функции выражения эха, если хочу

my_function() {
    echo "-> my_function()"
    # do stuff
    POINTER="my_function_return"
    echo "<- my_function. $POINTER"
}

Ответ 8

В качестве дополнения к отличным постам других, здесь статья, обобщающая эти методы:

  • установить глобальную переменную
  • установить глобальную переменную, имя которой вы передали функции
  • установить код возврата (и забрать его с помощью $?)
  • 'echo' некоторые данные (и забрать их с помощью MYVAR = $ (myfunction))

Возврат значений из функций Bash

Ответ 10

Git Bash в Windows с использованием массивов для multiple возвращаемых значений

BASH КОД:

#!/bin/bash

##A 6-element array used for returning
##values from functions:
declare -a RET_ARR
RET_ARR[0]="A"
RET_ARR[1]="B"
RET_ARR[2]="C"
RET_ARR[3]="D"
RET_ARR[4]="E"
RET_ARR[5]="F"


function FN_MULTIPLE_RETURN_VALUES(){

   ##give the positional arguments/inputs
   ##$1 and $2 some sensible names:
   local out_dex_1="$1" ##output index
   local out_dex_2="$2" ##output index

   ##Echo for debugging:
   echo "running: FN_MULTIPLE_RETURN_VALUES"

   ##Here: Calculate output values:
   local op_var_1="Hello"
   local op_var_2="World"

   ##set the return values:
   RET_ARR[ $out_dex_1 ]=$op_var_1
   RET_ARR[ $out_dex_2 ]=$op_var_2
}


echo "FN_MULTIPLE_RETURN_VALUES EXAMPLES:"
echo "-------------------------------------------"
fn="FN_MULTIPLE_RETURN_VALUES"
out_dex_a=0
out_dex_b=1
eval $fn $out_dex_a $out_dex_b  ##<--Call function
a=${RET_ARR[0]} && echo "RET_ARR[0]: $a "
b=${RET_ARR[1]} && echo "RET_ARR[1]: $b "
echo
##----------------------------------------------##
c="2"
d="3"
FN_MULTIPLE_RETURN_VALUES $c $d ##<--Call function
c_res=${RET_ARR[2]} && echo "RET_ARR[2]: $c_res "
d_res=${RET_ARR[3]} && echo "RET_ARR[3]: $d_res "
echo
##----------------------------------------------##
FN_MULTIPLE_RETURN_VALUES 4 5  ##<---Call function
e=${RET_ARR[4]} && echo "RET_ARR[4]: $e "
f=${RET_ARR[5]} && echo "RET_ARR[5]: $f "
echo
##----------------------------------------------##


read -p "Press Enter To Exit:"

ОЖИДАЕМЫЙ ВЫХОД:

FN_MULTIPLE_RETURN_VALUES EXAMPLES:
-------------------------------------------
running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[0]: Hello
RET_ARR[1]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[2]: Hello
RET_ARR[3]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[4]: Hello
RET_ARR[5]: World

Press Enter To Exit: