Valencia, 22/09/2026, G.B.
Cuando administramos servidores web como Apache, Nginx o LiteSpeed, los archivos de acceso (access.log) se convierten rápidamente en la fuente principal para identificar tráfico no deseado, bots malintencionados o intentos de fuerza bruta.
Aunque existen herramientas de análisis y cortafuegos dinámicos, en muchas ocasiones necesitamos realizar una limpieza rápida de un archivo de log directamente desde la consola, sin instalar dependencias adicionales ni recurrir a lenguajes externos como Python o Perl.
En este artículo veremos cómo construir un script en Bash puro que nos permita eliminar líneas enteras de un log basándonos en direcciones IP individuales o rangos mediante expresiones regulares (regex).
La herramienta clave: grep en modo extendido
Para filtrar contenido en GNU/Linux descartando líneas completas, el comando por excelencia es grep. En lugar de buscar lo que queremos conservar, le indicamos lo que queremos excluir mediante el parámetro -v.
Para manejar rangos o múltiples patrones en una sola pasada, utilizaremos dos modificadores esenciales:
-v(--revert-match): Invierte el resultado de la búsqueda, conservando únicamente las líneas que no coinciden con el patrón.-E(--extended-regexp): Activa las expresiones regulares extendidas, permitiéndonos usar operadores lógicos como la barra vertical (|) para el condicional «O» (OR) y corchetes[]para rangos numéricos.
Filtrado de rangos IP mediante Expresiones Regulares
A diferencia de las herramientas orientadas a red que interpretan notación CIDR (como /24), las utilidades del Shell procesan el archivo de log como texto plano. Por ello, traduciremos las subredes a expresiones regulares fijas.
A continuación, algunas equivalencias útiles para definir rangos dentro del patrón de filtrado:
| Rango deseado | Expresión Regular | Explicación |
Subred /24 completa | 192\.168\.1\. | Coincide con cualquier IP que comience por 192.168.1. |
Subred /16 completa | 10\.0\. | Coincide con cualquier IP que empiece por 10.0. |
Rango .0 a .9 | 172\.16\.0\.[0-9]\b | Filtra del 172.16.0.0 al 172.16.0.9 |
Rango .10 a .19 | 172\.16\.0\.1[0-9]\b | Filtra del 172.16.0.10 al 172.16.0.19 |
| IP única | 203\.0\.113\.45 | Filtra exclusivamente esa dirección |
Nota: Es fundamental escapar los puntos (
\.) con una barra invertida. De lo contrario, en expresiones regulares el punto actuaría como un comodín que representa cualquier carácter. Igualmente importante: los patrones que terminan en un dígito suelto (como[0-9]o1[0-9]) deben cerrarse con\b(límite de palabra), o de lo contrario también harán match con números más largos que empiecen por esos mismos dígitos.
El Script Bash: filtrar_log.sh
A continuación se muestra la estructura completa del script. Este toma como entrada el archivo de log original y genera un nuevo archivo limpio sin las líneas asociadas a las IP definidas.
#!/bin/bash
# ==============================================================================
# Script de filtrado de IPs y rangos en logs de acceso
# Publicado en Indaga.net
# ==============================================================================
# Validar que se introduzcan los argumentos necesarios
if [ "$#" -ne 2 ]; then
echo "Error: Argumentos insuficientes."
echo "Uso: $0 <log_origen> <log_destino>"
exit 1
fi
LOG_ORIGEN="$1"
LOG_DESTINO="$2"
# Verificar la existencia del archivo de origen
if [ ! -f "$LOG_ORIGEN" ]; then
echo "Error: El archivo '$LOG_ORIGEN' no existe."
exit 1
fi
# Definición de reglas y rangos a excluir (formato Regex)
PATRONES=(
"192\.168\.1\." # Subred 192.168.1.0/24
"10\.0\." # Subred 10.0.0.0/16
"172\.16\.0\.[0-9]\b" # IPs del 172.16.0.0 al 172.16.0.9
"172\.16\.0\.1[0-9]\b" # IPs del 172.16.0.10 al 172.16.0.19
"203\.0\.113\.45" # Dirección IP única estática
)
# Unir el array de patrones utilizando la barra vertical (|) como separador OR
PATRON_REGEX=$(IFS='|'; echo "${PATRONES[*]}")
# Ejecución del filtrado con grep
grep -v -E "$PATRON_REGEX" "$LOG_ORIGEN" > "$LOG_DESTINO"
RESULTADO=$?
# Confirmación de salida
# grep devuelve: 0 = hubo líneas tras filtrar, 1 = ninguna línea coincidió
# (log resultante vacío, no es un error), 2 = error real (lectura, etc.)
if [ "$RESULTADO" -le 1 ]; then
echo "Filtrado completado con éxito."
echo "Archivo limpio guardado en: $LOG_DESTINO"
else
echo "Ocurrió un error al procesar el archivo."
fi
Modo de empleo
- Guardar el archivo: Crea el script en tu servidor, por ejemplo con
nano filtrar_log.sh. - Asignar permisos de ejecución:
chmod +x filtrar_log.sh - Ejecutar el proceso: Pasa como primer argumento el log actual y como segundo el nombre del archivo resultante:
./filtrar_log.sh /var/log/nginx/access.log /var/log/nginx/access_limpio.log
Ejecución rápida en una sola línea (One-Liner)
Si no necesitas la estructura del script y buscas solucionar una tarea puntual en la terminal, puedes ejecutar el filtro directamente concatenando los patrones deseados:
grep -v -E "192\.168\.1\.|10\.0\.|172\.16\.0\.[0-9]\b" access.log > access_filtrado.logEsta alternativa utiliza únicamente las utilidades nativas de cualquier sistema POSIX, garantizando un rendimiento óptimo al procesar grandes volúmenes de texto sin sobrecargar los recursos del sistema.

