Volver
Módulo 7 - Manejo Avanzado AVANZADO

Argumentos de Línea de Comandos

Aprende a crear scripts profesionales que manejen argumentos de forma robusta y flexible

Los argumentos de línea de comandos son fundamentales para crear scripts flexibles y reutilizables. En este módulo aprenderás a manejar parámetros de forma profesional, crear interfaces de usuario intuitivas y validar entradas de manera robusta.

Conceptos Fundamentales

En Bash, los argumentos se pasan al script y se almacenan en variables especiales:

Variable Descripción Ejemplo
$0 Nombre del script script.sh
$1, $2, ..., $n Argumentos posicionales primer, segundo argumento
$# Número total de argumentos 3
$* Todos los argumentos como cadena "arg1 arg2 arg3"
$@ Todos los argumentos como array ["arg1", "arg2", "arg3"]
$$ PID del proceso actual 1234
$? Código de salida del último comando 0 (éxito) o >0 (error)

Script Básico con Argumentos

Script básico - argumentos.sh
bash
#!/bin/bash

# Script básico para mostrar argumentos
echo "Nombre del script: $0"
echo "Primer argumento: $1"
echo "Segundo argumento: $2"
echo "Tercer argumento: $3"
echo ""
echo "Número total de argumentos: $#"
echo "Todos los argumentos (\$*): $*"
echo "Todos los argumentos (\$@): $@"
echo "PID del proceso: $$"
chmod +x argumentos.sh
./argumentos.sh archivo.txt backup 2024

Manejo Avanzado de Argumentos

Script avanzado - backup.sh
bash
#!/bin/bash

# Script de backup con validación de argumentos

# Función para mostrar ayuda
mostrar_ayuda() {
    echo "Uso: $0 [OPCIONES] DIRECTORIO_ORIGEN DIRECTORIO_DESTINO"
    echo ""
    echo "OPCIONES:"
    echo "  -h, --help        Mostrar esta ayuda"
    echo "  -v, --verbose     Modo verbose"
    echo "  -c, --compress    Comprimir backup"
    echo "  -t, --timestamp   Agregar timestamp al nombre"
    echo ""
    echo "Ejemplos:"
    echo "  $0 /home/usuario /backup"
    echo "  $0 -v -c /documentos /backup"
    echo "  $0 --verbose --timestamp /proyecto /backup"
    exit 0
}

# Verificar número mínimo de argumentos
if [ $# -lt 2 ]; then
    echo "Error: Se requieren al menos 2 argumentos"
    echo "Use '$0 --help' para ver la ayuda"
    exit 1
fi

# Variables por defecto
VERBOSE=false
COMPRESS=false
TIMESTAMP=false
ORIGEN=""
DESTINO=""

# Procesar argumentos
while [ $# -gt 0 ]; do
    case $1 in
        -h|--help)
            mostrar_ayuda
            ;;
        -v|--verbose)
            VERBOSE=true
            shift
            ;;
        -c|--compress)
            COMPRESS=true
            shift
            ;;
        -t|--timestamp)
            TIMESTAMP=true
            shift
            ;;
        -*)
            echo "Error: Opción desconocida $1"
            echo "Use '$0 --help' para ver opciones válidas"
            exit 1
            ;;
        *)
            # Argumentos posicionales
            if [ -z "$ORIGEN" ]; then
                ORIGEN="$1"
            elif [ -z "$DESTINO" ]; then
                DESTINO="$1"
            else
                echo "Error: Demasiados argumentos"
                exit 1
            fi
            shift
            ;;
    esac
done

# Validar que se proporcionaron origen y destino
if [ -z "$ORIGEN" ] || [ -z "$DESTINO" ]; then
    echo "Error: Debe especificar directorio origen y destino"
    exit 1
fi

# Validar que el directorio origen existe
if [ ! -d "$ORIGEN" ]; then
    echo "Error: El directorio origen '$ORIGEN' no existe"
    exit 1
fi

# Crear directorio destino si no existe
if [ ! -d "$DESTINO" ]; then
    mkdir -p "$DESTINO"
    [ $VERBOSE = true ] && echo "Directorio destino creado: $DESTINO"
fi

# Generar nombre de backup
BACKUP_NAME="backup"
if [ $TIMESTAMP = true ]; then
    BACKUP_NAME="${BACKUP_NAME}_$(date +%Y%m%d_%H%M%S)"
fi

# Mostrar configuración si verbose está activado
if [ $VERBOSE = true ]; then
    echo "=== CONFIGURACIÓN DE BACKUP ==="
    echo "Origen: $ORIGEN"
    echo "Destino: $DESTINO"
    echo "Nombre: $BACKUP_NAME"
    echo "Comprimir: $COMPRESS"
    echo "Timestamp: $TIMESTAMP"
    echo "================================"
