Cómo filtrar direcciones IP y rangos en logs Web usando solo Bash

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 deseadoExpresión RegularExplicación
Subred /24 completa192\.168\.1\.Coincide con cualquier IP que comience por 192.168.1.
Subred /16 completa10\.0\.Coincide con cualquier IP que empiece por 10.0.
Rango .0 a .9172\.16\.0\.[0-9]\bFiltra del 172.16.0.0 al 172.16.0.9
Rango .10 a .19172\.16\.0\.1[0-9]\bFiltra del 172.16.0.10 al 172.16.0.19
IP única203\.0\.113\.45Filtra 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] o 1[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

  1. Guardar el archivo: Crea el script en tu servidor, por ejemplo con nano filtrar_log.sh.
  2. Asignar permisos de ejecución:
    chmod +x filtrar_log.sh
  3. 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.log

Esta 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.