Saltar al contenido principal

Clase 05 — Funciones

Declarar y llamar funciones

#!/bin/bash

# Forma 1 (recomendada)
saludar() {
echo "¡Hola desde una función!"
}

# Forma 2
function despedir {
echo "¡Adiós desde una función!"
}

# Llamar funciones
saludar
despedir

Funciones con parámetros

#!/bin/bash

# Los parámetros se acceden con $1, $2, etc.
saludar() {
local nombre="$1"
local apellido="$2"
echo "Hola, $nombre $apellido"
}

sumar() {
local a="$1"
local b="$2"
echo "$((a + b))"
}

# Llamar con argumentos
saludar "Roxs" "Ross"
resultado=$(sumar 10 20)
echo "La suma es: $resultado"

Variables locales vs globales

#!/bin/bash

variable_global="Soy global"

mi_funcion() {
local variable_local="Soy local"
variable_global="Modificada desde función"

echo "Dentro de la función:"
echo " Global: $variable_global"
echo " Local: $variable_local"
}

echo "Antes: $variable_global"
mi_funcion
echo "Después: $variable_global"
echo "Local fuera: $variable_local" # Vacía - no existe fuera
Buena práctica

Siempre usá local para variables dentro de funciones para evitar efectos secundarios.

Retornar valores

#!/bin/bash

# Método 1: return (solo para códigos de estado 0-255)
es_par() {
if (( $1 % 2 == 0 )); then
return 0 # true/éxito
else
return 1 # false/fallo
fi
}

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

if ! es_par 7; then
echo "7 es impar"
fi

# Método 2: echo + sustitución de comando (para valores)
obtener_fecha() {
echo "$(date '+%Y-%m-%d')"
}

fecha=$(obtener_fecha)
echo "Hoy es: $fecha"

# Método 3: Variable global (menos recomendado)
calcular() {
RESULTADO=$(( $1 * $2 ))
}

calcular 5 3
echo "Resultado: $RESULTADO"

Funciones con validación

#!/bin/bash

crear_directorio() {
local dir="$1"

if [[ -z "$dir" ]]; then
echo "ERROR: Se requiere un nombre de directorio" >&2
return 1
fi

if [[ -d "$dir" ]]; then
echo "WARN: El directorio '$dir' ya existe"
return 0
fi

mkdir -p "$dir" && echo "Directorio '$dir' creado" || {
echo "ERROR: No se pudo crear '$dir'" >&2
return 1
}
}

crear_directorio "mi_proyecto"
crear_directorio ""
crear_directorio "mi_proyecto"

Ejemplo: Librería de colores

#!/bin/bash
# colores.sh - Funciones de utilidad para colores en terminal

# Colores
ROJO='\033[0;31m'
VERDE='\033[0;32m'
AMARILLO='\033[0;33m'
AZUL='\033[0;34m'
RESET='\033[0m'

print_info() {
echo -e "${AZUL}[INFO]${RESET} $1"
}

print_ok() {
echo -e "${VERDE}[OK]${RESET} $1"
}

print_warn() {
echo -e "${AMARILLO}[WARN]${RESET} $1"
}

print_error() {
echo -e "${ROJO}[ERROR]${RESET} $1" >&2
}

print_header() {
local texto="$1"
local largo=${#texto}
local linea=$(printf '=%.0s' $(seq 1 $((largo + 4))))

echo ""
echo "$linea"
echo " $texto"
echo "$linea"
echo ""
}

# Uso
print_header "DEPLOY EN PRODUCCIÓN"
print_info "Iniciando deploy..."
print_ok "Imagen Docker construida"
print_warn "La versión anterior será eliminada"
print_error "Falló la conexión a la base de datos"

Ejemplo práctico: Gestor de backups

#!/bin/bash
# backup.sh - Sistema de backup con funciones

BACKUP_DIR="./backups"
LOG_FILE="./backup.log"

log() {
local nivel="$1"
local mensaje="$2"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$nivel] $mensaje" | tee -a "$LOG_FILE"
}

crear_directorio_backup() {
if [[ ! -d "$BACKUP_DIR" ]]; then
mkdir -p "$BACKUP_DIR"
log "INFO" "Directorio de backups creado: $BACKUP_DIR"
fi
}

hacer_backup() {
local origen="$1"
local nombre_backup="$2"

if [[ -z "$origen" ]] || [[ -z "$nombre_backup" ]]; then
log "ERROR" "Uso: hacer_backup <origen> <nombre>"
return 1
fi

if [[ ! -e "$origen" ]]; then
log "ERROR" "El origen '$origen' no existe"
return 1
fi

local fecha=$(date '+%Y%m%d_%H%M%S')
local destino="${BACKUP_DIR}/${nombre_backup}_${fecha}.tar.gz"

tar -czf "$destino" "$origen" 2>/dev/null
if [[ $? -eq 0 ]]; then
local tamano=$(du -h "$destino" | cut -f1)
log "OK" "Backup creado: $destino ($tamano)"
return 0
else
log "ERROR" "Falló el backup de '$origen'"
return 1
fi
}

listar_backups() {
log "INFO" "Listando backups en $BACKUP_DIR"
echo ""
printf "%-40s %-10s %-20s\n" "ARCHIVO" "TAMAÑO" "FECHA"
printf "%-40s %-10s %-20s\n" "--------" "------" "-----"

for f in "$BACKUP_DIR"/*.tar.gz; do
[[ -f "$f" ]] || continue
local nombre=$(basename "$f")
local tamano=$(du -h "$f" | cut -f1)
local fecha=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$f" 2>/dev/null || \
stat --format="%y" "$f" 2>/dev/null | cut -d. -f1)
printf "%-40s %-10s %-20s\n" "$nombre" "$tamano" "$fecha"
done
}

limpiar_backups() {
local dias="${1:-7}"
log "INFO" "Limpiando backups con más de $dias días"
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +"$dias" -delete
log "OK" "Limpieza completada"
}

# --- Ejecución ---
crear_directorio_backup
hacer_backup "/etc/hosts" "hosts"
hacer_backup "$HOME/.bashrc" "bashrc"
listar_backups

Cargar funciones desde otro archivo (source)

#!/bin/bash
# main.sh - Cargar funciones externas

# Cargar librería de funciones
source ./colores.sh
# o también:
# . ./colores.sh

print_header "MI APLICACIÓN"
print_info "Funciones cargadas desde archivo externo"
print_ok "¡Todo listo!"

Funciones recursivas

#!/bin/bash

# Factorial
factorial() {
local n=$1
if (( n <= 1 )); then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}

echo "5! = $(factorial 5)"
echo "10! = $(factorial 10)"

# Listar directorio recursivamente (tree casero)
mi_tree() {
local dir="${1:-.}"
local prefix="${2:-}"

for entry in "$dir"/*; do
[[ -e "$entry" ]] || continue
local nombre=$(basename "$entry")

if [[ -d "$entry" ]]; then
echo "${prefix}📁 $nombre/"
mi_tree "$entry" " $prefix"
else
echo "${prefix}📄 $nombre"
fi
done
}

mi_tree "."

Ejercicios

  1. Creá una función validar_email que verifique si un string tiene formato de email
  2. Creá una librería de funciones matemáticas (sumar, restar, multiplicar, dividir) y usala desde otro script con source
  3. Creá una función que reciba un directorio y devuelva el número total de archivos y su tamaño