Categorías
GNU-Linux Script

Crea y ejecuta tu primer script de Bash Shell

Usar funciones

Usar funciones en Bash

Cuando los scripts bash se hacen más y más grandes, las cosas pueden volverse muy desordenadas.

Puedes encontrarte reescribiendo las mismas piezas de código una y otra vez en diferentes partes de tus scripts bash.

Afortunadamente, puedes evitar reescribir código usando funciones en bash que harán tus scripts más organizados y legibles.

En este capítulo, aprenderás a crear funciones, devolver valores de funciones y pasar argumentos de funciones en los scripts de bash.

Además, aprenderás cómo funciona el ámbito de las variables y cómo definir funciones recursivas.

Crear funciones en bash

Hay dos sintaxis diferentes para declarar funciones bash. La siguiente es la más utilizada para crear funciones bash:

nombre_funcion () {
comandos
}

La segunda forma menos utilizada de crear funciones bash comienza con la función de trabajo reservada seguida del nombre de la función:

function nombre_funcion () {
comandos
}

Ahora hay un par de cosas que debes tener en cuenta cuando trabajas con funciones:

  • Una función nunca se ejecutará a menos que llames a la función.
  • La definición de la función debe preceder a cualquier llamada a la misma.

Cada vez que quieras que una función se ejecute, sólo tienes que llamarla. Una llamada a la función se realiza simplemente haciendo referencia al nombre de la función.

Observa el siguiente script bash fun.sh:

#!/bin/bash

hello () {
echo "Hola Mundo"
}

hola
hola
hola

Definí una función llamada hola que simplemente hace eco de la línea «Hola Mundo» en la terminal. Hice tres llamadas a la función hola y, por lo tanto, si ejecutas el script, verás la línea «Hola Mundo» impresa tres veces en la pantalla:

$ ./fun.sh
Hola Mundo
Hola Mundo
Hola Mundo

Obtener la salida de una función en bash

En muchos lenguajes de programación, las funciones devuelven un valor cuando son llamadas; sin embargo, este no es el caso de bash ya que las funciones de bash no devuelven valores.

Cuando una función bash termina de ejecutarse, devuelve el estado de salida del último comando ejecutado capturado en la variable $? El cero indica que la ejecución ha sido exitosa o un entero positivo distinto de cero (1-255) para indicar que ha fallado.

Puedes utilizar una sentencia return para modificar el estado de salida de la función. Observa el siguiente script error.sh:

#! /bin/bash

error () {
blabla
return 0
}

error
echo "El estado de salida de la función de error es: $?"

Si ejecutas el script bash error.sh, puede que te sorprendas de la salida:

$ ./error.sh 
./error.sh: línea 4: blabla: orden no encontrada
El estado de salida de la función de error es: 0

Sin la sentencia return 0, la función de error nunca habría devuelto un estado de salida distinto de cero, ya que blabla resulta en un error de orden no encontrada.

Así que como puedes ver, aunque las funciones de bash no devuelven valores, hice una solución alterando los estados de salida de las funciones.

También debes tener en cuenta que una sentencia return termina inmediatamente una función.

Pasar argumentos a una función bash

Puedes pasar argumentos a una función de la misma manera que puedes pasar argumentos a un script de bash. Sólo tienes que incluir los argumentos cuando haces la llamada a la función.

Para demostrarlo, observemos el siguiente script bash espar.sh:

#!/bin/bash

espar () {
if [ $(($1 % 2)) -eq 0 ]; then
echo "$1 es par."
else
echo "$1 es impar."
fi
}

espar 3
espar 4
espar 20
espar 111

La función espar() evalúa si un número es par o impar. Hice cuatro llamados a espar(). Para cada llamada a la función, he introducido un número que es el primer incremento de la función espar() y al que hace referencia la variable $1 en la definición de la función.

Ejecutemos el script bash espar.sh para asegurarnos de que funciona:

$ ./espar.sh
3 es impar.
4 es par.
20 es par.
111 es impar.

También debes saber que los argumentos de las funciones de bash y los argumentos de los scripts de bash son dos cosas diferentes. Para contrastar la diferencia, observa el script bash funarg.sh:

#!/bin/bash

fun () {
echo "$1 es el primer argumento de fun()"
echo "$2 es el segundo argumento de fun()"
}

echo "$1 es el primer argumento del script."
echo "$2 es el segundo argumento del script."

fun Si 7

Ejecuta el script con un par de argumentos y observa el resultado:

$ ./funarg.sh Cosas Geniales
Cosas es el primer argumento del script.
Geniales es el segundo argumento del script.
Si es el primer argumento de fun()
7 es el segundo argumento de fun()

Como puedes ver, aunque hayas utilizado las mismas variables $1 y $2 para referirte tanto a los argumentos del script como a los de la función, producen resultados diferentes cuando se llaman desde una función.

Variables locales y globales en las funciones de bash

Las variables de Bash pueden tener un alcance global o local. Puedes acceder a una variable global en cualquier parte de un script bash sin importar el ámbito. Por el contrario, sólo se puede acceder a una variable local desde la definición de su función.

Para demostrarlo, mira el siguiente script bash ambito.sh:

#!/bin/bash

v1='A'
v2='B'

mifun() {
local v1='C'
v2='D'
echo "Dentro de mifun(): v1: $v1, v2: $v2"
}

echo "Antes de llamar a mifun(): v1: $v1, v2: $v2"
mifun
echo "Luego de llamar a mifun(): v1: $v1, v2: $v2"

Primero definí dos variables globales v1 y v2. A continuación, dentro de la definición de mifun(), utilicé la palabra clave local para definir una variable local v1 y modifiqué la variable global v2. Ten en cuenta que puedes utilizar el mismo nombre de variable para las variables locales en diferentes funciones.

Ejecutemos el script:

$ ./ambito.sh
Antes de llamar a mifun(): v1: A, v2: B
Dentro de mifun(): v1: C, v2: D
Luego de llamar a mifun(): v1: A, v2: D

De la salida del script, se puede concluir lo siguiente:

  • Una variable local que tenga el mismo nombre que una variable global tendrá prioridad sobre las variables globales dentro del cuerpo de una función.
  • Puedes modificar una variable global desde dentro de una función.

Funciones recursivas

Una función recursiva es una función que se llama a sí misma. Las funciones recursivas resultan útiles cuando se intenta resolver un problema de programación que puede dividirse en subproblemas más pequeños.

La función factorial es un ejemplo clásico de función recursiva. Observa el siguiente script bash factorial.sh:

#!/bin/bash

factorial () {
if [ $1 -le 1 ]; then
echo 1
else
last=$(factorial $(( $1 -1)))
echo $(( $1 * last ))
fi
}

echo -n "4! es: "
factorial 4
echo -n "5! es: "
factorial 5
echo -n "6! es: "
factorial 6

Toda función recursiva debe comenzar con un caso base que es necesario para terminar la cadena de llamadas a funciones recursivas. En la función factorial(), el caso base se define así:

if [ $1 -le 1 ]; then
echo 1

Ahora deducimos el caso recursivo de la función factorial. Para calcular el factorial de un número n donde n es un número positivo mayor que uno, puedes multiplicar n por el factorial de n-1:

factorial(n) = n * factorial(n-1)

Utilicemos la ecuación anterior para escribir este caso recursivo:

last=$(factorial $(( $1 -1)))
echo $(( $1 * last ))

Ejecutamos el script y nos aseguramos de obtener los resultados correctos:

$ ./factorial.sh
4! es: 24
5! es: 120
6! es: 720

Como ejercicio adicional, intenta escribir una función recursiva para calcular el número n de Fibonacci. Primero, trata de plantear el caso base y luego el caso recursivo; ¡ya lo tienes!