4  Búsqueda y Filtrado

La capacidad de encontrar información rápidamente en archivos y datos es crucial para la productividad. Esta sección cubre las herramientas más potentes para búsqueda de texto ultrarrápida, filtrado interactivo y procesamiento de datos estructurados.

4.1 ripgrep (rg) - Búsqueda ultrarrápida

ripgrep es una herramienta de búsqueda que combina la potencia de grep con la velocidad moderna y características inteligentes como respeto automático de .gitignore.

4.1.1 Características principales

  • 🚀 Extremadamente rápido - Hasta 10x más rápido que grep
  • 🎯 Respeta .gitignore automáticamente
  • 🌈 Colores por defecto para mejor legibilidad
  • 🔍 Búsqueda recursiva por defecto
  • 📁 Filtros por tipo de archivo inteligentes

4.1.2 Uso básico

# Búsqueda simple en directorio actual
rg "función"

# Búsqueda ignorando mayúsculas/minúsculas
rg -i "error"

# Buscar palabra completa
rg -w "class"

# Búsqueda literal (sin regex)
rg -F "console.log("

4.1.3 Ejemplos por tipo de archivo

4.1.3.1 Desarrollo web

# Buscar imports en archivos JavaScript
rg -t js "import.*from"

# Encontrar funciones en TypeScript
rg -t ts "function\s+\w+" 

# Buscar clases CSS
rg -t css "\.[\w-]+\s*\{"

# APIs en archivos de configuración
rg -t json "api.*url"

4.1.3.2 Python

# Buscar definiciones de clase
rg -t py "^class\s+\w+"

# Encontrar imports específicos
rg -t py "from django import"

# Buscar decoradores
rg -t py "@\w+"

# Variables de entorno
rg -t py "os\.environ"

4.1.4 Filtros avanzados

4.1.4.1 Por ubicación

# Buscar solo en archivos de configuración
rg "database" --glob="*.{conf,cfg,ini,yaml,yml}"

# Excluir directorios específicos
rg "TODO" --glob="!node_modules/**"

# Buscar en archivos modificados recientemente
find . -mtime -1 -name "*.py" | xargs rg "import"

4.1.4.2 Por contexto

# Mostrar 3 líneas antes y después
rg -A 3 -B 3 "error"

# Mostrar 5 líneas de contexto
rg -C 5 "function main"

# Mostrar número de línea
rg -n "import React"

# Mostrar solo nombres de archivos que contienen el patrón
rg -l "password"

4.1.5 Casos de uso avanzados

4.1.5.1 Auditoría de código

# Encontrar hardcoded passwords/secrets
rg -i "(password|secret|key|token)\s*[:=]" --type-not=json

# Buscar código duplicado (funciones similares)
rg "function \w+\(" -A 10 | grep -A 10 "function.*(" | sort | uniq -d

# Encontrar imports no utilizados en Python
rg "^import \w+" -t py | cut -d: -f2 | sort | uniq > imports.txt
# Luego verificar cada import en el código

# URLs hardcodeadas
rg "https?://[^\s\"']+"

4.1.5.2 Análisis de logs

# Errores por severidad
rg "(ERROR|WARN|INFO)" /var/log/app.log

# IPs sospechosas (muchas peticiones)
rg -o "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" access.log | \
sort | uniq -c | sort -nr | head -10

# Códigos de error HTTP
rg " [45]\d{2} " access.log -o | sort | uniq -c

4.1.5.3 Migración de código

# Encontrar uso de API deprecated
rg "\.oldFunction\(" -t js

# Buscar patterns específicos para refactoring
rg "var\s+\w+\s*=" -t js  # Variables con 'var' para cambiar a 'let/const'

# Encontrar comentarios TODO/FIXME con contexto
rg -C 2 "(TODO|FIXME|HACK)"

4.1.6 Configuración personalizada

4.1.6.1 Archivo de configuración

Crear ~/.ripgreprc:

# Siempre mostrar números de línea
--line-number

# Búsqueda inteligente de mayúsculas
--smart-case

# Mostrar tipos de archivo soportados
--type-list

# Colores personalizados
--colors=line:fg:yellow
--colors=line:style:bold
--colors=path:fg:green
--colors=match:fg:red
--colors=match:style:bold

4.2 fzf - Filtro difuso interactivo

fzf es un filtro de línea de comandos que permite búsqueda difusa interactiva en cualquier lista de elementos.

4.2.1 Configuración inicial

# Configurar para zsh (agregar a .zshrc)
source <(fzf --zsh)

# Variables de entorno útiles
export FZF_DEFAULT_COMMAND='rg --files --hidden --follow --glob "!.git/*"'
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'

4.2.2 Uso básico interactivo

# Buscar archivos en directorio actual
find . -type f | fzf

# Buscar en historial de comandos
history | fzf

# Buscar procesos
ps aux | fzf

4.2.3 Ejemplos prácticos

4.2.3.2 Git workflow

# Checkout a branch
git checkout $(git branch | fzf | tr -d ' ')

# Ver commit específico
git show $(git log --oneline | fzf | cut -d' ' -f1)

# Agregar archivos selectivamente
git add $(git status --porcelain | fzf -m | cut -c4-)

# Ver diff de archivo
git diff $(git status --porcelain | fzf | cut -c4-)

4.2.3.3 Gestión de procesos

# Matar proceso seleccionado
kill $(ps aux | fzf | awk '{print $2}')

# Ver logs de servicio systemd
journalctl -u $(systemctl list-units --type=service | fzf | awk '{print $1}')

# Conectar por SSH
ssh $(grep "^Host " ~/.ssh/config | cut -d' ' -f2 | fzf)

4.2.4 Integración avanzada

4.2.4.1 Funciones de shell personalizadas

# Función para buscar y editar archivos
fe() {
    local files
    files=$(find . -type f | fzf --multi --preview 'bat --color=always {}')
    [[ -n "$files" ]] && ${EDITOR:-vim} "${files[@]}"
}

# Función para navegar a directorio frecuente
fd() {
    local dir
    dir=$(find ${1:-.} -type d 2>/dev/null | fzf +m) && cd "$dir"
}

# Función para ver historia de comandos y ejecutar
fh() {
    eval $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed 's/ *[0-9]* *//')
}

# Función para matar procesos interactivamente
fkill() {
    local pid
    pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')
    if [ "x$pid" != "x" ]; then
        echo $pid | xargs kill -${1:-9}
    fi
}

4.2.4.2 Vista previa avanzada

# Buscar archivos con vista previa
fzf --preview 'bat --style=numbers --color=always --line-range :500 {}'

# Buscar en git commits con vista previa
git log --oneline | fzf --preview 'git show --color=always {1}'

# Buscar archivos de código con vista previa y números de línea
rg --files | fzf --preview 'bat --style=numbers --color=always --highlight-line {2} {1}' --delimiter ':'

4.3 jq - Procesador JSON

jq es como sed pero para datos JSON, permitiendo consultar, filtrar y transformar datos JSON de manera elegante.

4.3.1 Sintaxis básica

# Pretty print JSON
curl -s https://api.github.com/users/octocat | jq .

# Extraer campo específico
echo '{"name":"Juan","age":30}' | jq .name

# Extraer campo anidado
echo '{"user":{"name":"Juan"}}' | jq .user.name

4.3.2 Filtros básicos

4.3.2.1 Acceso a datos

# Array de objetos - extraer campo de todos
jq '.[].name' users.json

# Filtrar por condición
jq '.[] | select(.age > 25)' users.json

# Extraer múltiples campos
jq '.[] | {name: .name, email: .email}' users.json

# Contar elementos
jq '. | length' array.json

4.3.2.2 Transformaciones

# Crear nuevo objeto
jq '{full_name: (.first_name + " " + .last_name), age: .age}' person.json

# Agregar campo
jq '. + {timestamp: now}' data.json

# Eliminar campo
jq 'del(.password)' user.json

# Renombrar campo
jq '{username: .name, years: .age}' user.json

4.3.3 Casos de uso con APIs

4.3.3.1 GitHub API

# Listar repositorios públicos de un usuario
curl -s https://api.github.com/users/octocat/repos | \
jq '.[] | {name: .name, stars: .stargazers_count, language: .language}'

