Operadores Lógicos y Comparación

Construye condiciones complejas combinando múltiples criterios

Módulo 3 ⏱️ 30-35 min 🔗 Operadores ⚡ Lógica 📚 Principiante

Introducción a los Operadores Lógicos

Los operadores lógicos te permiten combinar múltiples condiciones en una sola expresión, creando lógica compleja y sofisticada en tus scripts. Son fundamentales para tomar decisiones basadas en múltiples criterios simultáneamente.

En Bash, los operadores lógicos principales son:

&&
AND

Verdadero solo si ambas condiciones son verdaderas

||
OR

Verdadero si al menos una condición es verdadera

!
NOT

Invierte el valor de verdad de la condición

Operador AND (&&)

El operador && (AND lógico) retorna verdadero solo cuando ambas condiciones son verdaderas. Si la primera condición es falsa, la segunda ni siquiera se evalúa (evaluación perezosa).

Lógica del Operador AND
Condición 1
&&
Condición 2

Solo es verdadero si ambas condiciones son verdaderas

Condición 1 Condición 2 Resultado (&&)
Verdadero Verdadero Verdadero
Verdadero Falso Falso
Falso Verdadero Falso
Falso Falso Falso
# Ejemplo básico de AND
$ edad=25
$ tiene_licencia=true

# Verificar si puede conducir
$ if [[ $edad -ge 18 && $tiene_licencia == true ]]; then
    echo "Puede conducir legalmente"
fi
Puede conducir legalmente

# Ejemplo con archivos
$ archivo1="documento.txt"
$ archivo2="respaldo.txt"

# Verificar si ambos archivos existen
$ if [[ -f "$archivo1" && -f "$archivo2" ]]; then
    echo "Ambos archivos están disponibles"
    echo "Comparando archivos..."
    diff "$archivo1" "$archivo2"
else
    echo "Falta al menos uno de los archivos"
fi

# Ejemplo de evaluación perezosa
$ echo "Probando evaluación perezosa:"
$ if [[ false && echo "Esto no se ejecutará" ]]; then
    echo "Esta línea no aparecerá"
fi
Probando evaluación perezosa:
# Nota: "Esto no se ejecutará" no aparece porque la primera condición es falsa
Ejemplo práctico: Validación de usuario Copiar
#!/bin/bash
# Validación completa de usuario antes de operaciones críticas

usuario_actual=$(whoami)
directorio_trabajo="/home/$usuario_actual/proyectos"

# Verificar múltiples condiciones antes de proceder
if [[ "$usuario_actual" != "root" && -d "$directorio_trabajo" && -w "$directorio_trabajo" ]]; then
    echo "✅ Validación exitosa:"
    echo "   - Usuario no-root: $usuario_actual"
    echo "   - Directorio existe: $directorio_trabajo"
    echo "   - Permisos de escritura: Sí"
    echo
    echo "Procediendo con la operación..."
    
    # Crear proyecto de ejemplo
    proyecto_nuevo="$directorio_trabajo/proyecto_$(date +%Y%m%d)"
    mkdir -p "$proyecto_nuevo"
    echo "Proyecto creado en: $proyecto_nuevo"
    
else
    echo "❌ Error en validación:"
    [[ "$usuario_actual" == "root" ]] && echo "   - No ejecutes como root"
    [[ ! -d "$directorio_trabajo" ]] && echo "   - Directorio no existe: $directorio_trabajo"
    [[ ! -w "$directorio_trabajo" ]] && echo "   - Sin permisos de escritura en: $directorio_trabajo"
    exit 1
fi

Operador OR (||)

El operador || (OR lógico) retorna verdadero cuando al menos una de las condiciones es verdadera. Si la primera condición es verdadera, la segunda no se evalúa.

Condición 1 Condición 2 Resultado (||)
Verdadero Verdadero Verdadero
Verdadero Falso Verdadero
Falso Verdadero Verdadero
Falso Falso Falso
# Ejemplo básico de OR
$ sistema=$(uname -s)

# Verificar si es Linux o macOS
$ if [[ "$sistema" == "Linux" || "$sistema" == "Darwin" ]]; then
    echo "Sistema Unix-like detectado: $sistema"
    echo "Los comandos bash funcionarán correctamente"
fi
Sistema Unix-like detectado: Linux
Los comandos bash funcionarán correctamente

# Ejemplo con múltiples archivos de configuración
$ config_local="$HOME/.miapp.conf"
$ config_global="/etc/miapp.conf"
$ config_defecto="/usr/share/miapp/default.conf"

# Buscar archivo de configuración (prioridad: local > global > defecto)
$ if [[ -f "$config_local" ]]; then
    echo "Usando configuración local: $config_local"
    config_archivo="$config_local"
elif [[ -f "$config_global" ]]; then
    echo "Usando configuración global: $config_global"
    config_archivo="$config_global"
elif [[ -f "$config_defecto" ]]; then
    echo "Usando configuración por defecto: $config_defecto"
    config_archivo="$config_defecto"
else
    echo "❌ No se encontró ningún archivo de configuración"
    exit 1
fi

# Verificar múltiples comandos necesarios
$ if command -v git >/dev/null || command -v svn >/dev/null || command -v hg >/dev/null; then
    echo "Sistema de control de versiones disponible"
else
    echo "Instala git, svn o mercurial para continuar"
fi
Ejemplo práctico: Detector de dependencias Copiar
#!/bin/bash
# Detector inteligente de dependencias del sistema

echo "=== Detector de Dependencias ==="

# Función para verificar comandos
verificar_comando() {
    if command -v "$1" >/dev/null 2>&1; then
        echo "✅ $1 está disponible"
        return 0
    else
        echo "❌ $1 no está disponible"
        return 1
    fi
}

# Verificar herramientas de desarrollo esenciales
echo "Verificando herramientas de desarrollo..."
dev_tools=0

if verificar_comando "gcc" || verificar_comando "clang" || verificar_comando "tcc"; then
    echo "   Compilador C disponible"
    ((dev_tools++))
fi

if verificar_comando "make" || verificar_comando "ninja" || verificar_comando "bazel"; then
    echo "   Sistema de build disponible"
    ((dev_tools++))
fi

if verificar_comando "git" || verificar_comando "svn" || verificar_comando "hg"; then
    echo "   Control de versiones disponible"
    ((dev_tools++))
fi

# Verificar editores de texto
echo -e "\nVerificando editores de texto..."
if verificar_comando "vim" || verificar_comando "nano" || verificar_comando "emacs"; then
    echo "   Editor de texto en línea de comandos disponible"
    ((dev_tools++))
fi

# Evaluación final
echo -e "\n=== Resultado ==="
if [[ $dev_tools -ge 3 ]]; then
    echo "🎉 Entorno de desarrollo completo ($dev_tools/4 herramientas)"
elif [[ $dev_tools -ge 2 ]]; then
    echo "👍 Entorno de desarrollo básico ($dev_tools/4 herramientas)"
elif [[ $dev_tools -ge 1 ]]; then
    echo "⚠️  Entorno de desarrollo mínimo ($dev_tools/4 herramientas)"
else
    echo "❌ Entorno de desarrollo incompleto (0/4 herramientas)"
fi

Operador NOT (!)

El operador ! (NOT lógico) invierte el resultado de una condición. Si la condición es verdadera, ! la hace falsa, y viceversa.

Lógica del Operador NOT
!
Condición

Invierte el valor de verdad

Condición Resultado (!)
Verdadero Falso
Falso Verdadero
# Ejemplos básicos de NOT
$ archivo="config.txt"

# Verificar si un archivo NO existe
$ if [[ ! -f "$archivo" ]]; then
    echo "El archivo $archivo no existe"
    echo "Creando archivo con configuración por defecto..."
    echo "debug=false" > "$archivo"
    echo "timeout=30" >> "$archivo"
fi

# Verificar si NO somos root
$ if [[ ! "$EUID" -eq 0 ]]; then
    echo "Ejecutando como usuario normal: $(whoami)"
else
    echo "⚠️  Ejecutando como root - ten cuidado"
fi

# Verificar si una variable NO está vacía
$ mi_variable="contenido"
$ if [[ ! -z "$mi_variable" ]]; then
    echo "La variable tiene contenido: $mi_variable"
fi

# Combinando NOT con otros operadores
$ usuario=$(whoami)
$ if [[ "$usuario" != "root" && ! -w "/etc/passwd" ]]; then
    echo "Usuario normal sin permisos administrativos"
fi
Usos Comunes del Operador NOT
  • Validaciones: Verificar que algo NO esté presente
  • Seguridad: Confirmar que NO somos root
  • Archivos: Acciones cuando archivos NO existen
  • Procesos: Detectar cuando servicios NO están corriendo

Combinando Operadores

Los operadores lógicos se pueden combinar para crear expresiones complejas. Es importante entender la precedencia de operadores para escribir condiciones correctas.

Precedencia de Operadores (mayor a menor)
1
! (NOT) - Mayor precedencia
2
&& (AND) - Precedencia media
3
|| (OR) - Menor precedencia
# Ejemplos de combinación de operadores
$ edad=25
$ tiene_licencia=true
$ tiene_seguro=false

# Ejemplo 1: Precedencia natural (sin paréntesis)
# Se evalúa como: (edad >= 18 && tiene_licencia == true) || tiene_seguro == true
$ if [[ $edad -ge 18 && $tiene_licencia == true || $tiene_seguro == true ]]; then
    echo "Caso 1: Puede conducir"
fi

# Ejemplo 2: Usando paréntesis para cambiar precedencia
# Se evalúa como: edad >= 18 && (tiene_licencia == true || tiene_seguro == true)
$ if [[ $edad -ge 18 && ( $tiene_licencia == true || $tiene_seguro == true ) ]]; then
    echo "Caso 2: Mayor de edad con licencia O seguro"
fi

# Ejemplo 3: Negación compleja
$ usuario=$(whoami)
$ if [[ ! ( "$usuario" == "root" || "$usuario" == "admin" ) ]]; then
    echo "Usuario normal (no es root ni admin)"
fi

# Ejemplo 4: Combinación muy compleja
$ archivo_config="/etc/myapp.conf"
$ archivo_backup="/etc/myapp.conf.bak"
$ servicio_activo=$(systemctl is-active myapp 2>/dev/null || echo "inactive")

$ if [[ ( -f "$archivo_config" && -r "$archivo_config" ) && 
        ( ! -f "$archivo_backup" || -w "$(dirname "$archivo_backup")" ) &&
        "$servicio_activo" != "active" ]]; then
    echo "Condiciones óptimas para actualizar configuración"
fi
Buenas Prácticas
  • Usa paréntesis para clarificar la precedencia, incluso si no es necesario
  • Divide expresiones muy complejas en variables intermedias
  • Comenta expresiones complejas para explicar la lógica
  • Prueba cada parte de la expresión por separado durante el desarrollo

Operadores de Comparación Avanzados

Más allá de las comparaciones básicas, Bash ofrece operadores avanzados para casos específicos:

Comparaciones con Patrones

# Comparación con patrones usando [[ ]]
$ nombre_archivo="documento.pdf"

$ if [[ $nombre_archivo == *.pdf ]]; then
    echo "Es un archivo PDF"
fi

$ if [[ $nombre_archivo == doc* ]]; then
    echo "El nombre empieza con 'doc'"
fi

# Múltiples patrones
$ extension="${nombre_archivo##*.}"
$ if [[ $extension == @(pdf|doc|docx|txt) ]]; then
    echo "Formato de documento reconocido: $extension"
fi

# Expresiones regulares con =~
$ email="[email protected]"
$ patron_email="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"

$ if [[ $email =~ $patron_email ]]; then
    echo "Email válido: $email"
    dominio="${email##*@}"
    echo "Dominio: $dominio"
fi

Comparaciones Aritméticas

# Comparaciones aritméticas con (( ))
$ numero=42

$ if (( numero > 0 && numero % 2 == 0 )); then
    echo "$numero es un número positivo y par"
fi

$ if (( numero >= 40 && numero <= 50 )); then
    echo "$numero está en el rango 40-50"
fi

# Operaciones más complejas
$ salario=45000
$ bonus=5000
$ impuestos=0.25

$ if (( (salario + bonus) * (1 - impuestos) > 35000 )); then
    echo "Salario neto después de impuestos es bueno"
    salario_neto=$(( (salario + bonus) * (1 - impuestos) ))
    echo "Salario neto: \$${salario_neto}"
fi

Casos Prácticos Avanzados

Sistema de validación robusto Copiar
#!/bin/bash
# Sistema de validación robusto para scripts críticos

# Variables de configuración
MIN_DISK_SPACE=1000000  # 1GB en KB
MIN_MEMORY=512000       # 512MB en KB
REQUIRED_TOOLS=("curl" "jq" "git")
LOG_FILE="/var/log/mi_script.log"

