Encadenar grep, sed y awk para procesar archivos de log: tutorial paso a paso

DUGLAS MORENODUGLAS MORENO 👁 22

Introducción

Cuando trabajás con sistemas en producción, los archivos de log son una de las fuentes más valiosas de información. Sin embargo, suelen ser voluminosos y contener líneas que no nos interesan. En este tutorial vas a aprender a filtrar, transformar y extraer datos de esos archivos usando la potencia combinada de grep, sed y awk. El enfoque es práctico: partiremos de un archivo de log de ejemplo y, en cada etapa, mostraremos el comando utilizado y la salida esperada.

A quién está dirigido

Este artículo está pensado para desarrolladores, administradores de sistemas y devops que ya tengan nociones básicas de la línea de comandos pero quieran profundizar en el uso de estas tres herramientas clásicas de Unix. No se requieren conocimientos avanzados de programación; basta con saber navegar en una terminal.

Qué vas a aprender

  • Cómo usar grep para seleccionar líneas que coincidan con un patrón.
  • Cómo aplicar sed para realizar sustituciones, eliminaciones y otras transformaciones de texto.
  • Cómo emplear awk para extraer campos, realizar cálculos y generar reportes.
  • Cómo encadenar estos comandos mediante tuberías (|) para crear flujos de procesamiento eficientes.
  • Buenas prácticas y errores comunes que debés evitar.

1. Preparación del entorno y ejemplo de log

Para que el tutorial sea concreto, vamos a trabajar con un archivo de log ficticio llamado app.log. Este archivo simula el salida de una aplicación web que registra peticiones HTTP, errores y eventos de seguridad.

Creá el archivo con el siguiente contenido (puedes copiarlo y pegarlo en tu terminal):

cat > app.log <<'EOF'
2025-09-24 10:12:01 INFO  [request] GET /api/users 200 123ms
2025-09-24 10:12:05 WARN  [request] POST /api/login 401 45ms
2025-09-24 10:12:07 ERROR [db]      Connection timeout
2025-09-24 10:12:10 INFO  [request] GET /api/products 200 89ms
2025-09-24 10:12:12 INFO  [request] GET /api/users/42 200 150ms
2025-09-24 10:12:15 WARN  [auth]    Invalid token for user 7
2025-09-24 10:12:18 ERROR [api]     NullPointerException in handler
2025-09-24 10:12:20 INFO  [request] DELETE /api/users/42 204 30ms
2025-09-24 10:12:22 INFO  [request] GET /api/orders 200 210ms
2025-09-24 10:12:25 ERROR [db]      Deadlock detected
2025-09-24 10:12:28 INFO  [request] PUT /api/products/5 200 110ms
2025-09-24 10:12:30 WARN  [request] GET /api/admin 403 60ms
EOF

Este archivo contiene columnas separadas por espacios o tabs: fecha, hora, nivel, componente entre corchetes, mensaje y, en algunos casos, códigos de estado o tiempos de respuesta. Vamos a usar este formato para demostrar cada herramienta.


2. Filtrado básico con grep

grep (Global Regular Expression Print) es la herramienta ideal para seleccionar líneas que contengan un determinado patrón. Su sintaxis básica es:

grep 'patrón' archivo

2.1. Buscar todas las líneas de error

Queremos ver solo las entradas cuyo nivel sea ERROR. El patrón es sencillo: la palabra ERROR seguida de un espacio.

grep 'ERROR' app.log

Salida esperada:

2025-09-24 10:12:07 ERROR [db]      Connection timeout
2025-09-24 10:12:18 ERROR [api]     NullPointerException in handler
2025-09-24 10:12:25 ERROR [db]      Deadlock detected

2.2. Filtrar por componente y nivel

Supongamos que nos interesan los errores de la base de datos ([db]). Podemos combinar dos patrones usando una expresión regular:

grep 'ERROR.*\[db\]' app.log

Salida esperada:

2025-09-24 10:12:07 ERROR [db]      Connection timeout
2025-09-24 10:12:25 ERROR [db]      Deadlock detected

Nota: el corchete [ y ] tienen significado especial en regex, por eso los escapamos con \.

2.3. Excluir ciertas líneas

A veces es más útil mostrar todo menos lo que no queremos. La opción -v invierte el match.

grep -v 'WARN' app.log

Salida esperada:

Todas las líneas excepto aquellas que contienen WARN (las dos líneas de advertencia desaparecen).


3. Transformación de texto con sed

sed (Stream Editor) permite modificar el contenido de cada línea mientras pasa por el flujo. Las operaciones más comunes son sustituciones (s), borrado (d) e inserción (i).

3.1. Sustituir el formato de fecha