# Top 5 repos por estrellas
curl -s https://api.github.com/users/octocat/repos | \
jq 'sort_by(.stargazers_count) | reverse | .[0:5] | .[] | .name'

# Repos por lenguaje
curl -s https://api.github.com/users/octocat/repos | \
jq 'group_by(.language) | .[] | {language: .[0].language, count: length}'

4.3.3.2 Análisis de logs JSON

# Logs de aplicación en formato JSON
cat app.log | jq 'select(.level == "ERROR")' 

# Agrupar errores por tipo
cat app.log | jq 'select(.level == "ERROR") | .error_type' | sort | uniq -c

# Estadísticas por hora
cat app.log | jq -r '.timestamp[0:13]' | sort | uniq -c

4.3.4 Operaciones avanzadas

4.3.4.1 Agregaciones y estadísticas

# Promedio de edades
jq '[.[] | .age] | add / length' users.json

# Valor máximo
jq '[.[] | .score] | max' scores.json

# Agrupar y contar
jq 'group_by(.category) | .[] | {category: .[0].category, count: length}' items.json

# Crear histograma de frecuencias
jq '[.[] | .status] | group_by(.) | .[] | {status: .[0], count: length}' requests.json

4.3.4.2 Transformaciones complejas

# Convertir array de objetos a objeto indexado
jq 'map({(.id|tostring): .}) | add' items.json

# Pivot de datos
jq 'group_by(.category) | map({category: .[0].category, items: map(.name)})' products.json

# Merge de múltiples objetos
jq '. as $root | .users[] | . + {company: $root.company}' company.json

4.3.4.3 Validación y filtrado

# Verificar estructura requerida
jq 'if has("name") and has("email") then . else empty end' user.json

# Filtros complejos con múltiples condiciones
jq '.[] | select(.age > 18 and .status == "active" and (.roles | contains(["admin"])))' users.json

# Validar emails
jq '.[] | select(.email | test("^[^@]+@[^@]+\\.[^@]+$"))' contacts.json

4.3.5 Integración con otras herramientas

4.3.5.1 Con curl para APIs

# Script para monitorear API
#!/bin/bash
API_URL="https://api.example.com/status"
while true; do
    status=$(curl -s "$API_URL" | jq -r '.status')
    if [ "$status" != "ok" ]; then
        echo "ALERT: API status is $status"
        # Enviar notificación
    fi
    sleep 60
done

4.3.5.2 Con fzf para selección interactiva

# Seleccionar usuario de API y ver detalles
user_id=$(curl -s https://api.example.com/users | \
          jq -r '.[] | "\(.id): \(.name)"' | \
          fzf | cut -d: -f1)

curl -s "https://api.example.com/users/$user_id" | jq .

4.3.5.3 Conversion de formatos

# JSON a CSV
jq -r '.[] | [.name, .age, .email] | @csv' users.json

# JSON a tabla HTML
jq -r '.[] | "<tr><td>\(.name)</td><td>\(.age)</td></tr>"' users.json

# JSON a variables de entorno
jq -r 'to_entries[] | "export \(.key)=\(.value)"' config.json

4.4 Workflows complejos combinando herramientas

4.4.1 Análisis completo de código base

#!/bin/bash
# Script para analizar un proyecto completo

echo "=== ANÁLISIS DEL PROYECTO ==="

# 1. Estadísticas generales
echo "Archivos por tipo:"
find . -type f | grep -E '\.[a-z]+$' | sed 's/.*\.//' | sort | uniq -c | sort -nr

# 2. Funciones más utilizadas
echo -e "\nFunciones más referenciadas:"
rg -o '\w+\(' --type js | sed 's/(//' | sort | uniq -c | sort -nr | head -10

# 3. TODOs y FIXMEs
echo -e "\nTareas pendientes:"
rg -C 1 "(TODO|FIXME|HACK)" | head -20

# 4. Archivos grandes
echo -e "\nArchivos más grandes:"
find . -type f -exec du -h {} + | sort -rh | head -10

# 5. Dependencias externas (JS/Python)
echo -e "\nImports externos más comunes:"
rg "^(import|from).*['\"]([^./].*)['\"]" -o --type py --type js | \
cut -d'"' -f2 | cut -d"'" -f2 | sort | uniq -c | sort -nr | head -10

4.4.2 Pipeline de procesamiento de datos

#!/bin/bash
# Procesar logs de servidor web y generar reporte

LOG_FILE="/var/log/nginx/access.log"

echo "=== REPORTE DE ACCESO WEB ==="

# 1. Top 10 IPs
echo "Top 10 IPs por número de requests:"
rg -o '^\S+' "$LOG_FILE" | sort | uniq -c | sort -nr | head -10

# 2. Páginas más visitadas
echo -e "\nPáginas más visitadas:"
rg -o '"GET ([^"]*)"' "$LOG_FILE" | cut -d' ' -f2 | sort | uniq -c | sort -nr | head -10

# 3. Códigos de error
echo -e "\nCódigos de respuesta:"
rg -o '" \d{3} ' "$LOG_FILE" | tr -d ' "' | sort | uniq -c | sort -nr

# 4. User agents más comunes
echo -e "\nNavigadores/bots más comunes:"
rg -o '"[^"]*" "[^"]*" "([^"]*)"$' "$LOG_FILE" | \
cut -d'"' -f6 | sort | uniq -c | sort -nr | head -10

# 5. Generar JSON para dashboard
echo -e "\nGenerando reporte JSON..."
{
    echo '{'
    echo '  "timestamp": "'$(date -Iseconds)'",'
    echo '  "total_requests": '$(wc -l < "$LOG_FILE")','
    echo '  "unique_ips": '$(rg -o '^\S+' "$LOG_FILE" | sort -u | wc -l)','
    echo '  "top_pages": ['
    rg -o '"GET ([^"]*)"' "$LOG_FILE" | cut -d' ' -f2 | sort | uniq -c | sort -nr | head -5 | \
    jq -R '. | split(" ") | {count: .[0]|tonumber, page: .[1]}' | jq -s .
    echo '  ]'
    echo '}'
} > web_report.json

echo "Reporte guardado en web_report.json"

4.4.3 Búsqueda interactiva universal

#!/bin/bash
# Función para búsqueda universal con vista previa

universal_search() {
    local search_term="$1"
    
    if [ -z "$search_term" ]; then
        echo "Uso: universal_search <término>"
        return 1
    fi
    
    # Buscar en archivos de código
    echo "=== Archivos de código ==="
    rg -l "$search_term" --type-add 'code:*.{py,js,ts,go,rs,java,cpp,c,h}' -t code | \
    fzf --preview "rg --color=always '$search_term' {}" \
        --preview-window=right:60% \
        --header="Archivos de código que contienen '$search_term'"
    
    # Buscar en documentación
    echo -e "\n=== Documentación ==="
    rg -l "$search_term" --type-add 'docs:*.{md,txt,rst,org}' -t docs | \
    fzf --preview "bat --color=always {}" \
        --preview-window=right:60% \
        --header="Documentación que contiene '$search_term'"
    
    # Buscar en configuración
    echo -e "\n=== Archivos de configuración ==="
    rg -l "$search_term" --type-add 'config:*.{yaml,yml,json,toml,ini,conf}' -t config | \
    fzf --preview "bat --color=always {}" \
        --preview-window=right:60% \
        --header="Configuración que contiene '$search_term'"
}

# Uso: universal_search "database"
Tips para búsqueda eficiente
  • Combina rg con fzf para búsqueda interactiva con vista previa
  • Usa jq con -r para output raw (sin comillas) cuando necesites procesar más
  • Configura aliases para combinaciones que uses frecuentemente
  • ripgrep respeta .gitignore automáticamente, úsalo en proyectos de código
Rendimiento
  • ripgrep es extremadamente rápido, pero en directorios muy grandes considera usar --max-depth
  • jq puede consumir mucha memoria con archivos JSON grandes, usa streaming con --stream si es necesario
  • fzf es rápido, pero la vista previa puede ser lenta con archivos grandes

En el próximo capítulo exploraremos herramientas esenciales para desarrollo y control de versiones con Git.