Valor de retorno en una función 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 return
declaración, lo único que puede especificar con ella es el exit
estado de la función (un valor entre 0
y 255
, 0 significa "éxito"). Entonces return
no es lo que quieres.
Es posible que desee convertir su return
declaración en una echo
declaració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 || fun2
solo se ejecutará fun2
si fun1
devuelve un 0
valor que no es. El valor de retorno predeterminado es el valor de salida de la última declaración ejecutada dentro de la función.
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 --help
salida 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 echo
a 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 return
es simplemente una manera fácil de proporcionarlo.
$(...)
captura el texto enviado a la salida estándar mediante el comando contenido en él. return
no genera salida estándar. $?
contiene el código de resultado del último comando.
fun1 (){
return 34
}
fun2 (){
fun1
local res=$?
echo $res
}
El problema con otras respuestas es que usan un global, que puede sobrescribirse cuando hay varias funciones en una cadena de llamadas, o que echo
significa 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 eval
que 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 local
regla 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_val
apunta 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
, stderr
pueden 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_val
as local
y 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
.