Dominando los Parámetros Posicionales: Una Guía para los Argumentos de Scripts Bash
Desbloquea el poder de los scripts Bash dinámicos dominando los parámetros posicionales. Esta guía completa explica cómo acceder a los argumentos de la línea de comandos usando `$1`, `$2` y variables especiales como `$#` (número de argumentos) y el crucial `"$@"` (todos los argumentos). Aprende las mejores prácticas esenciales para la validación de entrada, comprende la diferencia entre `\$*` y `\$@`, y ve ejemplos prácticos para escribir scripts robustos y con verificación de errores que se adaptan perfectamente a la entrada del usuario.
Dominando los Parámetros Posicionales: Una Guía para los Argumentos de Scripts Bash
Los scripts Bash se vuelven mucho más útiles cuando aceptan argumentos en lugar de obligarte a editar variables dentro del archivo. Un script de respaldo debería aceptar un directorio de origen. Un script de despliegue debería aceptar un nombre de entorno. Un script de limpieza debería aceptar una o más rutas. Esos valores llegan como parámetros posicionales: $1, $2, $3, y así sucesivamente.
La parte complicada no es leer $1. La parte complicada es manejar argumentos faltantes, argumentos con espacios, banderas opcionales y el momento en que tu script crece de "solo para mí" a algo que otra persona ejecutará a las 2 a.m.
La Anatomía de los Parámetros Posicionales
Los parámetros posicionales son variables especiales definidas por el shell que corresponden a las palabras proporcionadas en la línea de comandos después del nombre del script. Están numerados secuencialmente, comenzando desde 1.
| Parámetro | Descripción | Valor de Ejemplo (al ejecutar ./script.sh archivo1 dir/) |
|---|---|---|
$0 |
El nombre del script en sí (o función). | ./script.sh |
$1 |
El primer argumento pasado al script. | archivo1 |
$2 |
El segundo argumento pasado al script. | dir/ |
$N |
El enésimo argumento (donde N > 0). | |
${10} |
Los argumentos más allá de 9 deben estar encerrados entre llaves. |
Accediendo a Argumentos Más Allá de $9
Mientras que los argumentos 1 al 9 se acceden directamente como $1 a $9, acceder al décimo argumento y siguientes requiere encerrar el número entre llaves para evitar ambigüedad con variables de entorno u operaciones de cadenas (por ejemplo, ${10} en lugar de $10).
Parámetros Especiales Esenciales para Scripting
Más allá de los parámetros numéricos, Bash proporciona varias variables especiales críticas que se relacionan con el conjunto de argumentos en su totalidad. Estos son indispensables para la validación y la iteración.
Contando Argumentos con $#
La variable especial $# contiene el número total de argumentos de línea de comandos pasados al script (excluyendo $0). Esta es quizás la variable más importante para implementar la validación de entrada.
#!/bin/bash
if [ "$#" -eq 0 ]; then
echo "Error: No se proporcionaron argumentos."
echo "Uso: $0 <archivo_entrada>"
exit 1
fi
echo "Proporcionaste $# argumentos."
Todos los Argumentos: $@ y $*
Las variables $@ y $* ambas representan la lista completa de argumentos, pero se comportan de manera diferente, especialmente cuando se citan.
$* (Cadena Única)
Cuando se cita doblemente ("$*"), toda la lista de parámetros posicionales se trata como un solo argumento, separado por el primer carácter de la variable IFS (Separador de Campo Interno) (generalmente un espacio).
- Si los argumentos de entrada son:
arg1arg2arg3 "$*"se expande a:"arg1 arg2 arg3"(un solo elemento)
$@ (Cadenas Separadas - Preferido)
Cuando se cita doblemente ("$@"), cada parámetro posicional se trata como un argumento separado y citado. Este es el método estándar y preferido para iterar sobre argumentos, ya que preserva correctamente los argumentos que contienen espacios.
- Si los argumentos de entrada son:
arg1"arg con espacio"arg3 "$@"se expande a:"arg1" "arg con espacio" "arg3"(tres elementos distintos)
Por Qué las Citas Importan: Una Demostración
Considere un script ejecutado con argumentos: ./test.sh 'hola mundo' archivo.txt
#!/bin/bash
# $* sin citar divide en espacios y generalmente está mal.
echo "-- Iterando usando $* sin citar --"
for item in $*; do
echo "Elemento: $item"
done
# "$@" citado preserva cada argumento original.
echo "-- Iterando usando "$@" citado --"
for item in "$@"; do
echo "Elemento: $item"
done
Con ./test.sh 'hola mundo' archivo.txt, el bucle sin citar imprime hola y mundo como elementos separados. El bucle "$@" mantiene hola mundo como un solo argumento. Esa diferencia es la razón por la que los usuarios experimentados de shell recurren a "$@" casi automáticamente.
Técnicas Prácticas para el Manejo de Argumentos
1. Script Básico de Recuperación de Argumentos
Este script simple demuestra cómo acceder a parámetros específicos y usar $0 para proporcionar comentarios útiles.
deploy_service.sh:
#!/bin/bash
# Uso: deploy_service.sh <nombre_servicio> <entorno>
NOMBRE_SERVICIO="$1"
ENTORNO="$2"
# Verificación de validación (mínimo dos argumentos)
if [ "$#" -lt 2 ]; then
echo "Uso: $0 <nombre_servicio> <entorno>"
exit 1
fi
echo "Iniciando despliegue para el servicio: $NOMBRE_SERVICIO"
echo "Entorno objetivo: $ENTORNO"
# Ejecutar comando usando los parámetros validados
ssh admin@servidor-"$ENTORNO" "/ruta/a/iniciar $NOMBRE_SERVICIO"
2. Validación Robusta de Entrada
Los buenos scripts siempre validan la entrada antes de proceder. Esto incluye verificar el conteo ($#) y a menudo verificar el contenido de los argumentos (por ejemplo, verificar si un argumento es un número o una ruta de archivo válida).
#!/bin/bash
# 1. Verificar el Número de Argumentos (Debe ser exactamente 3)
if [ "$#" -ne 3 ]; then
echo "Error: Este script requiere tres argumentos (origen, destino, usuario)."
echo "Uso: $0 <ruta_origen> <ruta_destino> <usuario>"
exit 1
fi
RUTA_ORIGEN="$1"
RUTA_DESTINO="$2"
USUARIO="$3"
# 2. Verificar Contenido (Ejemplo: Verificar que la ruta de origen exista)
if [ ! -f "$RUTA_ORIGEN" ]; then
echo "Error: El archivo de origen '$RUTA_ORIGEN' no se encuentra o no es un archivo."
exit 2
fi
# Si la validación pasa, proceder
echo "Copiando $RUTA_ORIGEN a $RUTA_DESTINO como usuario $USUARIO..."
Consejo de Mejores Prácticas: Siempre proporciona una declaración
Uso:clara y concisa cuando la validación falle. Esto ayuda a los usuarios a corregir rápidamente su invocación de comando.
3. Iterando Argumentos con shift
El comando shift es una excelente herramienta para procesar argumentos secuencialmente, a menudo usado cuando se manejan banderas simples o cuando se procesan argumentos uno por uno dentro de un bucle while.
shift descarta el argumento actual $1, mueve $2 a $1, $3 a $2, y decrementa $# en uno. Esto te permite procesar el primer argumento y luego iterar hasta que no queden argumentos.
#!/bin/bash
# Procesar una bandera -v simple y luego listar los archivos restantes
VERBOSO=false
if [ "$1" = "-v" ]; then
VERBOSO=true
shift # Descartar la bandera -v y desplazar los argumentos hacia arriba
fi
if $VERBOSO; then
echo "Modo verboso activado."
fi
if [ "$#" -eq 0 ]; then
echo "No se especificaron archivos."
exit 0
fi
echo "Procesando $# archivos restantes:"
for archivo in "$@"; do
if $VERBOSO; then
echo "Verificando archivo: $archivo"
fi
# ... lógica de procesamiento aquí
done
Nota:
shiftes útil para análisis simples. Para scripts complejos con muchas banderas,getoptssuele ser una mejor opción para opciones cortas. El manejo de opciones largas varía según la plataforma, así que prueba cuidadosamente si usasgetoptexterno.
Un Analizador Más Realista
Muchos scripts internos comienzan con una bandera opcional y un valor requerido. Aquí hay un pequeño patrón que se mantiene legible:
#!/usr/bin/env bash
set -u
ejecucion_en_seco=false
entorno=""
uso() {
echo "Uso: $0 [--dry-run] --env <dev|staging|prod> <archivo>..." >&2
}
while [ "$#" -gt 0 ]; do
case "$1" in
--dry-run)
ejecucion_en_seco=true
shift
;;
--env)
if [ "$#" -lt 2 ]; then
echo "Error: --env requiere un valor." >&2
uso
exit 2
fi
entorno="$2"
shift 2
;;
--help|-h)
uso
exit 0
;;
--)
shift
break
;;
-*)
echo "Error: opción desconocida: $1" >&2
uso
exit 2
;;
*)
break
;;
esac
done
if [ -z "$entorno" ]; then
echo "Error: --env es requerido." >&2
uso
exit 2
fi
if [ "$#" -eq 0 ]; then
echo "Error: proporciona al menos un archivo." >&2
uso
exit 2
fi
for archivo in "$@"; do
if [ ! -f "$archivo" ]; then
echo "Error: archivo no encontrado: $archivo" >&2
exit 3
fi
if $ejecucion_en_seco; then
echo "Se subiría $archivo a $entorno"
else
echo "Subiendo $archivo a $entorno"
# comando de subida va aquí
fi
done
Nota los detalles aburridos. Los mensajes de error van a stderr. -- significa "dejar de analizar opciones", lo que permite que alguien pase un nombre de archivo que comience con un guión. El bucle final de archivos usa "$@", por lo que notas de lanzamiento.txt sigue siendo un nombre de archivo.
Errores Comunes
El error más común es olvidar las comillas:
cp $1 $2
Eso se rompe cuando cualquiera de las rutas contiene espacios o caracteres glob del shell. Usa:
cp -- "$1" "$2"
El -- le dice a muchos comandos que el análisis de opciones ha terminado, lo que ayuda si una ruta comienza con -.
Otro error común es validar demasiado tarde. Si tu script espera dos argumentos, verifica eso antes de hacer cualquier cosa destructiva:
if [ "$#" -ne 2 ]; then
echo "Uso: $0 <origen> <destino>" >&2
exit 2
fi
Usa códigos de salida distintos cuando ayude a la persona que llama. Un error de uso podría ser 2; un archivo faltante podría ser 3; un comando externo fallido puede mantener su propio estado. No necesitas una taxonomía gigante de códigos de salida, pero devolver 0 después de una invocación incorrecta hace que la automatización sea menos confiable.
Las Funciones También Tienen Parámetros Posicionales
Dentro de una función Bash, $1 y $2 se refieren a los argumentos de la función, no a los argumentos originales del script.
log_copia() {
local origen="$1"
local destino="$2"
echo "Copiando $origen a $destino"
cp -- "$origen" "$destino"
}
log_copia "$1" "$2"
Eso es útil, pero puede sorprenderte si esperabas que $1 dentro de la función significara el primer argumento a nivel de script. Pasa valores explícitamente. Hace que la función sea más fácil de probar y más fácil de reutilizar.
Reenviando Argumentos a Otro Comando
Muchos scripts envoltorio existen solo para agregar un poco de configuración antes de llamar a otro comando. En ese caso, "$@" es lo que mantiene honesto al envoltorio.
#!/usr/bin/env bash
set -e
export APP_ENV=staging
exec /usr/local/bin/miapp "$@"
Si alguien ejecuta:
./ejecutar-staging.sh --config "config con espacios.yml" --verbose
el comando envuelto recibe los mismos tres argumentos. Si usaste $* o $@ sin comillas, la ruta de configuración podría dividirse en varias palabras.
exec es opcional, pero a menudo es útil en envoltorios porque reemplaza el proceso del shell con el proceso objetivo. Eso hace que las señales se comporten de manera más predecible bajo systemd, Docker o un supervisor de procesos.
Valores Predeterminados Sin Sorpresas
A veces un argumento debería ser opcional. La expansión de parámetros de Bash puede ayudar:
entorno="${1:-dev}"
Eso significa "usa $1 si está establecido y no vacío; de lo contrario, usa dev". Esto está bien para scripts locales amigables, pero ten cuidado con scripts de producción. Un valor predeterminado silencioso puede desplegar en el entorno equivocado si alguien olvida un argumento.
Para comandos riesgosos, prefiere entrada explícita:
if [ "$#" -lt 1 ]; then
echo "Uso: $0 <entorno>" >&2
exit 2
fi
Los valores predeterminados son mejores cuando la consecuencia es pequeña, como predeterminar un nivel de registro o un directorio de salida. Son riesgosos cuando el argumento elige un servidor, elimina datos o cambia un objetivo de despliegue.
Parámetros Posicionales y set -u
Muchos scripts Bash usan set -u para que las variables no establecidas causen un error. Eso puede detectar errores tipográficos, pero también cambia cómo se comportan los parámetros posicionales faltantes.
#!/usr/bin/env bash
set -u
echo "Primer argumento: $1"
Ejecuta ese script sin argumentos y Bash sale con un error de "variable no vinculada". Ese error es técnicamente correcto, pero no es amigable. Valida $# antes de leer parámetros requeridos:
if [ "$#" -lt 1 ]; then
echo "Uso: $0 <archivo-entrada>" >&2
exit 2
fi
archivo_entrada="$1"
Para parámetros opcionales bajo set -u, usa una expansión protegida:
modo="${2:-default}"
Eso mantiene el modo estricto útil sin hacer que los valores opcionales faltantes bloqueen el script.
Cuándo los Parámetros Posicionales Son la Interfaz Incorrecta
Los parámetros posicionales son excelentes para comandos pequeños:
respaldo.sh /var/www /backup/www.tar.gz
Se vuelven difíciles de leer cuando el script toma muchos valores:
desplegar.sh prod us-east-1 api v2.4.1 true false 30
Nadie quiere recordar qué significa el quinto argumento. Una vez que un script llega a ese punto, usa banderas con nombre o un archivo de configuración:
desplegar.sh --env prod --region us-east-1 --service api --version v2.4.1 --timeout 30
El código es ligeramente más largo, pero la línea de comandos se vuelve autodocumentada. Eso es un buen intercambio para scripts utilizados por un equipo.
El buen manejo de parámetros posicionales es principalmente disciplina: valida temprano, cita cada expansión a menos que quieras división intencionalmente, usa "$@" para reenviar argumentos y mantén los mensajes de uso cerca de las comprobaciones que los activan. Esos hábitos hacen que los scripts pequeños sobrevivan a nombres de archivos reales, usuarios reales y automatización real.