Encadenar grep, sed y awk para procesar archivos de log: tutorial paso a paso
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
greppara seleccionar líneas que coincidan con un patrón. - Cómo aplicar
sedpara realizar sustituciones, eliminaciones y otras transformaciones de texto. - Cómo emplear
awkpara 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
EOFEste 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' archivo2.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.logSalida 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 detected2.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.logSalida esperada:
2025-09-24 10:12:07 ERROR [db] Connection timeout
2025-09-24 10:12:25 ERROR [db] Deadlock detectedNota: 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.logSalida 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.logSalida 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.logSalida 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.logSalida 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.logSalida 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 403Nota: 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.logSalida esperada:
INFO 5
WARN 3
ERROR 44.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.logSalida esperada:
Media respuesta 200: 118.5msExplicació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.logSalida 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 210msPaso 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.logAplicá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 210Paso 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 -nrSalida 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 89Flujo 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 -nrEsto muestra cómo el poder de cada herramienta se potencia al encadenarlas.
6. Buenas prácticas y errores comunes
Buenas prácticas
- Usar comillas simples alrededor de patrones que contengan espacios o caracteres especiales (
'patrón'). Esto evita que el shell interprete símbolos como$,*,\. - Prefiere
grep -Eoawkcon expresiones regulares cuando necesites lógica compleja; no intentes hacer todo con múltiplesgrep. - Aprovecha las variables de entorno de awk (
FS,OFS) para cambiar el separador de campo cuando tus logs usan otro delimitador (por ejemplo,|o,). - Prueba cada etapa por separado antes de encadenar. Usa
headolesspara verificar la salida intermedia. - 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
greposed. Recuerda que[y]tienen significado especial; usa\[y\]para buscarlos literalmente. - Confundir los campos de awk cuando hay espacios múltiples.
awkcolapsa secuencias de espacios/tabs en un solo separador, pero si tu log usa tabs y espacios mezclados, verifica concat -Aood -c. - Usar
sedpara hacer operaciones que son más simples conawk(como sumar o contar). Cada herramienta tiene su fortaleza;sedes excelente para sustituciones lineales,awkpara trabajo con campos y cálculos. - No considerar la locale al ordenar números con
sort. Usasort -npara ordenación numérica ysort -nrpara 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!
DUGLAS MORENO
Comentarios (0)
Sé el primero en comentar.