fi

# Realizar backup
if [ $COMPRESS = true ]; then
    tar -czf "$DESTINO/$BACKUP_NAME.tar.gz" -C "$(dirname "$ORIGEN")" "$(basename "$ORIGEN")"
    [ $VERBOSE = true ] && echo "Backup comprimido creado: $DESTINO/$BACKUP_NAME.tar.gz"
else
    cp -r "$ORIGEN" "$DESTINO/$BACKUP_NAME"
    [ $VERBOSE = true ] && echo "Backup creado: $DESTINO/$BACKUP_NAME"
fi

echo "Backup completado exitosamente"

Comando shift y Argumentos Dinámicos

El comando shift permite desplazar los argumentos posicionales hacia la izquierda, útil para procesar argumentos de longitud variable:

Ejemplo con shift - procesador.sh
bash
#!/bin/bash

# Script que procesa múltiples archivos
echo "Procesando archivos..."

# Contar archivos iniciales
echo "Total de archivos: $#"

# Procesar cada archivo
contador=1
while [ $# -gt 0 ]; do
    echo "[$contador] Procesando: $1"
    
    # Aquí iría la lógica de procesamiento
    if [ -f "$1" ]; then
        echo "    ✓ Archivo válido"
        wc -l "$1" 2>/dev/null && echo "    ✓ Líneas contadas"
    else
        echo "    ✗ Archivo no encontrado"
    fi
    
    # Desplazar argumentos
    shift
    ((contador++))
done

echo "Procesamiento completado"

Argumentos con Valores

Manejo de argumentos con valores - config.sh
bash
#!/bin/bash

# Script de configuración con argumentos que requieren valores

# Variables por defecto
HOST=""
PORT=22
USER=""
PASSWORD=""
TIMEOUT=30

# Función de ayuda
mostrar_uso() {
    cat << EOF
Uso: $0 [OPCIONES]

OPCIONES:
    -h, --host HOST       Servidor host (requerido)
    -p, --port PORT       Puerto (default: 22)
    -u, --user USER       Usuario (requerido)
    -w, --password PASS   Contraseña
    -t, --timeout SECS    Timeout en segundos (default: 30)
    --help               Mostrar esta ayuda

Ejemplo:
    $0 -h servidor.com -u admin -p 2222 -t 60
EOF
}

# Procesar argumentos
while [ $# -gt 0 ]; do
    case $1 in
        -h|--host)
            if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
                HOST="$2"
                shift 2
            else
                echo "Error: --host requiere un valor"
                exit 1
            fi
            ;;
        -p|--port)
            if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
                PORT="$2"
                shift 2
            else
                echo "Error: --port requiere un valor"
                exit 1
            fi
            ;;
        -u|--user)
            if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
                USER="$2"
                shift 2
            else
                echo "Error: --user requiere un valor"
                exit 1
            fi
            ;;
        -w|--password)
            if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
                PASSWORD="$2"
                shift 2
            else
                echo "Error: --password requiere un valor"
                exit 1
            fi
            ;;
        -t|--timeout)
            if [ -n "$2" ] && [ "${2:0:1}" != "-" ] && [[ "$2" =~ ^[0-9]+$ ]]; then
                TIMEOUT="$2"
                shift 2
            else
                echo "Error: --timeout requiere un número válido"
                exit 1
            fi
            ;;
        --help)
            mostrar_uso
            exit 0
            ;;
        *)
            echo "Error: Opción desconocida '$1'"
            mostrar_uso
            exit 1
            ;;
    esac
done

# Validar argumentos requeridos
if [ -z "$HOST" ]; then
    echo "Error: Se requiere especificar --host"
    exit 1
fi

if [ -z "$USER" ]; then
    echo "Error: Se requiere especificar --user"
    exit 1
fi

# Mostrar configuración
echo "=== CONFIGURACIÓN ==="
echo "Host: $HOST"
echo "Port: $PORT"
echo "User: $USER"
echo "Password: ${PASSWORD:+***configurada***}"
echo "Timeout: $TIMEOUT segundos"
echo "===================="

# Aquí iría la lógica principal del script
echo "Conectando a $USER@$HOST:$PORT..."
echo "Configuración aplicada correctamente"

Validación Avanzada de Argumentos

Validación robusta - validador.sh
bash
#!/bin/bash

# Funciones de validación

# Validar que sea un número entero
validar_entero() {
    local valor="$1"
    local nombre="$2"
    
    if ! [[ "$valor" =~ ^-?[0-9]+$ ]]; then
        echo "Error: $nombre debe ser un número entero válido"
        return 1
    fi
    return 0
}

