Valor de retorno en una función Bash

Resuelto mindia asked hace 11 años • 12 respuestas

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?

mindia avatar Jun 27 '13 14:06 mindia
Aceptado

Aunque Bash tiene una returndeclaración, lo único que puede especificar con ella es el exitestado de la función (un valor entre 0y 255, 0 significa "éxito"). Entonces returnno es lo que quieres.

Es posible que desee convertir su returndeclaración en una echodeclaración; de esa manera, la salida de su función podría capturarse usando $()llaves, que parece ser exactamente lo que desea.

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 entre 0 y 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 la lógica booleana; me gusta fun1 || fun2solo se ejecutará fun2si fun1devuelve un 0valor que no es. El valor de retorno predeterminado es el valor de salida de la última declaración ejecutada dentro de la función.

tamasgal avatar Jun 27 '2013 07:06 tamasgal

Las funciones en Bash no son funciones como en otros lenguajes; en realidad son comandos. Por lo tanto, las funciones se utilizan como si fueran archivos binarios o scripts obtenidos de su ruta. Desde la perspectiva de la lógica de su programa, realmente no debería haber ninguna diferencia.

Los comandos de Shell están conectados por tuberías (también conocidas como secuencias) y no por tipos de datos fundamentales o definidos por el usuario, como en los lenguajes de programación "reales". No existe un valor de retorno para un comando, tal vez principalmente porque no hay una forma real de declararlo. Podría ocurrir en la página de manual o en la --helpsalida del comando, pero ambos solo son legibles por humanos y, por lo tanto, están escritos en el viento.

Cuando un comando quiere obtener información, la lee de su flujo de entrada o de la lista de argumentos. En ambos casos es necesario analizar las cadenas de texto.

Cuando un comando quiere devolver algo, debe enviarlo echoa su flujo de salida. Otra forma que se practica con frecuencia es almacenar el valor de retorno en variables globales dedicadas. Escribir en el flujo de salida es más claro y flexible, porque también puede aceptar datos binarios. Por ejemplo, puedes devolver un BLOB fácilmente:

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

encrypt public.dat > private.dat # Write the function result to a file

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

Paralelamente, la función "devolvería" el código de salida de gpg( GnuPG ). Piense en el código de salida como una ventaja que otros lenguajes no tienen o, dependiendo de su temperamento, como un "Schmutzeffekt" de funciones de shell. Este estado es, por convención, 0 en caso de éxito o un número entero en el rango de 1 a 255 en caso de otra cosa. Para dejar esto claro: return(como exit) sólo puede tomar un valor de 0 a 255, y los valores distintos de 0 no son necesariamente errores, como se suele afirmar.

Cuando no proporciona un valor explícito con return, el estado se toma del último comando en una instrucción/función/comando Bash, etc. Así que siempre hay un estatus y returnes simplemente una manera fácil de proporcionarlo.

Andreas Spindler avatar Jun 27 '2013 08:06 Andreas Spindler

$(...)captura el texto enviado a la salida estándar mediante el comando contenido en él. returnno genera salida estándar. $?contiene el código de resultado del último comando.

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}
Ignacio Vazquez-Abrams avatar Jun 27 '2013 07:06 Ignacio Vazquez-Abrams

El problema con otras respuestas es que usan un global, que puede sobrescribirse cuando hay varias funciones en una cadena de llamadas, o que echosignifica que su función no puede generar información de diagnóstico (olvidará que su función hace esto y el "resultado", es decir, devolverá valor, contendrá más información de la que espera la persona que llama, lo que provocará errores extraños) o evalque es demasiado pesado y complicado.

La forma correcta de hacer esto es colocar las cosas de nivel superior en una función y usar la localregla de alcance dinámico 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

Esto produce

nothing
hi
bye

¡El alcance dinámico significa que ret_valapunta a un objeto diferente, dependiendo de la persona que llama! Esto es diferente del alcance léxico, que es lo que utilizan la mayoría de los lenguajes de programación. En realidad, esta es una característica documentada , fácil de pasar por alto y no muy bien explicada. Aquí está la documentación correspondiente (el énfasis es mío):

Las variables locales de la función se pueden declarar con la función local incorporada. Estas variables son visibles sólo para la función y los comandos que invoca .

Para alguien con experiencia en C, C++, Python, Java, C# o JavaScript, este es probablemente el mayor obstáculo: las funciones en bash no son funciones, son comandos y se comportan como tales: pueden enviar salida a / stdout, stderrpueden canalizar entrada/salida, y pueden devolver un código de salida. Básicamente, no hay diferencia entre definir un comando en un script y crear un ejecutable que pueda llamarse desde la línea de comando.

Entonces, en lugar de escribir tu guión así:

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 ret_valas localy todas las demás funciones devuelven valores a través de ret_val.

Consulte también la pregunta sobre Unix y Linux Alcance de las variables locales en las funciones del Shell .

Otra solución, quizás incluso mejor según la situación, es la publicada por ya.teck que utiliza local -n.

Oliver avatar Aug 29 '2018 16:08 Oliver