# Función de logging
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# Función de validación completa
validar_sistema() {
    local errores=0
    
    log_message "=== Iniciando validación del sistema ==="
    
    # 1. Verificar que no somos root (para seguridad)
    if [[ $EUID -eq 0 ]]; then
        log_message "❌ ERROR: No ejecutes este script como root"
        ((errores++))
    fi
    
    # 2. Verificar espacio en disco
    disco_disponible=$(df /tmp | tail -1 | awk '{print $4}')
    if [[ $disco_disponible -lt $MIN_DISK_SPACE ]]; then
        log_message "❌ ERROR: Espacio insuficiente en disco"
        log_message "   Requerido: ${MIN_DISK_SPACE}KB, Disponible: ${disco_disponible}KB"
        ((errores++))
    fi
    
    # 3. Verificar memoria disponible
    memoria_disponible=$(free | grep '^Mem:' | awk '{print $7}')
    if [[ $memoria_disponible -lt $MIN_MEMORY ]]; then
        log_message "❌ ERROR: Memoria insuficiente"
        log_message "   Requerida: ${MIN_MEMORY}KB, Disponible: ${memoria_disponible}KB"
        ((errores++))
    fi
    
    # 4. Verificar herramientas requeridas
    for tool in "${REQUIRED_TOOLS[@]}"; do
        if ! command -v "$tool" >/dev/null 2>&1; then
            log_message "❌ ERROR: Herramienta no encontrada: $tool"
            ((errores++))
        fi
    done
    
    # 5. Verificar conectividad de red
    if ! ping -c 1 8.8.8.8 >/dev/null 2>&1; then
        log_message "❌ ERROR: Sin conectividad de red"
        ((errores++))
    fi
    
    # 6. Verificar permisos de escritura
    if [[ ! -w "$(dirname "$LOG_FILE")" ]]; then
        echo "❌ ERROR: Sin permisos para escribir logs en $(dirname "$LOG_FILE")"
        ((errores++))
    fi
    
    # Resultado de la validación
    if [[ $errores -eq 0 ]]; then
        log_message "✅ Validación exitosa - Sistema listo"
        return 0
    else
        log_message "❌ Validación fallida - $errores errores encontrados"
        return 1
    fi
}

# Función principal
main() {
    # Validar sistema antes de proceder
    if validar_sistema; then
        log_message "Iniciando operación principal..."
        
        # Aquí iría la lógica principal del script
        sleep 2
        log_message "✅ Operación completada exitosamente"
    else
        log_message "❌ No se puede continuar debido a errores de validación"
        exit 1
    fi
}

# Ejecutar script
main
Analizador inteligente de logs Copiar
#!/bin/bash
# Analizador inteligente de logs con múltiples criterios

# Configuración
LOG_DIRS=("/var/log" "/var/log/nginx" "/var/log/apache2")
PATRONES_ERROR=("error" "Error" "ERROR" "fail" "FAIL" "critical" "CRITICAL")
PATRONES_WARNING=("warn" "Warning" "WARNING" "notice")
FECHA_HOY=$(date '+%Y-%m-%d')
FECHA_AYER=$(date -d yesterday '+%Y-%m-%d' 2>/dev/null || date -v-1d '+%Y-%m-%d' 2>/dev/null)

# Función para analizar un archivo de log
analizar_log() {
    local archivo="$1"
    local categoria="$2"
    
    if [[ ! -f "$archivo" || ! -r "$archivo" ]]; then
        return 1
    fi
    
    local total_lineas=$(wc -l < "$archivo")
    local errores_hoy=0
    local warnings_hoy=0
    
    # Contar errores de hoy
    for patron in "${PATRONES_ERROR[@]}"; do
        errores_encontrados=$(grep -i "$patron" "$archivo" | grep -c "$FECHA_HOY" 2>/dev/null || echo "0")
        ((errores_hoy += errores_encontrados))
    done
    
    # Contar warnings de hoy
    for patron in "${PATRONES_WARNING[@]}"; do
        warnings_encontrados=$(grep -i "$patron" "$archivo" | grep -c "$FECHA_HOY" 2>/dev/null || echo "0")
        ((warnings_hoy += warnings_encontrados))
    done
    
    # Determinar el estado del log
    if [[ $errores_hoy -gt 50 || ($errores_hoy -gt 10 && $warnings_hoy -gt 100) ]]; then
        estado="🔴 CRÍTICO"
    elif [[ $errores_hoy -gt 10 || $warnings_hoy -gt 50 ]]; then
        estado="🟡 ADVERTENCIA"
    elif [[ $errores_hoy -gt 0 || $warnings_hoy -gt 0 ]]; then
        estado="🟢 NORMAL"
    else
        estado="✅ LIMPIO"
    fi
    
    # Mostrar resumen
    echo "[$categoria] $archivo"
    echo "  Estado: $estado"
    echo "  Total líneas: $total_lineas"
    echo "  Errores hoy: $errores_hoy"
    echo "  Warnings hoy: $warnings_hoy"
    
    # Si hay muchos errores, mostrar los más recientes
    if [[ $errores_hoy -gt 5 ]]; then
        echo "  Errores recientes:"
        for patron in "${PATRONES_ERROR[@]}"; do
            grep -i "$patron" "$archivo" | grep "$FECHA_HOY" | tail -2 | sed 's/^/    /'
        done
    fi
    
    echo
}

# Función principal
main() {
    echo "=== Analizador Inteligente de Logs ==="
    echo "Fecha de análisis: $FECHA_HOY"
    echo
    
    local logs_analizados=0
    local logs_con_errores=0
    
    # Buscar y analizar logs en los directorios especificados
    for directorio in "${LOG_DIRS[@]}"; do
        if [[ -d "$directorio" && -r "$directorio" ]]; then
            echo "Analizando directorio: $directorio"
            
            # Buscar archivos de log comunes
            find "$directorio" -name "*.log" -o -name "access.log*" -o -name "error.log*" | while read -r archivo; do
                if analizar_log "$archivo" "$(basename "$directorio")"; then
                    ((logs_analizados++))
                fi
            done
        fi
    done
    
    echo "=== Resumen Final ==="
    echo "Logs analizados: $logs_analizados"
    echo "Análisis completado: $(date)"
}

# Ejecutar con validaciones previas
if [[ $(id -u) -eq 0 ]]; then
    echo "⚠️  Ejecutando como root - ten cuidado con los permisos"
fi

if ! command -v find >/dev/null || ! command -v grep >/dev/null; then
    echo "❌ Herramientas requeridas no disponibles (find, grep)"
    exit 1
fi

main

Ejercicios Prácticos

Ejercicio 1: Validador de Contraseñas

Crea un script que valide contraseñas usando múltiples criterios:

validador_password.sh Copiar
#!/bin/bash
# Plantilla para validador de contraseñas

echo "Introduce tu contraseña:"
read -s password

# Criterios de validación
longitud_minima=8
tiene_mayuscula=false
tiene_minuscula=false
tiene_numero=false
tiene_especial=false

# TODO: Implementar validaciones usando operadores lógicos

# 1. Verificar longitud
if [[ ${#password} ___ $longitud_minima ]]; then
    echo "✅ Longitud adecuada (${#password} caracteres)"
else
    echo "❌ Muy corta (mínimo $longitud_minima caracteres)"
fi

# 2. Verificar mayúsculas
if [[ $password =~ [A-Z] ]]; then
    tiene_mayuscula=true
    echo "✅ Contiene mayúsculas"
else
    echo "❌ Debe contener al menos una mayúscula"
fi

# 3. Verificar minúsculas
# TODO: Implementar verificación de minúsculas

# 4. Verificar números
# TODO: Implementar verificación de números

# 5. Verificar caracteres especiales
# TODO: Implementar verificación de caracteres especiales

# 6. Evaluación final usando AND lógico
if [[ $tiene_mayuscula == true ___ $tiene_minuscula == true ___ \
      $tiene_numero == true ___ $tiene_especial == true ]]; then
    echo "🎉 ¡Contraseña segura!"
else
    echo "❌ La contraseña no cumple todos los criterios"
fi
Ejercicio 2: Monitor de Sistema

Desarrolla un monitor que combine múltiples métricas del sistema:

monitor_sistema.sh Copiar
#!/bin/bash
# Monitor de sistema con alertas inteligentes

# Umbrales de alerta
CPU_CRITICO=90
CPU_ALTO=70
MEM_CRITICO=95
MEM_ALTO=80
DISK_CRITICO=95
DISK_ALTO=85

# Obtener métricas actuales
cpu_uso=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
memoria_uso=$(free | grep Mem | awk '{printf("%.1f", ($3/$2) * 100.0)}')
disco_uso=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')

# TODO: Implementar lógica de alertas usando operadores lógicos

echo "=== Monitor de Sistema ==="
echo "CPU: ${cpu_uso}%"
echo "Memoria: ${memoria_uso}%"
echo "Disco: ${disco_uso}%"
echo

# Determinar nivel de alerta
if [[ ___ ]]; then
    echo "🔴 ESTADO CRÍTICO"
    echo "Acción inmediata requerida"
elif [[ ___ ]]; then
    echo "🟡 ESTADO DE ADVERTENCIA"
    echo "Monitorear de cerca"
else
    echo "✅ ESTADO NORMAL"
fi

# DESAFÍO: Agregar más métricas (carga del sistema, procesos, red)
Ejercicio Avanzado: Desplegador Inteligente

Crea un script que decida cómo desplegar una aplicación basándose en múltiples condiciones: