Valor de retorno en una función de Bash

Estoy trabajando con un script bash y quiero ejecutar una función para imprimir un valor de retorno:

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

Cuando ejecuto fun2 , no imprime “34”. ¿Por qué es este el caso?

    Aunque bash tiene una instrucción return , lo único que puede especificar con ella es el estado de exit la función (un valor entre 0 y 255 , 0 que significa “éxito”). Entonces, el return no es lo que quieres.

    Es posible que desee convertir su statement de return en una statement de echo ; de ese modo, su salida de función podría capturarse utilizando llaves $() , que parece ser exactamente lo que quiere.

    Aquí hay un ejemplo:

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

    Otra forma de obtener el valor de retorno (si solo desea devolver un número entero de 0 a 255) es $? .

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

    Además, tenga en cuenta que puede usar el valor de retorno para usar lógica booleana como fun1 || fun2 fun1 || fun2 solo ejecutará fun2 si fun1 devuelve un valor de 0 . El valor de retorno predeterminado es el valor de salida de la última instrucción ejecutada dentro de la función.

    $(...) captura el texto enviado a stdout por el comando contenido dentro. return no da salida a stdout. $? contiene el código de resultado del último comando.

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

    Las funciones en Bash no son funciones como en otro idioma; en realidad son comandos. Por lo tanto, las funciones se utilizan como si fueran binarios o scripts recuperados de su ruta de acceso. Desde la perspectiva de la lógica de tu progtwig, realmente no debería haber diferencia.

    Los comandos de Shell están conectados por tuberías (también conocidas como streams), y no por tipos de datos fundamentales o definidos por el usuario, como en los lenguajes de progtwigción “reales”. No hay tal cosa como un valor de retorno para un comando, tal vez sobre todo porque no hay una forma real de declararlo. Podría ocurrir en la página de manual o en la salida de --help del comando, pero ambas son solo legibles por el ser humano y, por lo tanto, se escriben en el viento.

    Cuando un comando quiere obtener una entrada, la lee de su flujo de entrada o de la lista de argumentos. En ambos casos, las cadenas de texto deben ser analizadas.

    Cuando un comando quiere devolver algo, tiene que echo en su flujo de salida. Otra forma frecuentemente practicada es almacenar el valor de retorno en variables globales dedicadas. Escribir en el flujo de salida es más claro y más flexible, ya que también puede tomar datos binarios. Por ejemplo, puede devolver un BLOB fácilmente:

     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 

    Como otros han escrito en este hilo, el que llama también puede usar la sustitución de comando $() para capturar el resultado.

    Paralelamente, la función “devolverá” el código de salida de gpg (GnuPG). Piense en el código de salida como un bono que otros idiomas no tienen, o, dependiendo de su temperamento, como un “Schmutzeffekt” de las funciones de shell. Este estado es, por convención, 0 en caso de éxito o un número entero en el rango 1-255 para otra cosa. Para dejar esto en claro: return (como exit ) solo puede tomar un valor de 0-255, y los valores distintos de 0 no son necesariamente errores, como suele afirmarse.

    Cuando no se proporciona un valor explícito con return el estado se toma del último comando en una instrucción / función / comando de Bash, y así sucesivamente. Entonces siempre hay un estado, y el return es solo una manera fácil de proporcionarlo.

    La statement de return establece el código de salida de la función, más o menos lo mismo que la exit para todo el script.

    El código de salida para el último comando siempre está disponible en $? variable.

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

    Me gusta hacer lo siguiente si se ejecuta en un script donde se define la función:

     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" 

    Me gusta esto, porque puedo incluir declaraciones de eco en mis funciones si quiero

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

    Como un complemento a las publicaciones excelentes de otros, aquí hay un artículo que resume estas técnicas:

    • establecer una variable global
    • establecer una variable global, cuyo nombre pasaste a la función
    • establecer el código de retorno (y recogerlo con $?)
    • ‘echo’ algunos datos (y retíralos con MYVAR = $ (myfunction))

    Devolución de valores de Bash Functions

    El problema con otras respuestas es que utilizan un global, que puede sobrescribirse cuando varias funciones están en una cadena de llamadas, o echo, lo que significa que su función no puede generar información de diagnóstico (se olvidará que su función hace esto y el “resultado”, es decir valor de retorno, contendrá más información de la que espera la persona que llama, lo que lleva a errores extraños), o eval que es muy pesado y hacky.

    La forma correcta de hacer esto es poner las cosas de nivel superior en una función y usar una regla de scope dynamic local de bash. Ejemplo:

     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 

    Estas salidas

     nothing hi bye 

    El scope dynamic significa que ret_val apunta a un objeto diferente según la persona que llama. Esto es diferente del scope léxico, que es lo que utilizan la mayoría de los lenguajes de progtwigción. Esta es en realidad una característica documentada , fácil de perder, y no muy bien explicada, aquí están los documentos (el énfasis es mío):

    Las variables locales a la función se pueden declarar con el builtin local. Estas variables son visibles solo para la función y los comandos que invoca .

    Para alguien con un fondo C / C ++ / Python / Java / C # / javascript, este es probablemente el mayor obstáculo: las funciones en bash no son funciones, son comandos y se comportan como tales: pueden dar salida a stdout / stderr, pueden entrada / salida de tuberías, pueden devolver un código de salida. Básicamente, no hay diferencia entre definir un comando en un script y crear un ejecutable al que se pueda llamar desde el comando.

    Entonces, en lugar de escribir tu script de esta manera:

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

    escríbelo así:

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

    donde main () declara que ret_val es local y todas las demás funciones devuelven valores a través de ret_val.

    Ver también https://unix.stackexchange.com/questions/282557/scope-of-local-variables-in-shell-functions .

    Git Bash en Windows usando matrices para valores de retorno múltiples

    CÓDIGO DE 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:" 

    RENDIMIENTO ESPERADO:

     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: