Я работаю с 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: