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
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
#!/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
#!/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
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)
# 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
#!/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
#!/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:
#!/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:
#!/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:
- Tipo de entorno (desarrollo, staging, producción)
- Recursos del sistema disponibles
- Estado de servicios dependientes
- Existencia de respaldos previos
- Hora del día (evitar despliegues en horario pico)