Queremos cambiar el formato de fecha de YYYY-MM-DD a DD/MM/YYYY. La fecha está al inicio de la línea, seguida de un espacio y la hora.

sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/' app.log

Salida esperada (primeras líneas):

24/09/2025 10:12:01 INFO  [request] GET /api/users 200 123ms
24/09/2025 10:12:05 WARN  [request] POST /api/login 401 45ms
24/09/2025 10:12:07 ERROR [db]      Connection timeout
...

Explicación: capturamos grupos con \( ... \) y los reordenamos en la sección de reemplazo.

3.2. Eliminar el componente entre corchetes

Si solo nos interesa el mensaje y no el componente, podemos borrar la sección que aparece entre [ y ] incluyendo los corchetes.

sed 's/\[[^]]*\]//g' app.log

Salida esperada:

2025-09-24 10:12:01 INFO   GET /api/users 200 123ms
2025-09-24 10:12:05 WARN   POST /api/login 401 45ms
2025-09-24 10:12:07 ERROR      Connection timeout
...

Nota: [^]]* significa "cualquier carácter que no sea ]" repetido cero o más veces.

3.3. Añadir un prefijo a cada línea

Para hacer más legible el registro, podemos anteponer la cadena [LOG] a cada línea.

sed 's/^/\[LOG\] /' app.log

Salida esperada:

[LOG] 2025-09-24 10:12:01 INFO  [request] GET /api/users 200 123ms
[LOG] 2025-09-24 10:12:05 WARN  [request] POST /api/login 401 45ms
[LOG] 2025-09-24 10:12:07 ERROR [db]      Connection timeout
...

4. Extracción y procesamiento con awk

awk es un lenguaje de programación orientado al procesamiento de columnas. Por defecto, separa los campos por espacios o tabs ($1, $2, …). Nos permite realizar cálculos, condicionales y generar reportes.

4.1. Mostrar solo la hora y el mensaje

Supongamos que queremos un resumen que muestre la hora (campo $2) y todo lo que venga después del componente (a partir del campo $5). Podemos usar una impresión personalizada:

awk '{print $2, $5, $6, $7, $8}' app.log

Salida esperada:

10:12:01 INFO [request] GET /api/users 200
10:12:05 WARN [request] POST /api/login 401
10:12:07 ERROR [db] Connection timeout
10:12:10 INFO [request] GET /api/products 200
10:12:12 INFO [request] GET /api/users/42 200
10:12:15 WARN [auth] Invalid token for user 7
10:12:18 ERROR [api] NullPointerException in handler
10:12:20 INFO [request] DELETE /api/users/42 204
10:12:22 INFO [request] GET /api/orders 200
10:12:25 ERROR [db] Deadlock detected
10:12:28 INFO [request] PUT /api/products/5 200
10:12:30 WARN [request] GET /api/admin 403

Nota: awk trata cada grupo de espacios como separador, por eso el mensaje puede quedar dividido en varios campos; aquí imprimimos hasta $8 para capturar la mayor parte.

4.2. Contar cuántas líneas hay por nivel

Podemos usar un array asociativo para acumular conteos:

awk '{nivel[$3]++} END {for (n in nivel) print n, nivel[n]}' app.log

Salida esperada:

INFO 5
WARN 3
ERROR 4

4.3. Calcular el tiempo medio de respuesta para las peticiones con código 200

Solo las líneas que tengan un código de estado numérico (como 200, 401, etc.) lo tienen en el campo antes del último (el que termina con ms). Vamos a filtrar aquellas que tengan 200 y luego extraer el número antes de ms.

awk '$9 == 200 {split($10, a, "ms"); suma+=a[1]; count++} END {if (count>0) print "Media respuesta 200:" suma/count "ms"}' app.log

Salida esperada:

Media respuesta 200: 118.5ms

Explicación: $9 es el código de estado, $10 es algo como 123ms. Dividimos por ms y sumamos.


5. Encadenamiento completo: grep → sed → awk

Ahora vamos a combinar las tres herramientas para obtener un reporte específico: listar todas las peticiones GET que terminaron con código 200, mostrar la hora, la ruta y el tiempo de respuesta, y ordenar el resultado por tiempo de respuesta descendente.

Paso 1: Filtrar con grep

Seleccionamos líneas que contengan GET y terminen con 200 antes del ms.

grep 'GET' app.log | grep ' 200 '

Salida intermedia:

2025-09-24 10:12:01 INFO  [request] GET /api/users 200 123ms
2025-09-24 10:12:10 INFO  [request] GET /api/products 200 89ms
2025-09-24 10:12:12 INFO  [request] GET /api/users/42 200 150ms
2025-09-24 10:12:22 INFO  [request] GET /api/orders 200 210ms
2025-09-24 10:12:28 INFO  [request] PUT /api/products/5 200 110ms

