Expresiones Regulares

Patrones poderosos para búsqueda, validación y manipulación avanzada de texto

Módulo 5 ⏱️ 35-40 min 🔍 Patrones ✅ Validación 📊 Intermedio

¿Qué son las Expresiones Regulares?

Las expresiones regulares (regex) son patrones de texto que describen conjuntos de cadenas de caracteres. Son una herramienta fundamental para buscar, validar, extraer y manipular texto de manera eficiente y flexible.

En el contexto de Bash y herramientas Unix, las expresiones regulares se utilizan con comandos como grep, sed, awk, y dentro de scripts para validación de datos y procesamiento de texto.

¿Por qué usar Regex?
  • Flexibilidad: Un solo patrón puede coincidir con múltiples variaciones
  • Precisión: Control exacto sobre qué texto buscar
  • Eficiencia: Procesamiento rápido de grandes volúmenes de texto
  • Reutilización: Patrones que funcionan en múltiples herramientas

Elementos Básicos

Las expresiones regulares se construyen con caracteres literales y metacaracteres especiales.

Caracteres Literales

# Búsqueda literal simple
$ echo "Hola mundo" | grep "mundo"
Hola mundo

# Caracteres literales coinciden exactamente
$ echo -e "gato\ngata\ngatito" | grep "gato"
gato

$ echo -e "123-456\n789-123\n456-789" | grep "123"
123-456
789-123

Metacaracteres Básicos

Metacaracter Descripción Ejemplo Coincide
. Cualquier carácter (excepto salto de línea) g.to gato, geto, g3to, g@to
* Cero o más del carácter anterior ga*to gto, gato, gaato, gaaato
^ Inicio de línea ^gato gato al inicio de línea
$ Final de línea gato$ gato al final de línea
[] Cualquier carácter del conjunto [aeiou] Cualquier vocal
[^] Cualquier carácter NO del conjunto [^0-9] Cualquier no-dígito
# Ejemplos prácticos de metacaracteres
$ echo -e "gato\ngeto\ngito\ngatito" | grep "g.to"
gato
geto

$ echo -e "color\ncolour\ncolr\ncollour" | grep "colo*ur"
colour
colr
collour

$ echo -e "inicio gato\ngato final\nmedio gato medio" | grep "^gato"
gato final

$ echo -e "archivo.txt\narchivo.log\narchivo.bak" | grep "\.txt$"
archivo.txt

Clases de Caracteres

Las clases de caracteres proporcionan formas convenientes de especificar conjuntos comunes de caracteres.

Clases POSIX

Clase POSIX Equivalente Descripción
[[:digit:]] [0-9] Dígitos
[[:alpha:]] [a-zA-Z] Letras alfabéticas
[[:alnum:]] [a-zA-Z0-9] Letras y dígitos
[[:space:]] [ \t\n\r\f\v] Espacios en blanco
[[:punct:]] Varios Signos de puntuación
[[:upper:]] [A-Z] Mayúsculas
[[:lower:]] [a-z] Minúsculas
# Ejemplos con clases de caracteres
$ echo -e "abc123\nXYZ789\nmixed123ABC" | grep "[[:digit:]]*$"
abc123
XYZ789
mixed123ABC

$ echo -e "MAYÚSCULA\nminúscula\nMixta" | grep "^[[:upper:]]*$"
MAYÚSCULA

$ echo -e "[email protected]\[email protected]\ninvalid-email" | grep "[[:alnum:]]*@[[:alnum:]]*\."
[email protected]
[email protected]

Cuantificadores

Los cuantificadores especifican cuántas veces debe aparecer el elemento anterior.

Cuantificador Significado Ejemplo Coincide
* 0 o más veces ab*c ac, abc, abbc, abbbc
+ 1 o más veces ab+c abc, abbc, abbbc
? 0 o 1 vez (opcional) ab?c ac, abc
{n} Exactamente n veces a{3} aaa
{n,} n o más veces a{2,} aa, aaa, aaaa, ...
{n,m} Entre n y m veces a{2,4} aa, aaa, aaaa
# Ejemplos con cuantificadores
$ echo -e "color\ncolour\ncolouur\ncoloouur" | grep "colou*r"
color
colour
colouur
coloouur

