jq es el procesador JSON de línea de comandos que todo administrador Linux e ingeniero DevOps debería tener en su kit de herramientas. Cuando trabajas con APIs REST, analizas salida de Kubernetes, procesas estado de Terraform o extraes datos de archivos de log, jq te permite cortar, filtrar y transformar JSON directamente desde la terminal — sin necesidad de scripts Python.
Esta guía cubre desde la extracción básica de campos hasta transformaciones avanzadas, con ejemplos del mundo real que usarás a diario.
Requisitos Previos
- Un sistema Linux (cualquier distribución)
- Familiaridad básica con pipes de shell y herramientas de línea de comandos
- Datos JSON para trabajar (usaremos ejemplos a lo largo de la guía)
Instalando jq
En Debian/Ubuntu:
sudo apt update && sudo apt install -y jq
En RHEL/Fedora/AlmaLinux:
sudo dnf install -y jq
En Alpine:
sudo apk add jq
En macOS:
brew install jq
Verificar:
jq --version
jq vs Otras Herramientas de Procesamiento JSON
| Herramienta | Mejor Para | Desventajas |
|---|---|---|
| jq | Transformaciones rápidas CLI, pipes de shell | La lógica compleja se vuelve verbosa |
| Python json | Procesamiento multi-paso, lógica compleja | Requiere runtime Python, más código |
| yq | Procesamiento YAML (sintaxis similar a jq) | Instalación separada, específico para YAML |
| fx | Exploración interactiva de JSON | No scriptable |
| gron | Hacer JSON “greppable” | Unidireccional (solo aplanar) |
jq gana cuando necesitas transformaciones rápidas y scriptables en un pipeline con cero dependencias más allá del binario.
jq Básico: Extrayendo Datos
El Filtro Identidad
La operación jq más simple imprime JSON con formato bonito:
echo '{"name":"nginx","version":"1.25.3","status":"running"}' | jq '.'
{
"name": "nginx",
"version": "1.25.3",
"status": "running"
}
Extrayendo Campos
Usa notación de punto para extraer valores específicos:
echo '{"name":"nginx","version":"1.25.3","status":"running"}' | jq '.name'
"nginx"
Para obtener el string sin comillas, agrega -r:
echo '{"name":"nginx","version":"1.25.3"}' | jq -r '.name'
nginx
La bandera
-res esencial para scripting. Sin ella, jq produce strings codificados en JSON (con comillas), lo que rompe asignaciones de variables y comparaciones en bash.
Objetos Anidados
echo '{"server":{"host":"10.0.0.1","port":8080}}' | jq '.server.port'
8080
Acceso a Arrays
echo '["nginx","apache","caddy"]' | jq '.[0]'
"nginx"
Obtener todos los elementos:
echo '["nginx","apache","caddy"]' | jq '.[]'
"nginx"
"apache"
"caddy"
Trabajando con Respuestas Reales de APIs
Aquí hay un escenario realista: consultar una API REST y extraer lo que necesitas.
# Obtener información de usuario de GitHub
curl -s https://api.github.com/users/torvalds | jq '{login, name, public_repos, followers}'
{
"login": "torvalds",
"name": "Linus Torvalds",
"public_repos": 7,
"followers": 223000
}
Extrayendo de Arrays de Objetos
La mayoría de las APIs devuelven arrays. Así es como trabajar con ellos:
# Simular una respuesta de API
cat << 'EOF' | jq '.servers[] | {name, status}'
{
"servers": [
{"name": "web-01", "status": "running", "cpu": 45},
{"name": "web-02", "status": "stopped", "cpu": 0},
{"name": "db-01", "status": "running", "cpu": 78}
]
}
EOF
{
"name": "web-01",
"status": "running"
}
{
"name": "web-02",
"status": "stopped"
}
{
"name": "db-01",
"status": "running"
}
Filtrando Datos JSON con jq
La Función select()
Filtra arrays solo a elementos coincidentes:
cat << 'EOF' | jq '.servers[] | select(.status == "running")'
{
"servers": [
{"name": "web-01", "status": "running", "cpu": 45},
{"name": "web-02", "status": "stopped", "cpu": 0},
{"name": "db-01", "status": "running", "cpu": 78}
]
}
EOF
{
"name": "web-01",
"status": "running",
"cpu": 45
}
{
"name": "db-01",
"status": "running",
"cpu": 78
}
Comparaciones Numéricas
# Servidores con uso de CPU mayor a 50%
cat servers.json | jq '.servers[] | select(.cpu > 50) | .name'
Combinando Condiciones
# Servidores en ejecución con CPU alta
cat servers.json | jq '.servers[] | select(.status == "running" and .cpu > 50)'
Coincidencia de Strings
# Servidores cuyo nombre contiene "web"
cat servers.json | jq '.servers[] | select(.name | contains("web"))'
Para coincidencia con regex:
# Nombres que coinciden con un patrón
cat servers.json | jq '.servers[] | select(.name | test("web-[0-9]+"))'
Transformando JSON con jq
Construyendo Nuevos Objetos
Construye nuevas estructuras JSON desde datos existentes:
cat << 'EOF' | jq '.servers[] | {hostname: .name, healthy: (.status == "running"), load: (.cpu / 100)}'
{
"servers": [
{"name": "web-01", "status": "running", "cpu": 45},
{"name": "db-01", "status": "running", "cpu": 78}
]
}
EOF
{
"hostname": "web-01",
"healthy": true,
"load": 0.45
}
{
"hostname": "db-01",
"healthy": true,
"load": 0.78
}
map() para Transformaciones de Arrays
Transforma cada elemento en un array:
echo '[1,2,3,4,5]' | jq 'map(. * 2)'
[2, 4, 6, 8, 10]
Ejemplo práctico — extraer solo nombres en un array:
cat servers.json | jq '[.servers[].name]'
["web-01", "web-02", "db-01"]
Ordenamiento y Agrupación
# Ordenar por CPU descendente
cat servers.json | jq '.servers | sort_by(.cpu) | reverse'
# Agrupar por status
cat servers.json | jq '.servers | group_by(.status)'
Agregaciones
# Contar servidores
cat servers.json | jq '.servers | length'
# Promedio de CPU
cat servers.json | jq '.servers | map(.cpu) | add / length'
# CPU máximo
cat servers.json | jq '.servers | max_by(.cpu) | .name'
jq en Scripts de Shell
Almacenando Resultados en Variables
#!/bin/bash
response=$(curl -s https://api.github.com/users/torvalds)
name=$(echo "$response" | jq -r '.name')
repos=$(echo "$response" | jq -r '.public_repos')
echo "User: $name has $repos public repos"
Truco: Siempre usa comillas en la variable en
echo "$response". Las variables sin comillas pierden espacios en blanco y rompen el análisis JSON.
Iterando Sobre Arrays JSON
#!/bin/bash
# Procesar cada servidor de la API
curl -s https://api.example.com/servers | jq -r '.[] | .name' | while read -r server; do
echo "Checking $server..."
ping -c 1 -W 2 "$server" > /dev/null 2>&1 && echo " UP" || echo " DOWN"
done
Generando Archivos de Configuración desde JSON
# Convertir inventario JSON a entradas de /etc/hosts
cat inventory.json | jq -r '.hosts[] | "\(.ip)\t\(.hostname)"' >> /etc/hosts
Procesamiento Condicional
#!/bin/bash
status=$(curl -s https://api.example.com/health | jq -r '.status')
if [ "$status" != "healthy" ]; then
echo "ALERT: Service is $status" | mail -s "Health Check Failed" admin@example.com
fi
Modificando JSON con jq
Actualizando Valores
echo '{"version": "1.0", "debug": false}' | jq '.version = "2.0" | .debug = true'
{
"version": "2.0",
"debug": true
}
Agregando Campos
echo '{"name": "app"}' | jq '. + {"env": "production", "replicas": 3}'
Eliminando Campos
echo '{"name":"app","secret":"abc123","version":"1.0"}' | jq 'del(.secret)'
Actualizando Valores Anidados
echo '{"database":{"host":"old-host","port":5432}}' | jq '.database.host = "new-host"'
Recetas jq del Mundo Real
Analizar Estado de Pods de Kubernetes
kubectl get pods -o json | jq -r '.items[] | select(.status.phase != "Running") | "\(.metadata.name)\t\(.status.phase)"'
Extraer Valores de Output de Terraform
terraform output -json | jq -r 'to_entries[] | "\(.key) = \(.value.value)"'
Analizar Estadísticas de Contenedores Docker
docker inspect $(docker ps -q) | jq '.[] | {name: .Name, ip: .NetworkSettings.IPAddress, status: .State.Status}'
Procesar Entradas de Log de CloudWatch
aws logs get-log-events --log-group-name /app/prod --log-stream-name main \
| jq -r '.events[] | "\(.timestamp | . / 1000 | strftime("%Y-%m-%d %H:%M:%S")) \(.message)"'
Salida CSV desde JSON
cat servers.json | jq -r '.servers[] | [.name, .status, (.cpu | tostring)] | @csv'
"web-01","running","45"
"web-02","stopped","0"
"db-01","running","78"
Solución de Problemas
“parse error: Invalid numeric literal”: Tu entrada no es JSON válido. Valídalo primero con jq '.' file.json. Causa común: la API devolvió HTML (página de error) en lugar de JSON.
Salida “null” cuando esperas datos: El nombre del campo es incorrecto o no existe en esa ruta. Usa jq 'keys' para ver campos disponibles, o jq '.' | head -20 para inspeccionar la estructura.
Comillas en asignaciones de variables: Siempre usa jq -r (salida sin formato) al asignar a variables de shell. Sin esto, obtienes "value" en lugar de value, lo que rompe comparaciones de strings.
“Cannot iterate over null”: Estás intentando iterar (.[]) sobre un campo que es null o falta. Agrega el operador opcional: .field[]? o verifica con select(. != null).
Problemas de espacios en blanco en pipes: Usa comillas en tus variables. echo $json | jq '.' se rompe con espacios en blanco. Usa echo "$json" | jq '.' en su lugar.
Resumen
- jq es la herramienta estándar para procesamiento de JSON en la línea de comandos — un binario, sin dependencias, funciona en todas partes
- Usa
.field,.[index]y.[]para extracción básica; agrega-rpara salida de string sin formato en scripts select()filtra arrays por condición — soporta coincidencia de strings, comparaciones numéricas y regex contest()- Construye nuevas estructuras JSON con construcción de objetos
{key: .field}ymap()para transformaciones de arrays - Combina con
curl,kubectl,terraformydockerpara automatización del mundo real sort_by(),group_by(),length,addmanejan ordenamiento y agregación- Modifica JSON con asignación (
.key = value), adición (. + {}), y eliminación (del(.key)) - Siempre usa la bandera
-ry comillas en variables al integrar jq en scripts bash
Bash Scripting for Sysadmins: The Essential Guide | Install Security Updates from the Command Line in Ubuntu