(Nota: la última línea es un PUT; la filtramos en el siguiente paso.)

Paso 2: Eliminar las que no son GET con sed (opcional)

Podemos usar sed para dejar solo las que realmente empiezan con GET después del componente. Una forma sencilla es volver a usar grep, pero vamos a mostrar cómo quedaría con sed:

sed -n '/\sGET\/.* 200 /p' app.log

Salida esperada:

2025-09-24 10:12:01 INFO  [request] GET /api/users 200 123ms
2025-09-24 10:12:10 INFO  [request] GET /api/products 200 89ms
2025-09-24 10:12:12 INFO  [request] GET /api/users/42 200 150ms
2025-09-24 10:12:22 INFO  [request] GET /api/orders 200 210ms

Paso 3: Extraer hora, ruta y tiempo con awk

Queremos imprimir: hora ($2), ruta ($6) y tiempo ($10 sin el ms).

awk '{gsub(/ms/,"",$10); print $2, $6, $10}' app.log

Aplicándolo sobre la salida anterior obtenemos:

10:12:01 /api/users 123
10:12:10 /api/products 89
10:12:12 /api/users/42 150
10:12:22 /api/orders 210

Paso 4: Ordenar por tiempo de respuesta (descendente)

Finalmente usamos sort (no es parte de las tres herramientas, pero suele ir al final) para ordenar por el tercer campo numérico, de mayor a menor:

... | sort -k3 -nr

Salida final esperada:

10:12:22 /api/orders 210
10:12:12 /api/users/42 150
10:12:01 /api/users 123
10:12:10 /api/products 89

Flujo completo en una sola tubería

Podemos escribir todo en una línea:

grep 'GET' app.log | grep ' 200 ' | awk '{gsub(/ms/,"",$10); print $2, $6, $10}' | sort -k3 -nr

Esto muestra cómo el poder de cada herramienta se potencia al encadenarlas.


6. Buenas prácticas y errores comunes

Buenas prácticas

  1. Usar comillas simples alrededor de patrones que contengan espacios o caracteres especiales ('patrón'). Esto evita que el shell interprete símbolos como $, *, \.
  2. Prefiere grep -E o awk con expresiones regulares cuando necesites lógica compleja; no intentes hacer todo con múltiples grep.
  3. Aprovecha las variables de entorno de awk (FS, OFS) para cambiar el separador de campo cuando tus logs usan otro delimitador (por ejemplo, | o ,).
  4. Prueba cada etapa por separado antes de encadenar. Usa head o less para verificar la salida intermedia.
  5. Documenta tus tuberías con comentarios si las vas a guardar en scripts. Un # al inicio de línea explica por qué se usó cada filtro.

Errores comunes

  • Olvidar escapar corchetes en expresiones regulares de grep o sed. Recuerda que [ y ] tienen significado especial; usa \[ y \] para buscarlos literalmente.
  • Confundir los campos de awk cuando hay espacios múltiples. awk colapsa secuencias de espacios/tabs en un solo separador, pero si tu log usa tabs y espacios mezclados, verifica con cat -A o od -c.
  • Usar sed para hacer operaciones que son más simples con awk (como sumar o contar). Cada herramienta tiene su fortaleza; sed es excelente para sustituciones lineales, awk para trabajo con campos y cálculos.
  • No considerar la locale al ordenar números con sort. Usa sort -n para ordenación numérica y sort -nr para inversa.
  • Sobrecargar la tubería con comandos innecesarios. Si una etapa no cambia el flujo, elimínala para mejorar legibilidad y rendimiento.

Conclusión

En este tutorial vimos cómo grep, sed y awk pueden combinarse para filtrar, transformar y extraer información de archivos de log de forma eficiente. Partimos de un archivo de ejemplo, aplicamos cada herramienta por separado mostrando la sintaxis y la salida esperada, y luego las encadenamos para lograr un objetivo más complejo: obtener un reporte ordenado de peticiones GET con respuesta 200.

Aprendimos también buenas prácticas para evitar errores típicos y cómo probar cada etapa antes de integrarla en una tubería larga.

Con estos conocimientos, podés enfrentar cualquier tarea de análisis de logs: desde buscar errores específicos, hasta generar métricas de rendimiento o preparar datos para herramientas de visualización. La clave es pensar en la tubería como una línea de ensamblaje donde cada herramienta hace su trabajo especializado y pasa el resultado al siguiente.

¡A poner en práctica lo aprendido y a explotar todo el potencial de la línea de comandos!

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario