Я работаю с bash script, и я хочу выполнить функцию для печати возвращаемого значения:
function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}
Когда я выполняю fun2, он не печатает "34". Почему это так?
Я работаю с bash script, и я хочу выполнить функцию для печати возвращаемого значения:
function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}
Когда я выполняю fun2, он не печатает "34". Почему это так?
 Несмотря на то, что 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. Возвращаемым значением по умолчанию является выходное значение последнего оператора, выполненного в функции.
 $(...) захватывает текст, отправленный в stdout командой, содержащейся внутри. return не выводится на stdout. $? содержит код результата последней команды.
fun1 (){
  return 34
}
fun2 (){
  fun1
  local res=$?
  echo $res
}
Функции в 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 - простой способ его предоставить.
Оператор 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
}
Проблема с другими ответами заключается в том, что они либо используют глобальный, который может быть перезаписан, когда несколько функций находятся в цепочке вызовов, либо 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.
Еще один способ добиться этого - ссылки на имена (требуется Bash 4. 3+).
function example {
  local -n VAR=$1
  VAR=foo
}
example RESULT
echo $RESULT
Мне нравится делать следующее, если выполняется в 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"
}
В качестве дополнения к отличным постам других, здесь статья, обобщающая эти методы:
bash, чтобы возвращать объекты скалярного и массива:
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: