Procesamiento de Texto en Bash: grep, sed y awk
Cómo construir pipelines de procesamiento de texto con grep, sed, awk, cut, sort, uniq y tr para análisis de logs y transformación de datos.
Descripción General
Cada herramienta de procesamiento de texto Unix hace una cosa bien, y el shell las conecta con pipes. Para análisis de logs, extracción de datos y generación de reportes, un one-liner suele ganar a un script de cien líneas en otro lenguaje. Abajo están las herramientas esenciales y cómo combinarlas de forma segura.
Cuándo Usar
- Extraer y filtrar líneas de log por patrón, tiempo o código de estado
- Transformar datos CSV o tabulares (ordenar, deduplicación, agregación)
- Buscar en bases de código por patrones a través de miles de archivos
- Generar reportes rápidos desde salida de texto estructurado
- Pre-procesar datos antes de alimentarlos a una base de datos o API
Cuándo NO Usar
- Parsear formatos anidados o irregulares (JSON, XML, HTML) — usa
jq,xqo un parser apropiado - Tareas que requieren estado complejo entre líneas — awk puede hacerlo, pero Python es más mantenible
- Transformaciones multi-paso donde el manejo de errores importa — los lenguajes de scripting tienen mejor debugging
- Casos edge de Unicode — las herramientas clásicas son byte-oriented y pueden dañar caracteres multibyte
Implementación Paso a Paso
grep — Coincidencia de Patrones
# Buscar recursivamente, mostrar números de línea, ignorar archivos binarios
grep -rn "ERROR" logs/
# Coincidencia invertida, contar ocurrencias
grep -vc "^#" config.ini
# Múltiples patrones con regex extendida
grep -E "(ERROR|FATAL|CRITICAL)" app.log
# Líneas de contexto: 2 antes, 3 después
grep -B 2 -A 3 "Exception" app.log
# Solo nombres de archivo que contienen coincidencia (útil para operaciones batch)
grep -rl "TODO" src/
# Regex compatible con Perl (PCRE) para lookaheads
grep -P "(?<=user_id=)\d+" access.log
sed — Edición de Streams
# Reemplazar primera ocurrencia por línea
sed 's/foo/bar/' file.txt
# Reemplazar todas las ocurrencias globalmente
sed 's/foo/bar/g' file.txt
# Reemplazar in-place con backup
sed -i.bak 's/old_domain/new_domain/g' config.conf
# Eliminar líneas que coinciden con patrón
sed '/^#/d' config.ini # Eliminar comentarios
sed '/^$/d' file.txt # Eliminar líneas vacías
# Extraer líneas específicas
sed -n '10,20p' file.txt # Imprimir líneas 10-20
sed -n '50,$p' file.txt # Imprimir desde línea 50 al final
# Reemplazo multi-línea (append después de coincidencia)
sed '/pattern/a\\New line after match' file.txt
awk — Procesamiento de Campos y Agregación
# Imprimir columnas específicas (delimitadas por espacio/tabulador)
awk '{print $1, $3}' access.log
# Sumar una columna
awk '{sum += $2} END {print sum}' sales.txt
# Promedio con conteo
awk '{sum += $2; count++} END {if (count) print sum/count}' data.txt
# Filtrar filas por condición
awk '$3 > 100 {print $1, $3}' orders.csv
# Procesar CSV con delimitador personalizado
awk -F',' '{print $2, $5}' customers.csv
# Agrupar y contar (como SQL GROUP BY)
awk '{count[$1]++} END {for (k in count) print k, count[k]}' status.log
# Formatear salida con encabezados
awk 'BEGIN {print "IP", "Requests"} {count[$1]++} END {for (ip in count) print ip, count[ip]}' access.log
cut, sort, uniq — Extracción de Columnas y Deduplicación
# Extraer columnas por posición o delimitador
cut -d',' -f1,3,5 data.csv
cut -c1-10 file.txt # Primeros 10 caracteres
# Ordenar numéricamente, inverso, por columna específica
sort -t',' -k3 -n sales.csv # Ordenar por 3ra columna numéricamente
sort -u file.txt # Ordenar y eliminar duplicados
# Contar ocurrencias únicas
sort file.txt | uniq -c | sort -rn # Más frecuentes primero
# Mostrar solo líneas duplicadas o únicas
sort file.txt | uniq -d # Solo duplicados
sort file.txt | uniq -u # Solo líneas únicas
tr — Traducción de Caracteres
# Convertir a mayúsculas
cat file.txt | tr 'a-z' 'A-Z'
# Comprimir caracteres repetidos
tr -s ' ' < file.txt # Colapsar múltiples espacios a uno
# Eliminar caracteres
tr -d '\r' < file.txt # Eliminar retornos de carro
# Reemplazar finales de línea
tr '\n' ',' < lines.txt > comma-separated.txt
Pipelines Complejos
# Top 10 tipos de error más frecuentes en un log
awk '$0 ~ /ERROR|FATAL/ {print $5}' app.log | \
sort | uniq -c | sort -rn | head -10
# Extraer IPs únicas de clientes con conteo de requests, ordenado
awk '{print $1}' access.log | sort | uniq -c | sort -rn | \
awk '{print $2 "," $1}' > ip_counts.csv
# Encontrar queries lentas (>1s) y agrupar por tabla
awk '$NF > 1 {print}' slow_query.log | \
grep -oP 'FROM \K\w+' | sort | uniq -c | sort -rn
# Convertir timestamps de log a formato ISO y filtrar rango de fechas
sed -n '/2024-06-01/,/2024-06-07/p' app.log | \
awk '{gsub(/\//, "-", $1); print $1 "T" $2}'
# Generar reporte: distribución de códigos de estado
awk '{print $9}' access.log | sort | uniq -c | \
awk '{printf "%s: %d requests (%.1f%%)\n", $2, $1, $1*100/total}' \
total=$(wc -l < access.log)
Lo que funciona
- Siempre entrecomilla patrones de regex con caracteres especiales.
grep "$pattern"previene que el shell expanda*o?antes de que grep los vea. - Usa
awkpara datos columnares en lugar decutcuando los campos varían en ancho.cutfalla con espaciado variable;awkdivide en cualquier whitespace por defecto. - Prefiere
jqpara JSON,xqpara XML,csvkitpara CSV. Las herramientas clásicas tratan estos formatos como texto plano y romperán en campos entrecomillados o estructuras anidadas. - Encadena herramientas de izquierda a derecha en orden de filtrado. Pon
greptemprano para reducir volumen de datos antes de operaciones costosas deawkosort. - Usa
LC_ALL=Cpara ordenamiento consistente y rendimiento. Fuerza ordenamiento byte-wise y evita comportamiento dependiente de locale.
Performance Tips
- Para iterar línea a línea con
while read, consulta Loop Sobre Archivos en Bash.
- Usa
LC_ALL=Cpara 2-5x de speedup en archivos grandes. El ordenamiento y comparación byte-wise omite el procesamiento Unicode dependiente de locale:
# Lento: ordenamiento locale-aware
sort large_file.txt > sorted.txt
# Rápido: ordenamiento byte-wise (2-5x más rápido en datos ASCII)
LC_ALL=C sort large_file.txt > sorted.txt
# grep rápido en archivos grandes
LC_ALL=C grep -n "pattern" huge_file.log
- Usa
grep -Fpara strings fijos. Cuando no necesitas regex, el modo de string fijo es considerablemente más rápido:
# Lento: el motor de regex procesa un string literal
grep "192.168.1.1" access.log
# Rápido: búsqueda de string fijo (sin overhead de regex)
grep -F "192.168.1.1" access.log
# Rápido: múltiples strings fijos desde un archivo
grep -Ff patterns.txt access.log
- Usa
mawkogawkpara rendimiento.mawkes más rápido quegawkpara tareas simples, mientras quegawktiene más features:
# Verificar qué awk está instalado
awk --version 2>/dev/null || awk -W version 2>&1 | head -1
# Instalar mawk para velocidad (Debian/Ubuntu)
# apt-get install mawk
# Usar mawk explícitamente para pipelines críticos de rendimiento
mawk '{print $1, $9}' access.log | sort | uniq -c | sort -rn
Para paralelizar estos pipelines entre núcleos, consulta Ejecución Paralela en Bash.
Errores Comunes
- Parsear JSON/HTML con grep/sed/awk. Estos no son formatos estructurados — usa
jq,python -m json.toolo un DOM parser. - Olvidar que
sedyawkoperan línea por línea por defecto. Los patrones multi-línea requieren flags especiales (sed -z, manipulación de RS enawk) que no son obvios. - Asumir que
sortes estable por defecto. La estabilidad desortvaría por implementación; usasort -ssi la necesitas. - Usar
catinnecesariamente.cat file | grep patternes un uso inútil decat. Usagrep pattern file. - No manejar entrada vacía. Muchos pipelines fallan silenciosamente en archivos vacíos — agrega
| catal final o verifica el tamaño del archivo primero.
Lectura Adicional
- Manual de GNU sed — rangos de direcciones y edición en el lugar
- Manual de GNU awk — campos, registros y reglas patrón-acción
- BashFAQ/021 — cuándo usar sed vs awk vs perl
Habilitar salida con color en grep
grep —color=auto “pattern” file.txt
Forzar color incluso al hacer pipe (útil para logging)
grep —color=always “ERROR” app.log | less -R
Color personalizado con awk
awk ’ /ERROR/ {print “\033[31m” $0 “\033[0m”; next} /WARN/ {print “\033[33m” $0 “\033[0m”; next} /INFO/ {print “\033[32m” $0 “\033[0m”; next} {print} ’ app.log
Preguntas frecuentes
¿Cuándo debo usar awk en lugar de sed?
Usa awk para procesamiento basado en campos, aritmética y registros estructurados. Usa sed para sustituciones simples, eliminaciones y transformaciones orientadas a líneas.
¿Cómo manejo archivos CSV de forma segura en Bash?
Usa un parser CSV apropiado como el módulo csv de Python o csvkit. El cut o awk puro falla con campos entre comillas y comas embebidas.
¿Por qué grep con regex es más lento de lo esperado?
Las expresiones regulares con backtracking, especialmente con alternancias y comodines, pueden ser lentas. Usa búsqueda de string fijo con grep -F cuando no necesites regex.
¿Cómo proceso archivos muy grandes sin quedarme sin memoria?
Todas las herramientas clásicas de Unix (grep, sed, awk, sort, cut) son orientadas a streams y procesan datos línea por línea. Usan memoria constante sin importar el tamaño del archivo. La excepción es sort, que usa archivos temporales para entradas grandes:
# Procesar un archivo de 50GB con memoria constante
LC_ALL=C grep "ERROR" huge_log.txt | awk '{print $5}' | sort | uniq -c | sort -rn
# sort usa archivos temporales automáticamente cuando la entrada excede la memoria
# Controla el directorio temporal con TMPDIR
TMPDIR=/fast_ssd sort huge_file.txt > sorted.txt
¿Cómo extraigo datos entre dos patrones?
Usa sed con rangos de dirección o awk con variables de flag:
# sed: imprimir líneas entre START y END (inclusivo)
sed -n '/START/,/END/p' file.txt
# sed: imprimir líneas entre START y END (exclusivo)
sed -n '/START/,/END/p' file.txt | sed '1d;$d'
# awk: más control sobre inclusión/exclusión
awk '/START/ {found=1; next} /END/ {found=0} found' file.txt
# awk: incluir marcadores de inicio y fin
awk '/START/ {found=1} found {print} /END/ {found=0}' file.txt
¿Cómo reemplazo texto a través de múltiples archivos de forma segura?
Usa find con sed -i y siempre crea backups:
#!/bin/bash
set -euo pipefail
# Buscar y reemplazar en todos los archivos .conf, con backup
find /etc/app -name "*.conf" -type f -exec sed -i.bak 's/old_host/new_host/g' {} +
# Verificar cambios antes de eliminar backups
find /etc/app -name "*.conf.bak" | while read bak; do
orig="${bak%.bak}"
if diff -q "$orig" "$bak" > /dev/null; then
echo "Sin cambios: $orig"
rm "$bak"
else
echo "Cambiado: $orig"
# Revisar cambios, luego eliminar backup si estás satisfecho
# rm "$bak"
fi
done
¿Cómo fusiono dos archivos ordenados y elimino duplicados?
Usa sort -m para fusionar archivos pre-ordenados, luego uniq:
# Fusionar dos archivos ordenados, eliminar duplicados
sort -m file1_sorted.txt file2_sorted.txt | uniq > merged_unique.txt
# Fusionar y mantener solo líneas presentes en ambos archivos (intersección)
sort file1.txt file2.txt | uniq -d > intersection.txt
# Fusionar y mantener solo líneas únicas de file1 (diferencia)
sort file1.txt file2.txt file2.txt | uniq -u > only_file1.txt
¿Cómo coloco color en la salida de grep en scripts?
# Habilitar salida con color en grep
grep --color=auto "pattern" file.txt
# Forzar color incluso al hacer pipe (útil para logging)
grep --color=always "ERROR" app.log | less -R
# Color personalizado con awk
awk '
/ERROR/ {print "\033[31m" $0 "\033[0m"; next}
/WARN/ {print "\033[33m" $0 "\033[0m"; next}
/INFO/ {print "\033[32m" $0 "\033[0m"; next}
{print}
' app.log
Recursos Relacionados
Cómo Iterar sobre Archivos en Bash
Cómo iterar de forma segura sobre archivos y directorios en Bash, manejando espacios, globs y listas grandes de archivos con patrones correctos.
RecipeEjecución Paralela en Bash
Cómo ejecutar comandos de shell en paralelo con xargs, GNU parallel y trabajos en segundo plano de Bash controlando la concurrencia y recolectando resultados. Incluye manejo de exit codes y un semáforo para APIs con límite de tasa.
RecipeLogging Estructurado
Implementa logging estructurado con salida JSON, correlation IDs y agregación de logs para observabilidad en producción.
RecipeRotación de copias en Bash: retención e integridad
Automatiza la rotación de copias en Bash con archivos diarios, semanales y mensuales, retención, cifrado y verificación de integridad.
RecipeControl de Jobs Paralelos
Cómo controlar jobs de Bash en paralelo una vez que corren: recolectar códigos de salida, reintentar fallos, aplicar timeouts y reanudar lotes interrumpidos.