$ echo -e "abc\nabbc\nabbbc\nabbbbc" | grep -E "ab+c"
abc
abbc
abbbc
abbbbc

$ echo -e "123-4567\n123-45678\n123-456789\n123-4567890" | grep -E "[0-9]{3}-[0-9]{4}$"
123-4567

# Validar formato de IP
$ echo -e "192.168.1.1\n256.300.400.500\n10.0.0.1" | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"
192.168.1.1
256.300.400.500
10.0.0.1
Diferencias entre BRE y ERE

En grep básico (BRE), los cuantificadores +, ?, {}, y los grupos () deben escaparse con \. Con grep -E (ERE) no necesitan escape.

  • BRE: grep "ab\+c"
  • ERE: grep -E "ab+c"

Grupos y Captura

Los grupos permiten aplicar operaciones a múltiples caracteres como una unidad y capturar partes del texto coincidente.

Grupos Básicos

# Grupos con paréntesis
$ echo -e "ababab\ncdcdcd\nabcabc" | grep -E "(ab)+"
ababab
abcabc

# Alternación con grupos
$ echo -e "gato\nperro\ngatos\nperros" | grep -E "(gato|perro)s?"
gato
perro
gatos
perros

# Grupos anidados
$ echo -e "www.google.com\nftp.google.com\nmail.google.com" | grep -E "(www|ftp|mail)\.google\.com"
www.google.com
ftp.google.com
mail.google.com

Captura con sed

Grupos de captura en sed Copiar
# Capturar y reutilizar grupos
# Intercambiar nombre y apellido
echo "Juan Pérez" | sed 's/\([A-Za-z]*\) \([A-Za-z]*\)/\2, \1/'
# Resultado: Pérez, Juan

# Extraer dominio de email
echo "[email protected]" | sed 's/.*@\(.*\)/\1/'
# Resultado: ejemplo.com

# Formatear números de teléfono
echo "1234567890" | sed 's/\([0-9]\{3\}\)\([0-9]\{3\}\)\([0-9]\{4\}\)/(\1) \2-\3/'
# Resultado: (123) 456-7890

# Convertir fecha formato YYYY-MM-DD a DD/MM/YYYY
echo "2024-01-15" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/'
# Resultado: 15/01/2024

Patrones de Validación Comunes

Aquí tienes una colección de patrones regex útiles para validaciones comunes en administración de sistemas.

Cheat Sheet - Patrones Comunes
Direcciones y Redes
  • IPv4:
    ^([0-9]{1,3}\.){3}[0-9]{1,3}$
  • Email básico:
    ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
  • URL:
    ^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
  • Nombre de host:
    ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$
Formatos y Códigos
  • Fecha YYYY-MM-DD:
    ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
  • Hora HH:MM:
    ^([01][0-9]|2[0-3]):[0-5][0-9]$
  • Teléfono (US):
    ^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$
  • Código postal (US):
    ^[0-9]{5}(-[0-9]{4})?$
# Función de validación de IP en Bash
validate_ip() {
    local ip=$1
    if echo "$ip" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
        # Validar rangos (0-255)
        IFS='.' read -ra ADDR <<< "$ip"
        for i in "${ADDR[@]}"; do
            if [[ $i -gt 255 ]]; then
                return 1
            fi
        done
        return 0
    fi
    return 1
}

# Pruebas
validate_ip "192.168.1.1" && echo "IP válida" || echo "IP inválida"
validate_ip "256.300.1.1" && echo "IP válida" || echo "IP inválida"

# Validar emails en un archivo
validate_emails() {
    grep -E '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' "$1"
}

# Extraer números de teléfono de texto
extract_phones() {
    grep -oE '\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4}' "$1"
}

Regex en Herramientas de Sistema

Las expresiones regulares son especialmente útiles en administración de sistemas para análisis de logs, configuraciones y monitoreo.

Análisis de Logs

Patrones para logs Copiar
# Buscar errores 404 en log de Apache
grep -E "\" 404 [0-9]+" /var/log/apache2/access.log

# Encontrar intentos de login fallidos
grep -E "Failed (password|publickey)" /var/log/auth.log

# IPs que intentan múltiples conexiones
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" /var/log/auth.log | sort | uniq -c | sort -nr

# Extraer timestamps de logs syslog
grep -oE "^[A-Za-z]{3} [ 0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}" /var/log/syslog

# Buscar procesos que consumen mucha memoria
ps aux | grep -E "^[^ ]+ +[0-9]+ +[0-9.]+ +([2-9][0-9]|[0-9]{3,})\.[0-9]"

Validación de Configuraciones

# Validar configuración de red
$ cat /etc/network/interfaces | grep -E "^(auto|iface|address|netmask|gateway)"
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1

# Verificar formato de entradas en /etc/hosts
$ grep -vE "^#|^$" /etc/hosts | grep -vE "^([0-9]{1,3}\.){3}[0-9]{1,3}[[:space:]]+[a-zA-Z0-9.-]+$" && echo "Formato incorrecto encontrado"

# Buscar usuarios con shell válido
$ grep -E ":/bin/(bash|zsh|sh|dash)$" /etc/passwd | cut -d: -f1
root
usuario1
usuario2

Script de Monitoreo

Monitor de sistema con regex Copiar
#!/bin/bash
# monitor_system.sh - Monitor básico del sistema usando regex

LOG_FILE="/tmp/system_monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

echo "=== Monitor del Sistema - $DATE ===" >> "$LOG_FILE"

# 1. Verificar procesos críticos
CRITICAL_PROCS=("sshd" "apache2" "mysql" "nginx")
for proc in "${CRITICAL_PROCS[@]}"; do
    if ! ps aux | grep -E "[^]]$proc" > /dev/null; then
        echo "$DATE: ALERTA - Proceso $proc no está ejecutándose" >> "$LOG_FILE"
    fi
done

# 2. Verificar uso de memoria alta
HIGH_MEM_PROCS=$(ps aux | awk 'NR>1 {print $4, $11}' | grep -E "^([5-9][0-9]|[0-9]{3,})\.[0-9]")
if [[ -n "$HIGH_MEM_PROCS" ]]; then
    echo "$DATE: ADVERTENCIA - Procesos con alto uso de memoria:" >> "$LOG_FILE"
    echo "$HIGH_MEM_PROCS" >> "$LOG_FILE"
fi

# 3. Verificar intentos de login sospechosos
FAILED_LOGINS=$(grep -E "Failed password.*$(date '+%b %d')" /var/log/auth.log 2>/dev/null | wc -l)
if [[ $FAILED_LOGINS -gt 10 ]]; then
    echo "$DATE: ALERTA - $FAILED_LOGINS intentos de login fallidos hoy" >> "$LOG_FILE"
fi

# 4. Verificar espacio en disco
df -h | grep -E "^/dev/" | while read line; do
    usage=$(echo "$line" | grep -oE "[0-9]+%" | grep -oE "[0-9]+")
    if [[ $usage -gt 80 ]]; then
        device=$(echo "$line" | cut -d' ' -f1)
        echo "$DATE: ADVERTENCIA - Disco $device al $usage% de capacidad" >> "$LOG_FILE"
    fi
done

# 5. Verificar conectividad de red
if ! ping -c 1 8.8.8.8 > /dev/null 2>&1; then
    echo "$DATE: ALERTA - Sin conectividad a Internet" >> "$LOG_FILE"
fi

echo "Monitor completado - Revisa $LOG_FILE para detalles"

Ejercicios Prácticos

Ejercicio 1: Validador de Datos

Crea un script que valide diferentes tipos de datos:

Validador multiuso Copiar
#!/bin/bash
# validator.sh - Validador de datos usando regex

validate_ip() {
    echo "$1" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$' && {
        IFS='.' read -ra octets <<< "$1"
        for octet in "${octets[@]}"; do
            [[ $octet -le 255 ]] || return 1
        done
        return 0
    }
    return 1
}

validate_email() {
    echo "$1" | grep -qE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
}

validate_date() {
    echo "$1" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
}

validate_phone() {
    echo "$1" | grep -qE '^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$|^[0-9]{3}-[0-9]{3}-[0-9]{4}$'
}

# Función principal
validate_data() {
    local type=$1
    local data=$2
    
    case $type in
        ip)
            validate_ip "$data" && echo "✓ IP válida" || echo "✗ IP inválida"
            ;;
        email)
            validate_email "$data" && echo "✓ Email válido" || echo "✗ Email inválido"
            ;;
        date)
            validate_date "$data" && echo "✓ Fecha válida" || echo "✗ Fecha inválida"
            ;;
        phone)
            validate_phone "$data" && echo "✓ Teléfono válido" || echo "✗ Teléfono inválido"
            ;;
        *)
            echo "Tipo no soportado. Use: ip, email, date, phone"
            return 1
            ;;
    esac
}

# Pruebas
echo "=== Pruebas de Validación ==="
validate_data ip "192.168.1.1"
validate_data ip "256.300.400.500"
validate_data email "[email protected]"
validate_data email "email-invalido"
validate_data date "2024-01-15"
validate_data date "2024/01/15"
validate_data phone "(555) 123-4567"
validate_data phone "555-123-4567"
Ejercicio 2: Analizador de Logs

Analiza logs del sistema con patrones regex avanzados:

Analizador de logs Copiar
#!/bin/bash
# log_analyzer.sh - Análisis avanzado de logs

# Crear log de prueba
cat > sample.log << 'EOF'
2024-01-15 10:15:22 192.168.1.100 INFO  User john logged in
2024-01-15 10:16:33 10.0.0.15     ERROR Database connection failed
2024-01-15 10:17:44 192.168.1.100 WARN  High memory usage: 85%
2024-01-15 10:18:55 172.16.0.50   INFO  User mary logged in
2024-01-15 10:19:11 192.168.1.100 ERROR Authentication failed for user admin
2024-01-15 10:20:22 10.0.0.15     FATAL System crash detected
2024-01-15 10:21:33 192.168.1.200 INFO  Backup completed successfully
EOF

echo "=== Análisis de Logs ==="

# 1. Contar eventos por nivel
echo "Eventos por nivel de log:"
grep -oE "(INFO|WARN|ERROR|FATAL)" sample.log | sort | uniq -c | sort -nr

# 2. IPs más activas
echo -e "\nIPs más activas:"
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" sample.log | sort | uniq -c | sort -nr

# 3. Eventos críticos (ERROR y FATAL)
echo -e "\nEventos críticos:"
grep -E "(ERROR|FATAL)" sample.log | 
while IFS= read -r line; do
    timestamp=$(echo "$line" | grep -oE "^[0-9-]+ [0-9:]+")
    ip=$(echo "$line" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}")
    level=$(echo "$line" | grep -oE "(ERROR|FATAL)")
    message=$(echo "$line" | sed -E 's/^[0-9-]+ [0-9:]+ [0-9.]+ [A-Z]+ +//')
    printf "%-19s %-15s %-5s %s\n" "$timestamp" "$ip" "$level" "$message"
done

# 4. Buscar patrones de ataque
echo -e "\nPosibles intentos de ataque:"
grep -iE "(failed|attack|intrusion|hack|exploit)" sample.log

# 5. Análisis temporal (eventos por hora)
echo -e "\nEventos por hora:"
grep -oE "[0-9]{2}:" sample.log | sort | uniq -c