¿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
El primer argumento pasado a la función
El segundo argumento pasado a la función
Cuenta cuántos argumentos se pasaron a la función
Lista de todos los argumentos como elementos separados
Todos los argumentos como una sola cadena
Parámetros Básicos
Veamos cómo pasar y usar parámetros básicos en las funciones:
#!/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:
#!/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 |
#!/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
#!/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
#!/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:
#!/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:
#!/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:
#!/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"