Paso de Parámetros

Crea funciones flexibles y dinámicas con parámetros

Módulo 4 ⏱️ 35-40 min 📋 Parámetros 🔀 Flexibilidad 📚 Intermedio

¿Qué son los Parámetros?

Los parámetros son valores que puedes pasar a una función para modificar su comportamiento. Hacen que las funciones sean flexibles y reutilizables, ya que pueden trabajar con diferentes datos sin necesidad de modificar el código de la función.

En Bash, los parámetros se pasan como argumentos separados por espacios, y dentro de la función se acceden usando variables especiales numeradas: $1, $2, $3, etc.

Variables de Parámetros en Bash

$1
Primer parámetro
El primer argumento pasado a la función
$2
Segundo parámetro
El segundo argumento pasado a la función
$#
Número de parámetros
Cuenta cuántos argumentos se pasaron a la función
$@
Todos los parámetros
Lista de todos los argumentos como elementos separados
$*
Todos los parámetros (cadena)
Todos los argumentos como una sola cadena

Parámetros Básicos

Veamos cómo pasar y usar parámetros básicos en las funciones:

Ejemplo básico de parámetros Copiar
#!/bin/bash

# Función que saluda a una persona
saludar() {
    local nombre="$1"    # Primer parámetro
    local apellido="$2"  # Segundo parámetro
    
    echo "¡Hola, $nombre $apellido!"
    echo "Bienvenido al sistema"
}

# Función que suma dos números
sumar() {
    local num1=$1
    local num2=$2
    local resultado=$((num1 + num2))
    
    echo "La suma de $num1 + $num2 = $resultado"
}

# Llamar las funciones con parámetros
saludar "Juan" "Pérez"
saludar "María" "García"

echo ""
sumar 15 25
sumar 100 200
$ ./parametros_basicos.sh
¡Hola, Juan Pérez!
Bienvenido al sistema
¡Hola, María García!
Bienvenido al sistema

La suma de 15 + 25 = 40
La suma de 100 + 200 = 300
Buenas Prácticas

Siempre usa local para asignar parámetros a variables locales. Esto previene conflictos con variables globales y hace el código más seguro.

Manejo de Múltiples Parámetros

Cuando trabajas con muchos parámetros, necesitas técnicas especiales para manejarlos eficientemente:

Función con múltiples parámetros Copiar
#!/bin/bash

# Función que muestra información de los parámetros
mostrar_parametros() {
    echo "=== Información de Parámetros ==="
    echo "Número total de parámetros: $#"
    echo "Todos los parámetros (\$@): $@"
    echo "Todos los parámetros (\$*): $*"
    echo ""
    
    # Mostrar cada parámetro individualmente
    local contador=1
    for parametro in "$@"; do
        echo "Parámetro $contador: '$parametro'"
        ((contador++))
    done
}

# Función que calcula el promedio de números
calcular_promedio() {
    local suma=0
    local contador=0
    
    # Verificar que se pasaron parámetros
    if [ $# -eq 0 ]; then
        echo "Error: No se proporcionaron números"
        return 1
    fi
    
    # Sumar todos los números
    for numero in "$@"; do
        # Verificar que sea un número
        if [[ "$numero" =~ ^-?[0-9]+$ ]]; then
            suma=$((suma + numero))
            ((contador++))
        else
            echo "Advertencia: '$numero' no es un número válido, omitiendo"
        fi
    done
    
    if [ $contador -gt 0 ]; then
        local promedio=$((suma / contador))
        echo "Promedio de $contador números: $promedio"
        echo "Suma total: $suma"
    else
        echo "Error: No se encontraron números válidos"
        return 1
    fi
}

# Ejemplos de uso
mostrar_parametros "hola" "mundo" "desde" "bash"

echo ""
calcular_promedio 10 20 30 40 50

echo ""
calcular_promedio 5 "abc" 15 "xyz" 25
$ ./multiples_parametros.sh
=== Información de Parámetros ===
Número total de parámetros: 4
Todos los parámetros ($@): hola mundo desde bash
Todos los parámetros ($*): hola mundo desde bash

Parámetro 1: 'hola'
Parámetro 2: 'mundo'
Parámetro 3: 'desde'
Parámetro 4: 'bash'

Promedio de 5 números: 30
Suma total: 150

Advertencia: 'abc' no es un número válido, omitiendo
Advertencia: 'xyz' no es un número válido, omitiendo
Promedio de 3 números: 15
Suma total: 45

Diferencias entre $@ y $*

Es importante entender las diferencias entre $@ y $*, especialmente cuando trabajas con parámetros que contienen espacios:

Variable Comportamiento Ejemplo Uso Recomendado
"$@" Cada parámetro como elemento separado "param1" "param2" "param3" ✅ Casi siempre usar esta
"$*" Todos los parámetros como una cadena "param1 param2 param3" ⚠️ Casos específicos
$@ Sin comillas, se divide por espacios param1 param2 param3 ❌ Evitar sin comillas
$* Sin comillas, se divide por espacios param1 param2 param3 ❌ Evitar sin comillas
Demostración de diferencias Copiar
#!/bin/bash

# Función para demostrar las diferencias
demostrar_diferencias() {
    echo "=== Parámetros recibidos ==="
    echo "Número de parámetros: $#"
    echo ""
    
    echo "Con \"\$@\" (recomendado):"
    local contador=1
    for param in "$@"; do
        echo "  [$contador]: '$param'"
        ((contador++))
    done
    
    echo ""
    echo "Con \"\$*\" (una sola cadena):"
    echo "  Resultado: '$*'"
    
    echo ""
    echo "Con \$@ sin comillas (problemático):"
    contador=1
    for param in $@; do
        echo "  [$contador]: '$param'"
        ((contador++))
    done
}

# Función que pasa parámetros a otra función
reenviar_parametros() {
    echo "Reenviando parámetros correctamente con \"\$@\":"
    demostrar_diferencias "$@"
    
    echo ""
    echo "========================================"
    echo ""
    
    echo "Reenviando incorrectamente con \$@:"
    demostrar_diferencias $@
}

# Probar con parámetros que contienen espacios
echo "Llamando con parámetros que contienen espacios:"
reenviar_parametros "primer parámetro" "segundo parámetro" "tercer parámetro"
Regla de Oro

Siempre usa "$@" (con comillas dobles) cuando quieras pasar todos los parámetros a otra función o comando. Esto preserva correctamente los espacios y caracteres especiales.

Validación de Parámetros

Es importante validar los parámetros para asegurar que tu función recibe los datos correctos:

Validación Básica
Validar número de parámetros Copiar
#!/bin/bash

# Función que requiere exactamente 2 parámetros
dividir() {
    # Verificar número de parámetros
    if [ $# -ne 2 ]; then
        echo "Error: La función requiere exactamente 2 parámetros"
        echo "Uso: dividir  "
        return 1
    fi
    
    local dividendo=$1
    local divisor=$2
    
    # Verificar que el divisor no sea cero
    if [ "$divisor" -eq 0 ]; then
        echo "Error: División por cero no permitida"
        return 1
    fi
    
    # Realizar la división
    local resultado=$((dividendo / divisor))
    echo "Resultado: $dividendo ÷ $divisor = $resultado"
    return 0
}

# Ejemplos de uso
echo "Caso correcto:"
dividir 20 4

echo ""
echo "Error - parámetros insuficientes:"
dividir 10

echo ""
echo "Error - división por cero:"
dividir 15 0
Validación Avanzada
Validar tipos de datos Copiar
#!/bin/bash

# Función para validar si es un número
es_numero() {
    local valor="$1"
    [[ "$valor" =~ ^-?[0-9]+$ ]]
}

# Función para validar si es un email
es_email() {
    local email="$1"
    [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}

# Función con validación completa
crear_usuario() {
    # Verificar número de parámetros
    if [ $# -lt 3 ]; then
        echo "Error: Parámetros insuficientes"
        echo "Uso: crear_usuario   "
        return 1
    fi
    
    local nombre="$1"
    local email="$2"
    local edad="$3"
    
    # Validar nombre (no vacío)
    if [ -z "$nombre" ]; then
        echo "Error: El nombre no puede estar vacío"
        return 1
    fi
    
    # Validar email
    if ! es_email "$email"; then
        echo "Error: '$email' no es un email válido"
        return 1
    fi
    
    # Validar edad
    if ! es_numero "$edad"; then
        echo "Error: '$edad' no es una edad válida"
        return 1
    fi
    
    if [ "$edad" -lt 0 ] || [ "$edad" -gt 120 ]; then
        echo "Error: La edad debe estar entre 0 y 120 años"
        return 1
    fi
    
    # Si todas las validaciones pasan
    echo "✓ Usuario creado exitosamente:"
    echo "  Nombre: $nombre"
    echo "  Email: $email"
    echo "  Edad: $edad años"
    return 0
}

# Ejemplos de uso
echo "Caso correcto:"
crear_usuario "Juan Pérez" "[email protected]" "30"

echo ""
echo "Caso con email inválido:"
crear_usuario "María García" "email-invalido" "25"

echo ""
echo "Caso con edad inválida:"
crear_usuario "Pedro López" "[email protected]" "abc"

Comando shift y Parámetros Dinámicos

El comando shift te permite "mover" los parámetros, eliminando el primero y renumerando los restantes. Es útil para procesar parámetros de manera dinámica:

Usando shift para procesar parámetros Copiar
#!/bin/bash

# Función que procesa opciones y parámetros
procesar_opciones() {
    local verbose=false
    local output_file=""
    local input_files=()
    
    # Procesar opciones
    while [ $# -gt 0 ]; do
        case "$1" in
            -v|--verbose)
                verbose=true
                shift  # Eliminar la opción procesada
                ;;
            -o|--output)
                if [ $# -lt 2 ]; then
                    echo "Error: La opción $1 requiere un valor"
                    return 1
                fi
                output_file="$2"
                shift 2  # Eliminar la opción y su valor
                ;;
            --output=*)
                output_file="${1#--output=}"
                shift
                ;;
            -*)
                echo "Error: Opción desconocida: $1"
                return 1
                ;;
            *)
                # Es un archivo de entrada
                input_files+=("$1")
                shift
                ;;
        esac
    done
    
    # Mostrar configuración
    echo "=== Configuración ==="
    echo "Verbose: $verbose"
    echo "Archivo de salida: ${output_file:-"(no especificado)"}"
    echo "Archivos de entrada:"
    if [ ${#input_files[@]} -eq 0 ]; then
        echo "  (ninguno)"
    else
        for file in "${input_files[@]}"; do
            echo "  - $file"
        done
    fi
}

# Función que calcula estadísticas de una lista
calcular_estadisticas() {
    if [ $# -eq 0 ]; then
        echo "Error: No se proporcionaron números"
        return 1
    fi
    
    local suma=0
    local contador=0
    local maximo
    local minimo
    
    # Procesar el primer número para inicializar min/max
    if es_numero "$1"; then
        maximo=$1
        minimo=$1
    fi
    
    # Procesar todos los números usando shift
    while [ $# -gt 0 ]; do
        if es_numero "$1"; then
            suma=$((suma + $1))
            ((contador++))
            
            # Actualizar máximo y mínimo
            if [ "$1" -gt "$maximo" ]; then
                maximo=$1
            fi
            if [ "$1" -lt "$minimo" ]; then
                minimo=$1
            fi
        else
            echo "Advertencia: '$1' no es un número, omitiendo"
        fi
        shift  # Pasar al siguiente parámetro
    done
    
    if [ $contador -gt 0 ]; then
        local promedio=$((suma / contador))
        echo "=== Estadísticas ==="
        echo "Cantidad de números: $contador"
        echo "Suma: $suma"
        echo "Promedio: $promedio"
        echo "Máximo: $maximo"
        echo "Mínimo: $minimo"
    fi
}

# Función auxiliar (ya definida anteriormente)
es_numero() {
    [[ "$1" =~ ^-?[0-9]+$ ]]
}

# Ejemplos de uso
echo "=== Ejemplo 1: Procesando opciones ==="
procesar_opciones -v --output resultado.txt archivo1.txt archivo2.txt archivo3.txt

echo ""
echo "=== Ejemplo 2: Calculando estadísticas ==="
calcular_estadisticas 10 5 15 20 8 12 25 3
$ ./shift_ejemplo.sh
=== Ejemplo 1: Procesando opciones ===
=== Configuración ===
Verbose: true
Archivo de salida: resultado.txt
Archivos de entrada:
  - archivo1.txt
  - archivo2.txt
  - archivo3.txt

=== Ejemplo 2: Calculando estadísticas ===
=== Estadísticas ===
Cantidad de números: 8
Suma: 98
Promedio: 12
Máximo: 25
Mínimo: 3

Ejercicios Prácticos

Ejercicio 1: Gestor de Archivos

Crea un conjunto de funciones que manejen archivos con diferentes parámetros:

gestor_archivos.sh Copiar
#!/bin/bash

# Función para copiar archivos con opciones
copiar_archivos() {
    local crear_backup=false
    local preservar_timestamps=false
    local verbose=false
    local origen=""
    local destino=""
    
    # Procesar opciones
    while [ $# -gt 0 ]; do
        case "$1" in
            -b|--backup)
                crear_backup=true
                shift
                ;;
            -p|--preserve)
                preservar_timestamps=true
                shift
                ;;
            -v|--verbose)
                verbose=true
                shift
                ;;
            -*)
                echo "Error: Opción desconocida: $1"
                return 1
                ;;
            *)
                if [ -z "$origen" ]; then
                    origen="$1"
                elif [ -z "$destino" ]; then
                    destino="$1"
                else
                    echo "Error: Demasiados argumentos"
                    return 1
                fi
                shift
                ;;
        esac
    done
    
    # Validar parámetros obligatorios
    if [ -z "$origen" ] || [ -z "$destino" ]; then
        echo "Error: Se requieren archivo origen y destino"
        echo "Uso: copiar_archivos [opciones]  "
        echo "Opciones:"
        echo "  -b, --backup    Crear backup si el destino existe"
        echo "  -p, --preserve  Preservar timestamps"
        echo "  -v, --verbose   Modo verboso"
        return 1
    fi
    
    # Verificar que el archivo origen existe
    if [ ! -f "$origen" ]; then
        echo "Error: El archivo '$origen' no existe"
        return 1
    fi
    
    # Crear backup si se solicita y el destino existe
    if [ "$crear_backup" = true ] && [ -f "$destino" ]; then
        local backup="${destino}.backup.$(date +%Y%m%d_%H%M%S)"
        if [ "$verbose" = true ]; then
            echo "Creando backup: $backup"
        fi
        mv "$destino" "$backup"
    fi
    
    # Preparar comando de copia
    local cmd_cp="cp"
    if [ "$preservar_timestamps" = true ]; then
        cmd_cp="cp -p"
    fi
    
    # Realizar la copia
    if [ "$verbose" = true ]; then
        echo "Copiando '$origen' -> '$destino'"
    fi
    
    if $cmd_cp "$origen" "$destino"; then
        if [ "$verbose" = true ]; then
            echo "✓ Copia completada exitosamente"
        fi
        return 0
    else
        echo "✗ Error al copiar el archivo"
        return 1
    fi
}

# Función para obtener información de múltiples archivos
info_archivos() {
    if [ $# -eq 0 ]; then
        echo "Error: No se especificaron archivos"
        echo "Uso: info_archivos  [archivo2] [archivo3] ..."
        return 1
    fi
    
    echo "=== Información de Archivos ==="
    local total_archivos=0
    local total_tamaño=0
    
    for archivo in "$@"; do
        if [ -f "$archivo" ]; then
            ((total_archivos++))
            local tamaño=$(stat -c%s "$archivo" 2>/dev/null || stat -f%z "$archivo" 2>/dev/null)
            local fecha_modificacion=$(stat -c%y "$archivo" 2>/dev/null || stat -f%Sm "$archivo" 2>/dev/null)
            
            echo ""
            echo "Archivo: $archivo"
            echo "  Tamaño: $tamaño bytes"
            echo "  Modificado: $fecha_modificacion"
            echo "  Permisos: $(ls -l "$archivo" | awk '{print $1}')"
            
            total_tamaño=$((total_tamaño + tamaño))
        else
            echo ""
            echo "Archivo: $archivo"
            echo "  Estado: ❌ No existe o no es un archivo regular"
        fi
    done
    
    echo ""
    echo "=== Resumen ==="
    echo "Total de archivos válidos: $total_archivos"
    echo "Tamaño total: $total_tamaño bytes"
}

# Ejemplos de uso
echo "=== Ejemplo 1: Copiando archivo con backup y verbose ==="
echo "Contenido original" > archivo_origen.txt
echo "Contenido destino" > archivo_destino.txt

copiar_archivos -b -v archivo_origen.txt archivo_destino.txt

echo ""
echo "=== Ejemplo 2: Información de archivos ==="
info_archivos archivo_origen.txt archivo_destino.txt archivo_inexistente.txt

# Limpiar archivos de ejemplo
rm -f archivo_origen.txt archivo_destino.txt archivo_destino.txt.backup.*
Ejercicio 2: Calculadora de Red

Crea funciones que trabajen con direcciones IP y rangos de red:

calculadora_red.sh Copiar
#!/bin/bash

# Función para validar IP
validar_ip() {
    local ip="$1"
    local patron='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
    
    if [[ "$ip" =~ $patron ]]; then
        # Verificar que cada octeto esté en rango 0-255
        IFS='.' read -ra octetos <<< "$ip"
        for octeto in "${octetos[@]}"; do
            if [ "$octeto" -lt 0 ] || [ "$octeto" -gt 255 ]; then
                return 1
            fi
        done
        return 0
    else
        return 1
    fi
}

# Función para convertir IP a decimal
ip_a_decimal() {
    local ip="$1"
    if ! validar_ip "$ip"; then
        echo "Error: IP inválida"
        return 1
    fi
    
    IFS='.' read -ra octetos <<< "$ip"
    local decimal=$((octetos[0] * 256**3 + octetos[1] * 256**2 + octetos[2] * 256 + octetos[3]))
    echo "$decimal"
}

# Función para verificar si una IP está en un rango
ip_en_rango() {
    local ip="$1"
    local red="$2"
    local mascara="$3"
    
    # Validar parámetros
    if [ $# -ne 3 ]; then
        echo "Error: Se requieren 3 parámetros: IP, Red, Máscara"
        echo "Uso: ip_en_rango   "
        return 1
    fi
    
    if ! validar_ip "$ip" || ! validar_ip "$red"; then
        echo "Error: IP inválida"
        return 1
    fi
    
    if [ "$mascara" -lt 0 ] || [ "$mascara" -gt 32 ]; then
        echo "Error: Máscara debe estar entre 0 y 32"
        return 1
    fi
    
    # Convertir IPs a decimal
    local ip_decimal=$(ip_a_decimal "$ip")
    local red_decimal=$(ip_a_decimal "$red")
    
    # Calcular máscara en decimal
    local mascara_decimal=$(( (0xFFFFFFFF << (32 - mascara)) & 0xFFFFFFFF ))
    
    # Aplicar máscara
    local ip_red=$((ip_decimal & mascara_decimal))
    local red_red=$((red_decimal & mascara_decimal))
    
    if [ $ip_red -eq $red_red ]; then
        echo "✓ La IP $ip está en la red $red/$mascara"
        return 0
    else
        echo "✗ La IP $ip NO está en la red $red/$mascara"
        return 1
    fi
}

# Función para calcular información de red
info_red() {
    local red="$1"
    local mascara="$2"
    
    if [ $# -ne 2 ]; then
        echo "Error: Se requieren 2 parámetros: Red y Máscara"
        echo "Uso: info_red  "
        return 1
    fi
    
    if ! validar_ip "$red" || [ "$mascara" -lt 0 ] || [ "$mascara" -gt 32 ]; then
        echo "Error: Parámetros inválidos"
        return 1
    fi
    
    # Convertir a decimal
    local red_decimal=$(ip_a_decimal "$red")
    local mascara_decimal=$(( (0xFFFFFFFF << (32 - mascara)) & 0xFFFFFFFF ))
    local broadcast_decimal=$((red_decimal | (0xFFFFFFFF >> mascara)))
    
    # Convertir de vuelta a IP
    local broadcast=$(printf "%d.%d.%d.%d" \
        $((broadcast_decimal >> 24 & 255)) \
        $((broadcast_decimal >> 16 & 255)) \
        $((broadcast_decimal >> 8 & 255)) \
        $((broadcast_decimal & 255)))
    
    local primera_ip_decimal=$((red_decimal + 1))
    local ultima_ip_decimal=$((broadcast_decimal - 1))
    
    local primera_ip=$(printf "%d.%d.%d.%d" \
        $((primera_ip_decimal >> 24 & 255)) \
        $((primera_ip_decimal >> 16 & 255)) \
        $((primera_ip_decimal >> 8 & 255)) \
        $((primera_ip_decimal & 255)))
    
    local ultima_ip=$(printf "%d.%d.%d.%d" \
        $((ultima_ip_decimal >> 24 & 255)) \
        $((ultima_ip_decimal >> 16 & 255)) \
        $((ultima_ip_decimal >> 8 & 255)) \
        $((ultima_ip_decimal & 255)))
    
    local hosts_disponibles=$((2 ** (32 - mascara) - 2))
    
    echo "=== Información de Red ==="
    echo "Red: $red/$mascara"
    echo "Dirección de broadcast: $broadcast"
    echo "Primera IP disponible: $primera_ip"
    echo "Última IP disponible: $ultima_ip"
    echo "Número de hosts: $hosts_disponibles"
}

# Ejemplos de uso
echo "=== Ejemplos de Calculadora de Red ==="

echo ""
echo "1. Validando IPs:"
for ip in "192.168.1.1" "192.168.1.256" "invalid.ip" "10.0.0.1"; do
    if validar_ip "$ip"; then
        echo "✓ $ip es válida"
    else
        echo "✗ $ip es inválida"
    fi
done

echo ""
echo "2. Información de red:"
info_red "192.168.1.0" "24"

echo ""
echo "3. Verificando si IPs están en rango:"
ip_en_rango "192.168.1.100" "192.168.1.0" "24"
ip_en_rango "192.168.2.100" "192.168.1.0" "24"