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

HerramientaMejor ParaDesventajas
jqTransformaciones rápidas CLI, pipes de shellLa lógica compleja se vuelve verbosa
Python jsonProcesamiento multi-paso, lógica complejaRequiere runtime Python, más código
yqProcesamiento YAML (sintaxis similar a jq)Instalación separada, específico para YAML
fxExploración interactiva de JSONNo scriptable
gronHacer 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 -r es 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 -r para salida de string sin formato en scripts
  • select() filtra arrays por condición — soporta coincidencia de strings, comparaciones numéricas y regex con test()
  • Construye nuevas estructuras JSON con construcción de objetos {key: .field} y map() para transformaciones de arrays
  • Combina con curl, kubectl, terraform y docker para automatización del mundo real
  • sort_by(), group_by(), length, add manejan ordenamiento y agregación
  • Modifica JSON con asignación (.key = value), adición (. + {}), y eliminación (del(.key))
  • Siempre usa la bandera -r y 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