# Validar que sea un número entero positivo
validar_entero_positivo() {
    local valor="$1"
    local nombre="$2"
    
    if ! validar_entero "$valor" "$nombre"; then
        return 1
    fi
    
    if [ "$valor" -le 0 ]; then
        echo "Error: $nombre debe ser mayor que 0"
        return 1
    fi
    return 0
}

# Validar formato de email
validar_email() {
    local email="$1"
    local patron="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    
    if [[ ! "$email" =~ $patron ]]; then
        echo "Error: Formato de email inválido: $email"
        return 1
    fi
    return 0
}

# Validar que un archivo existe y es legible
validar_archivo() {
    local archivo="$1"
    
    if [ ! -f "$archivo" ]; then
        echo "Error: El archivo '$archivo' no existe"
        return 1
    fi
    
    if [ ! -r "$archivo" ]; then
        echo "Error: No se puede leer el archivo '$archivo'"
        return 1
    fi
    return 0
}

# Validar que un directorio existe y es escribible
validar_directorio() {
    local directorio="$1"
    
    if [ ! -d "$directorio" ]; then
        echo "Error: El directorio '$directorio' no existe"
        return 1
    fi
    
    if [ ! -w "$directorio" ]; then
        echo "Error: No se puede escribir en el directorio '$directorio'"
        return 1
    fi
    return 0
}

# Validar rango de puerto
validar_puerto() {
    local puerto="$1"
    
    if ! validar_entero_positivo "$puerto" "puerto"; then
        return 1
    fi
    
    if [ "$puerto" -lt 1 ] || [ "$puerto" -gt 65535 ]; then
        echo "Error: El puerto debe estar entre 1 y 65535"
        return 1
    fi
    return 0
}

# Ejemplo de uso con validación
main() {
    local email=""
    local puerto=""
    local archivo=""
    local directorio=""
    
    # Procesar argumentos (versión simplificada)
    while [ $# -gt 0 ]; do
        case $1 in
            --email)
                email="$2"
                shift 2
                ;;
            --puerto)
                puerto="$2"
                shift 2
                ;;
            --archivo)
                archivo="$2"
                shift 2
                ;;
            --directorio)
                directorio="$2"
                shift 2
                ;;
            *)
                echo "Opción desconocida: $1"
                exit 1
                ;;
        esac
    done
    
    # Validar todos los campos
    local errores=0
    
    if [ -n "$email" ]; then
        if ! validar_email "$email"; then
            ((errores++))
        fi
    fi
    
    if [ -n "$puerto" ]; then
        if ! validar_puerto "$puerto"; then
            ((errores++))
        fi
    fi
    
    if [ -n "$archivo" ]; then
        if ! validar_archivo "$archivo"; then
            ((errores++))
        fi
    fi
    
    if [ -n "$directorio" ]; then
        if ! validar_directorio "$directorio"; then
            ((errores++))
        fi
    fi
    
    # Salir si hay errores
    if [ $errores -gt 0 ]; then
        echo "Se encontraron $errores error(es) en la validación"
        exit 1
    fi
    
    echo "Todas las validaciones pasaron correctamente"
}

# Ejecutar función principal con todos los argumentos
main "$@"

Tips y Mejores Prácticas

  • Siempre valida argumentos: Nunca asumas que los usuarios proporcionarán argumentos válidos
  • Proporciona ayuda: Incluye una opción --help en todos tus scripts
  • Usa valores por defecto: Define valores sensatos para argumentos opcionales
  • Maneja errores graciosamente: Proporciona mensajes de error claros y códigos de salida apropiados
  • Documenta tu script: Incluye comentarios y ejemplos de uso
  • Utiliza funciones: Separa la lógica de validación en funciones reutilizables

Consideraciones de Seguridad

  • Nunca pases contraseñas como argumentos de línea de comandos (son visibles en ps)
  • Valida y sanitiza todas las entradas antes de usarlas en comandos
  • Usa comillas dobles alrededor de variables que contengan rutas
  • Evita usar eval con argumentos del usuario

Ejercicios Prácticos

Ejercicio 1: Script de Copia de Seguridad

Crea un script que:

  • Acepte argumentos para directorio origen y destino
  • Incluya opciones para compresión y timestamp
  • Valide que los directorios existen
  • Proporcione una opción de ayuda detallada

Ejercicio 2: Generador de Reportes

Desarrolla un script que:

  • Procese múltiples archivos de log
  • Permita especificar formato de salida (txt, html, json)
  • Incluya filtros por fecha y nivel de error
  • Valide todos los argumentos de entrada