Funciones: definición, return, local, ámbito de variables

Introducción

Las funciones permiten agrupar código reutilizable con un nombre. Evitan la duplicación, hacen los scripts más legibles y facilitan el mantenimiento. En bash, las funciones son ciudadanas de primera clase: se pueden pasar como argumentos y llamar recursivamente.

Definición de funciones

Hay dos sintaxis equivalentes:


# Sintaxis 1 (recomendada, POSIX)
nombre_funcion() {
    # comandos
}

# Sintaxis 2 (bash específico)
function nombre_funcion {
    # comandos
}

# Llamar a la función
nombre_funcion
nombre_funcion arg1 arg2
          

Las funciones deben definirse antes de ser llamadas. Se suelen poner al principio del script o en un fichero de librería que se carga con source.

Argumentos de funciones

Las funciones reciben argumentos igual que los scripts: $1, $2... Dentro de la función, estos valores son locales a ella y no interfieren con los del script principal.


#!/usr/bin/env bash

saludar() {
    local NOMBRE="$1"
    local IDIOMA="{$2:-es}"

    case $IDIOMA in
        es) echo "Hola, $NOMBRE" ;;
        en) echo "Hello, $NOMBRE" ;;
        fr) echo "Bonjour, $NOMBRE" ;;
        *)  echo "Hola, $NOMBRE (idioma desconocido)" ;;
    esac
}

saludar "Juan"
saludar "John" "en"
saludar "Jean" "fr"
          

return — Código de salida de la función

return N establece el código de salida de la función (accesible con $? después de llamarla). Solo acepta valores enteros de 0 a 255. No devuelve cadenas.


es_par() {
    [ $(($1 % 2)) -eq 0 ]   # la condición ya establece $? (0 o 1)
    return $?
}

# Forma más corta (el return es implícito):
es_par() {
    (( $1 % 2 == 0 ))
}

if es_par 4; then
    echo "4 es par"
fi

es_par 7
echo "¿7 es par? Código: $?"    # 1 (falso)
          

Devolver valores de cadena — capturar stdout:

La forma estándar de devolver un valor de texto desde una función es imprimirlo con echo y capturar la salida con $():


mayusculas() {
    echo "$1" | tr '[:lower:]' '[:upper:]'
}

RESULTADO=$(mayusculas "hola mundo")
echo "$RESULTADO"    # HOLA MUNDO
          

local — Ámbito de variables

En bash, las variables son globales por defecto: una variable definida en una función modifica la variable del mismo nombre en el script principal. Para evitar efectos colaterales, declarar las variables de la función como local.


#!/usr/bin/env bash

VALOR="global"

sin_local() {
    VALOR="modificado por sin_local"   # modifica la global
}

con_local() {
    local VALOR="local a la función"   # no afecta a la global
    echo "Dentro: $VALOR"
}

echo "Antes: $VALOR"        # global
sin_local
echo "Después sin_local: $VALOR"   # modificado por sin_local
con_local                           # Dentro: local a la función
echo "Después con_local: $VALOR"   # modificado por sin_local (sin cambio)
          

Regla: declarar siempre las variables internas de una función como local para evitar contaminar el ámbito global.

Librerías de funciones

Para reutilizar funciones en varios scripts, guardarlas en un fichero separado y cargarlo con source:


# fichero: /usr/local/lib/mis_funciones.sh

log_info() {
    echo "[INFO]  $(date '+%Y-%m-%d %H:%M:%S') $*"
}

log_error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
}

comprobar_root() {
    if [ "$(id -u)" -ne 0 ]; then
        log_error "Este script debe ejecutarse como root"
        exit 1
    fi
}
          

#!/usr/bin/env bash
# Cargar la librería
source /usr/local/lib/mis_funciones.sh

comprobar_root
log_info "Script iniciado"
# ...
log_info "Script finalizado correctamente"
          

Recursividad


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

echo "5! = $(factorial 5)"   # 5! = 120
          

La recursividad en bash es posible pero cara en rendimiento. Para cálculos intensivos, preferir herramientas externas como awk o python3.

Siempre usar local para variables internas de funciones. Siempre definir las funciones antes de llamarlas. Para devolver cadenas, usar echo y capturar con $().

Entrada y salida: read, echo, printf, here-doc

Índice de la sección

Índice del curso