Повернути значення в функцію Bash

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

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

Коли я виконую fun2 , він не друкує "34". Чому це так?

172
return у вашому випадку є, по суті, таким самим, як код виходу , який коливається від 0 - 255 . Використовуйте echo , як це пропонується @septi. Коди виходу можна зафіксувати за допомогою $? .
додано Автор devnull, джерело
Належним способом це зробити, щоб вміст найвищого рівня виконувався у функції, і використовувати місцеве правило з динамічним визначенням баху. Я створив відповідь, щоб продемонструвати, це не відома функція, але одна повністю підтримується.
додано Автор Oliver, джерело
У цьому випадку набагато гнучкіше використовувати echo в fun1. Це ідея unix-програмування: echo відсилає результати до стандартного виводу, який потім може бути використаний іншими функціями з res = $ (fun1) - або безпосередньо підключити до інших функцій: function a() (echo 34; } функція b() {при читанні даних; робити echo $ data; done;} a | b
додано Автор Arne Babenhauserheide, джерело

8 Відповіді

Хоча 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 буде запускатися лише fun2 , якщо fun1 повертає значення 0 . За замовчуванням повертається значення - значення виходу останнього виразу, виконаного в межах функції.

234
додано
Чому б не використовувати $? ?
додано Автор Pithikos, джерело
Ні, мені потрібна чертова повернення значення . До пекла з ехом
додано Автор Tomáš Zato, джерело
@Blauhirn у цьому середовищі, з такою конструкцією || , код виходу 0 вважається успішним і тому "true". Не-нуль - це помилка, і тому помилкова. Подумайте про fun1 || fun2 , як скорочення для "якщо fun1 повертає успіх або fun2 повертає успіх", а не оператор фактично повертає значення.
додано Автор meowsqueak, джерело
@ Блаухірн так, я думаю, ти правий. Або це має бути "ненульовим значенням", як ви кажете, або оператор повинен мати && не || . Мій попередній коментар був невірним.
додано Автор meowsqueak, джерело
Спасибі, але я хочу використовувати return.How я можу отримати значення? В цьому випадку
додано Автор mindia, джерело
fun1 || Функція fun2 буде запускатися тільки fun2, якщо fun1 повертає значення 0. не повинен бути a non-0 ?
додано Автор Blauhirn, джерело
@Москвак, вибачте, я цього не розумію. , якщо fun1 повертає успіх або fun2 повертає успіх . Отже, якщо fun1 вже повернув успіх, fun2 НЕ буде виконано! правильно? таким чином, fun1 потрібно повернути NON-0-value для fun2 для запуску.
додано Автор Blauhirn, джерело
+1 , коли не пропонується прямо $? .
додано Автор devnull, джерело
Належним способом це зробити, щоб вміст найвищого рівня виконувався у функції, і використовувати місцеве правило з динамічним визначенням баху. Я створив відповідь, щоб продемонструвати, це не відома функція, але одна повністю підтримується.
додано Автор Oliver, джерело
Що дратує те, що функція, яка повинна надавати дані, також не може відлунювати інші елементи до stdout, тому що абонент, який використовує $ (), теж отримуватиме таке повідомлення і буде сплутатись чи потрібно буде проаналізувати вихід. Глобальні змінні не великі, тому що це просто питання часу, перш ніж використовувати ту саму глобальну вартість у двох місцях, які можуть бути вкладені, і дані можуть втратитись. Там повинні бути окремі канали для друку даних проти передачі даних назад.
додано Автор Oliver, джерело
Вам потрібно виконати fun1 , а потім повернути значення зберігається в $? . Хоча я і не рекомендую це робити ...
додано Автор tamasgal, джерело

$(...) captures the text sent to stdout by the command contained within. return does not output to stdout. $? contains the result code of the last command.

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}
50
додано
і return - це код result ?
додано Автор Blauhirn, джерело
Так, return використовується для встановлення $? , який є exit status . У наведеному вище прикладі fun1 exit status буде 34 . Також зауважте, що $ (...) також захоплює stderr крім stdout з вказаної команди.
додано Автор swoop81, джерело

Функції в Bash не є функціями, подібними іншою мовою; вони насправді наказують. Отже, функції використовуються так, ніби вони були бінарними або скриптами, отриманими з вашого шляху. З точки зору логіки вашої програми, дійсно не повинно бути різниці.

Команди оболонки з'єднуються трубами (ака потоками), а не основними або користувацькими типами даних, як з "реальними" мовами програмування. Такої речі, як зворотне значення для команди, немає, можливо, головним чином тому, що немає реального способу оголосити його. Це може виникнути на сторінці man-page або на виході команди -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

Як інші пишуть у цій гілці, абонент може також використовувати команду substitution $() для захоплення виводу.

Паралельно ця функція "повертає" код виходу gpg (GnuPG). Подумайте про код виходу як бонус, який інші мови не мають, або, залежно від вашого темпераменту, як "Schmutzeffekt" функцій оболонки. Цей статус, за умовчанням, 0 для успіху або ціле число в діапазоні 1-255 для чогось іншого. Щоб зробити це ясним: return (наприклад, exit ) може мати значення лише від 0 до 255, а значення, відмінні від 0, не обов'язково є помилками, як це часто доводиться.

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

31
додано
+1 для пояснення функцій і команд і як це впливає на поняття передачі даних назад абоненту
додано Автор Oliver, джерело

Вислів return встановлює код виходу функції, так само, як і для exit для всього сценарію.

Код виходу для останньої команди завжди доступний в змінній $? .

function fun1(){
  return 34
}

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

  res=$(fun1)
  echo $?  #<-- Outputs 34
}
20
додано

Мені подобається робити наступне, якщо працює в сценарії, де визначено цю функцію:

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"

Мені це подобається, тому що я можу включити в мої функції функції echo, якщо я хочу

my_function() {
    echo "-> my_function()"
    # do stuff
    POINTER="my_function_return"
    echo "<- my_function. $POINTER"
}
4
додано

Як доповнення до чудових публікацій, ось стаття, яка підсумовує ці методи:

  • встановити глобальну змінну
  • встановити глобальну змінну, назва якої ви перейшли до функції
  • встановіть код повернення (і знайдіть його за допомогою $?)
  • "відлучити" деякі дані (і забрати його з MYVAR = $ (myfunction))

Повернення значень з Bash-функцій

1
додано

Git Bash on Windows using arrays for multiple return values

BASH CODE:

#!/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:
1
додано

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

Належним способом це зробити, щоб вміст найвищого рівня виконувався у функції, і використовувати місцеве правило з динамічним визначенням баху. Приклад:

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/C #/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 як локальну, а всі інші функції повертають значення через ret_val.

Див. Також https://unix.stackexchange.com/questions/282557/ обсяги локальних змінних-в-оболонки-функції .

0
додано