¿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
# 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
# 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
#!/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:
#!/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:
#!/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