Introducción a las Herramientas de Texto
Unix/Linux ofrece un conjunto poderoso de herramientas especializadas en el procesamiento de texto. Estas herramientas, siguiendo la filosofía Unix de "hacer una cosa y hacerla bien", pueden combinarse para realizar tareas complejas de manipulación de datos.
Comparación de Herramientas
grep
Búsqueda
Encuentra patrones en texto
cut
Extracción
Corta columnas de texto
sed
Edición
Editor de flujo no interactivo
awk
Procesamiento
Lenguaje de programación
grep - Búsqueda de Patrones
grep
(Global Regular Expression Print) es una herramienta fundamental para buscar texto que coincida con patrones específicos en archivos o entrada estándar.
Sintaxis Básica
# Sintaxis básica
grep [opciones] patrón [archivo...]
# Ejemplos básicos
grep "error" archivo.log # Buscar "error" en archivo
grep -i "ERROR" archivo.log # Búsqueda insensible a mayúsculas
grep -n "warning" archivo.log # Mostrar números de línea
grep -v "debug" archivo.log # Líneas que NO contienen "debug"
grep -c "success" archivo.log # Contar coincidencias
Opciones Importantes
# Búsqueda recursiva en directorios
$ grep -r "function" /home/user/scripts/
/home/user/scripts/backup.sh:function create_backup() {
/home/user/scripts/utils.sh:function log_message() {
# Mostrar contexto (líneas antes y después)
$ grep -A 3 -B 2 "ERROR" app.log
2024-01-15 10:23:45 INFO Starting application
2024-01-15 10:23:46 WARN Configuration file missing
2024-01-15 10:23:47 ERROR Failed to connect to database
2024-01-15 10:23:48 ERROR Retrying connection...
2024-01-15 10:23:49 INFO Connection established
# Buscar múltiples patrones
$ grep -E "(error|warning|critical)" system.log
2024-01-15 10:15:22 WARNING High memory usage
2024-01-15 10:16:33 ERROR Database timeout
2024-01-15 10:17:44 CRITICAL System overload
Expresiones Regulares con grep
grep soporta diferentes tipos de expresiones regulares:
- grep: Expresiones regulares básicas (BRE)
- grep -E / egrep: Expresiones regulares extendidas (ERE)
- grep -F / fgrep: Cadenas fijas (sin regex)
- grep -P: Expresiones regulares compatibles con Perl
cut - Extracción de Columnas
cut
extrae secciones específicas de cada línea de texto, siendo ideal para procesar datos estructurados como CSV o archivos delimitados.
Modos de Operación
# Por caracteres
cut -c 1-5 archivo.txt # Caracteres 1 al 5
cut -c 1,3,5 archivo.txt # Caracteres 1, 3 y 5
cut -c 5- archivo.txt # Desde el carácter 5 al final
# Por campos (delimitados)
cut -d ',' -f 1,3 datos.csv # Campos 1 y 3, delimitador coma
cut -d ':' -f 1 /etc/passwd # Primer campo (usuario) de passwd
cut -d ' ' -f 2-4 archivo.txt # Campos 2 al 4, delimitador espacio
# Por bytes
cut -b 1-10 archivo.txt # Primeros 10 bytes
Ejemplos Prácticos
# Extraer usuarios del sistema
$ cut -d ':' -f 1 /etc/passwd | head -5
root
daemon
bin
sys
sync
# Procesar archivo CSV
$ cat ventas.csv
Fecha,Vendedor,Producto,Cantidad,Precio
2024-01-15,Juan,Laptop,2,1200.00
2024-01-15,Maria,Mouse,5,25.50
$ cut -d ',' -f 2,4 ventas.csv
Vendedor,Cantidad
Juan,2
Maria,5
# Extraer direcciones IP de logs
$ cut -d ' ' -f 1 access.log | head -3
192.168.1.100
10.0.0.15
172.16.0.50
Limitaciones de cut
cut
no puede manejar delimitadores de longitud variable (como múltiples espacios). Para esto, es mejor usar awk
.
sed - Editor de Flujo
sed
(Stream Editor) es un editor no interactivo que puede realizar transformaciones de texto complejas. Es especialmente útil para sustituciones, eliminaciones e inserciones automatizadas.
Comandos Fundamentales
# Sustitución (s)
sed 's/viejo/nuevo/' archivo.txt # Primera ocurrencia por línea
sed 's/viejo/nuevo/g' archivo.txt # Todas las ocurrencias
sed 's/viejo/nuevo/2' archivo.txt # Segunda ocurrencia por línea
# Eliminación (d)
sed '3d' archivo.txt # Eliminar línea 3
sed '/patrón/d' archivo.txt # Eliminar líneas con patrón
sed '1,5d' archivo.txt # Eliminar líneas 1-5
# Inserción y adición
sed '3i\Nueva línea' archivo.txt # Insertar antes de línea 3
sed '3a\Nueva línea' archivo.txt # Añadir después de línea 3
# Imprimir líneas específicas
sed -n '1,10p' archivo.txt # Imprimir líneas 1-10
sed -n '/patrón/p' archivo.txt # Imprimir líneas con patrón
Ejemplos Avanzados
# Reemplazar múltiples patrones
$ echo "Hola mundo" | sed 's/Hola/Hello/; s/mundo/world/'
Hello world
# Cambiar formato de fecha
$ echo "2024-01-15" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/'
15/01/2024
# Eliminar líneas vacías
$ sed '/^$/d' archivo.txt
# Numerar líneas no vacías
$ sed '/./=' archivo.txt | sed 'N; s/\n/\t/'
# Convertir a mayúsculas (donde esté disponible)
$ echo "texto en minúsculas" | sed 's/.*/\U&/'
TEXTO EN MINÚSCULAS
Modificación In-Situ
Con la opción -i
, sed puede modificar archivos directamente:
sed -i 's/viejo/nuevo/g' archivo.txt # Modifica el archivo
sed -i.bak 's/viejo/nuevo/g' archivo.txt # Crea respaldo .bak
awk - Procesamiento Avanzado
awk
es un lenguaje de programación completo diseñado para el procesamiento de texto. Divide automáticamente cada línea en campos y permite realizar cálculos complejos.
Estructura de awk
# Estructura básica
awk 'patrón { acción }' archivo
# Variables automáticas importantes
# $0 = línea completa
# $1, $2, $3... = campos individuales
# NF = número de campos en la línea actual
# NR = número de línea actual
# FS = separador de campos (por defecto: espacio/tab)
# Ejemplos básicos
awk '{ print $1 }' archivo.txt # Imprimir primer campo
awk '{ print NF, $0 }' archivo.txt # Número de campos y línea
awk 'NR==5' archivo.txt # Imprimir línea 5
awk 'NF > 3' archivo.txt # Líneas con más de 3 campos
Operaciones Comunes
# Sumar una columna
$ awk '{ sum += $3 } END { print "Total:", sum }' ventas.txt
Total: 15750.00
# Calcular promedio
$ awk '{ sum += $2; count++ } END { print "Promedio:", sum/count }' datos.txt
Promedio: 87.5
# Formatear salida
$ awk '{ printf "%-10s %8.2f\n", $1, $2 }' productos.txt
Laptop 1200.00
Mouse 25.50
Teclado 150.00
# Procesar CSV con separador personalizado
$ awk -F ',' '{ print $2, $4 }' datos.csv
Juan 2
Maria 5
Carlos 3
# Contar ocurrencias
$ awk '{ count[$1]++ } END { for (i in count) print i, count[i] }' access.log
192.168.1.100 45
10.0.0.15 23
172.16.0.50 18
Scripts awk Avanzados
#!/usr/bin/awk -f
# Análisis de logs de Apache/Nginx
BEGIN {
print "=== Análisis de Log ===="
print "IP\t\tRequests\tBytes"
print "--------------------------------"
}
{
# Supone formato: IP - - [fecha] "request" status bytes
ip = $1
bytes = $10
# Acumular estadísticas
requests[ip]++
total_bytes[ip] += bytes
grand_total += bytes
}
END {
# Imprimir estadísticas por IP
for (ip in requests) {
printf "%-15s %8d %12d\n", ip, requests[ip], total_bytes[ip]
}
print "--------------------------------"
printf "Total bytes transferidos: %d\n", grand_total
printf "Número total de IPs: %d\n", length(requests)
}
Combinando Herramientas
El verdadero poder de estas herramientas surge cuando se combinan usando pipes y redirecciones para crear pipelines de procesamiento complejos.
# Pipeline complejo: análisis de log de servidor web
$ grep "ERROR" /var/log/apache2/error.log | \
cut -d' ' -f1-3 | \
sort | \
uniq -c | \
sort -nr | \
head -10
42 [Mon Oct 15
38 [Tue Oct 16
35 [Wed Oct 17
# Extraer y procesar datos de un CSV
$ cat ventas.csv | \
grep -v "Fecha" | \
cut -d',' -f4,5 | \
awk -F',' '{ total += $1 * $2 } END { print "Ingresos totales: $" total }'
Ingresos totales: $3876.50
# Análisis de uso de memoria por proceso
$ ps aux | \
awk 'NR>1 { mem[$11] += $6 } END { for (cmd in mem) printf "%-20s %8.2f MB\n", cmd, mem[cmd]/1024 }' | \
sort -k2 -nr | \
head -5
firefox 1247.83 MB
chrome 987.45 MB
java 654.23 MB
# Encontrar archivos grandes y organizarlos
$ find /var/log -type f -name "*.log" | \
xargs ls -la | \
awk '{ print $5, $9 }' | \
sort -nr | \
head -10 | \
awk '{ printf "%8.2f MB %s\n", $1/1024/1024, $2 }'
45.67 MB /var/log/syslog
23.45 MB /var/log/auth.log
18.92 MB /var/log/apache2/access.log
Pipeline Optimization Tips
- Usa
grep
primero para filtrar datos antes de procesamiento pesado cut
es más rápido queawk
para extracciones simplessed
es eficiente para sustituciones simples en archivos grandesawk
es ideal cuando necesitas cálculos o lógica compleja- Usa
sort -u
en lugar desort | uniq
cuando sea posible
Ejercicios Prácticos
Ejercicio 1: Análisis de Log
Crea un script que analice un log de servidor web:
#!/bin/bash
# Crear datos de prueba
cat > access.log << EOF
192.168.1.100 - - [15/Oct/2024:10:15:22] "GET /index.html" 200 1024
10.0.0.15 - - [15/Oct/2024:10:16:33] "POST /login" 404 512
192.168.1.100 - - [15/Oct/2024:10:17:44] "GET /images/logo.png" 200 2048
172.16.0.50 - - [15/Oct/2024:10:18:55] "GET /about.html" 500 256
192.168.1.100 - - [15/Oct/2024:10:19:11] "GET /contact" 200 1536
EOF
# 1. Top 5 IPs por número de requests
echo "=== Top 5 IPs por requests ==="
awk '{ print $1 }' access.log | sort | uniq -c | sort -nr | head -5
# 2. Códigos de respuesta más comunes
echo -e "\n=== Códigos de respuesta ==="
awk '{ print $9 }' access.log | sort | uniq -c | sort -nr
# 3. Total de bytes transferidos por IP
echo -e "\n=== Bytes por IP ==="
awk '{ bytes[$1] += $10 } END {
for (ip in bytes) printf "%-15s %8d bytes\n", ip, bytes[ip]
}' access.log | sort -k2 -nr
Ejercicio 2: Procesador de CSV
Procesa un archivo CSV de ventas:
#!/bin/bash
# Crear datos de prueba
cat > ventas.csv << EOF
Fecha,Vendedor,Producto,Cantidad,Precio_Unitario,Ciudad
2024-01-15,Juan,Laptop,2,1200.00,Madrid
2024-01-15,Maria,Mouse,5,25.50,Barcelona
2024-01-16,Carlos,Teclado,3,150.00,Valencia
2024-01-16,Ana,Monitor,1,300.00,Madrid
2024-01-17,Juan,Laptop,1,1200.00,Sevilla
EOF
echo "=== Análisis de Ventas ==="
# 1. Ventas totales por vendedor
echo "Ventas por vendedor:"
tail -n +2 ventas.csv | \
awk -F',' '{
ventas[$2] += $4 * $5
} END {
for (vendedor in ventas)
printf "%-10s: €%.2f\n", vendedor, ventas[vendedor]
}' | sort -k2 -nr
# 2. Producto más vendido
echo -e "\nProductos más vendidos:"
tail -n +2 ventas.csv | \
cut -d',' -f3,4 | \
awk -F',' '{ productos[$1] += $2 } END {
for (producto in productos)
printf "%-10s: %d unidades\n", producto, productos[producto]
}' | sort -k2 -nr
# 3. Ventas por ciudad
echo -e "\nVentas por ciudad:"
tail -n +2 ventas.csv | \
awk -F',' '{ ciudades[$6] += $4 * $5 } END {
for (ciudad in ciudades)
printf "%-12s: €%.2f\n", ciudad, ciudades[ciudad]
}' | sort -k2 -nr