El lenguaje de scripting de bash permite el uso de funciones que deben estar declaradas siempre antes de ser llamadas, pero a diferencia de otros lenguajes, no permite retornar valores. Siempre que una función de bash finaliza devuelve el valor de estado de salida, RC o $?, que es cero en caso de ejecución correcta y cualquier otro valor en caso de error.
Aquí se describen algunas formas de retornar valores. Se suelen usar variables globales, pero también se pueden usar sustitución de comandos o se puede pasar una variable donde el resultado estará cargado.
Variable global
El método común y más simple es cargar una variable global, pues en bash todas las variables son globales (salvo definición explícita de local):
function mifuncion() { RESULTADO='un valor' } mifuncion echo $RESULTADO
Simple y claro. Pero las variables globales se vuelven complejas de manejar en códigos extensos, principalmente a la hora de encontrar errores de ejecución.
Entonces es posible usar variables locales a las funciones, pero el problema es devolver el valor al código que llamó a la función.
Sustitución de comando (STDOUT)
function mifuncion() { local miresultado='un valor' echo "$miresultado" } RESULTADO=$(mifuncion) # o RESULTADO=`mifuncion` echo $RESULTADO
Aquí el resultado se muestra en STDOUT y se produce la sustitución del comando al llamar a la función para cargarlo en una variable.
Pasar una variable (eval)
Otra forma de pasar un resultado es escribir la función para que reciba una variable al ser llamada y entonces cambiar el valor de la variable como resultado de la función:
function mifuncion() { local __resultadovar=$1 local miresultado='un valor' eval $__resultadovar="'$miresultado'" } mifuncion RESULTADO echo $RESULTADO
Aqui lo que hacemos es guardar el nombre de una variable en una variable; por eso no podemos configurar la variable directamente, necesitamos usar eval para configurarla. El comando eval le dice a bash que interprete la linea dos veces: la primera vez devuelve el string miresultado=’un valor’, el cual es interpretado una vez mas para obtener el valor de la variable.
Cuando se guarda el nombre de una variable que se pasa en al linea de comando, debemos asegurarnos que es guardado en una variable de tipo local (local __resultadovar=$1) con un nombre que no sea usado cuando se llama a la función (por eso el nombre __resultadovar). Si no tenemos cuidado con esto, y se usa el mismo nombre de variable, la variable de resultado no será cargada; por ejemplo, lo siguiente no funciona:
function mifuncion() { local RESULTADO=$1 local miresultado='un valor' eval $RESULTADO="'$miresultado'" } mifuncion RESULTADO echo $RESULTADO
La razón por la que no funciona es que cuando eval realiza la segunda interpretación evalúa RESULTADO=’un valor, pero RESULTADO es en ese momento una variable local a la función, y entonces queda establecida así, en lugar de configurar la variable que se pasa al llamar a la función.
Otra precaución para evitar este funcionamiento no deseado, es utilizar variables en mayúsculas cuando son globales y minúsculas cuando son locales.
Para mayor flexibilidad, las funciones pueden ser escritas para combinar ambos métodos:
function mifuncion() { local __resultadovar=$1 local miresultado='un valor' if [[ "$__resultadovar" ]]; then eval $__resultadovar="'$miresultado'" else echo "$miresultado" fi } mifuncion RESULTADO echo $RESULTADO RESULTADO2=$(mifuncion) echo $RESULTADO2
Entonces, si ninguna variable es pasada a la función, el valor de salida es colocado en STDOUT.
Basado (casi traducción textual) del artículo del 2009 de Mitch Frazier, publicado en Linux Journal con el título «Returning Values from Bash Functions«, pues ha sido el artículo con el que comprendí claramente cómo gestionar las funciones en bash.