32blogby StudioMitsu

Guía completa de jq: domina el procesamiento JSON en la terminal

Aprende jq desde filtros básicos hasta transformación avanzada de JSON: respuestas de API, archivos de configuración e integración con scripts.

10 min read
Contenido

Necesitas extraer campos específicos de una respuesta de API. Parsear un package.json para listar dependencias. Filtrar entradas de error de logs en formato JSON. Todo desde la terminal, sin escribir un script.

Eso es lo que hace jq. Conocido como la navaja suiza del procesamiento JSON, maneja el formateo, filtrado, transformación y agregación de datos JSON — todo en un solo comando.

Esta guía recorre todo, desde filtros básicos hasta ejemplos de scripting del mundo real, con comandos listos para ejecutar.

¿Qué es jq?

jq es un procesador JSON ligero de línea de comandos escrito en C. Se distribuye como un binario único sin dependencias externas.

Características clave:

  • Pretty-printing — formatea JSON minificado en una salida legible
  • Extracción de campos — obtén exactamente los valores que necesitas
  • Filtrado — selecciona datos que cumplan condiciones específicas
  • Transformación — reestructura datos y calcula agregados
  • Encadenamiento — combina múltiples operaciones en una sola expresión

Así como sed y awk son las herramientas de referencia para el procesamiento de texto, jq es el estándar para JSON. Combinado con curl, es una parte esencial de cualquier kit de herramientas CLI moderno.

Instalación

powershell
# winget (recomendado)
winget install jqlang.jq

# Si usas WSL, instala vía el gestor de paquetes de Linux
# sudo apt install jq

Verifica la instalación:

bash
jq --version
text
jq-1.8.1

Uso básico

Pretty-print de JSON

El filtro más simple es . — toma la entrada y la imprime formateada.

bash
echo '{"name":"test","value":42,"active":true}' | jq '.'
json
{
  "name": "test",
  "value": 42,
  "active": true
}

Para leer desde un archivo, pasa el nombre del archivo como argumento:

bash
jq '.' data.json

Extraer campos

Usa notación de punto para acceder a campos específicos:

bash
echo '{"name":"jq","version":"1.8.1"}' | jq '.name'
text
"jq"

Para salida de cadena sin comillas, usa -r (salida raw):

bash
echo '{"name":"jq","version":"1.8.1"}' | jq -r '.name'
text
jq

Campos anidados

Encadena puntos para acceder a valores profundamente anidados:

bash
echo '{"data":{"users":[{"name":"Alice","age":30}]}}' | jq '.data.users[0].name'
text
"Alice"

Operaciones con arrays

bash
# Primer elemento
echo '[10,20,30]' | jq '.[0]'

# Último elemento
echo '[10,20,30]' | jq '.[-1]'

# Longitud del array
echo '[10,20,30]' | jq 'length'

# Porción (índice 1 a 2)
echo '[10,20,30,40]' | jq '.[1:3]'

Formato de salida

bash
# Salida compacta (una sola línea)
echo '{"name":"test","value":42}' | jq -c '.'

# Indentación con tabulaciones
echo '{"name":"test","value":42}' | jq --tab '.'

# Claves ordenadas
echo '{"b":2,"a":1}' | jq -S '.'

Filtros y pipes

El verdadero poder de jq reside en combinar filtros. Usa el operador pipe | para encadenar operaciones.

Iterador de arrays

[] expande cada elemento de un array:

bash
echo '{"users":[{"name":"Alice"},{"name":"Bob"},{"name":"Charlie"}]}' \
  | jq '.users[].name'
text
"Alice"
"Bob"
"Charlie"

select — filtrado condicional

select() mantiene solo los elementos que cumplen una condición:

bash
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25},{"name":"Charlie","age":35}]}' \
  | jq '.users[] | select(.age > 28)'
json
{
  "name": "Alice",
  "age": 30
}
{
  "name": "Charlie",
  "age": 35
}

map — transformar arrays

map() aplica un filtro a cada elemento y devuelve un nuevo array:

bash
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
  | jq '.users | map(.name)'
json
[
  "Alice",
  "Bob"
]

Construcción de objetos

Construye nuevos objetos con solo los campos que necesitas:

bash
echo '{"users":[{"name":"Alice","age":30,"email":"alice@example.com","role":"admin"}]}' \
  | jq '.users[] | {name: .name, email: .email}'
json
{
  "name": "Alice",
  "email": "alice@example.com"
}

Interpolación de cadenas

Usa \() para incrustar valores en cadenas. Combina con -r para salida limpia:

bash
echo '{"users":[{"name":"Alice","email":"alice@example.com"},{"name":"Bob","email":"bob@example.com"}]}' \
  | jq -r '.users[] | "\(.name): \(.email)"'
text
Alice: alice@example.com
Bob: bob@example.com

Expresiones condicionales

bash
echo '{"status":"ok","data":"hello"}' \
  | jq 'if .status == "ok" then .data else "error: \(.status)" end'
text
"hello"

Ordenación, agrupación y agregación

bash
# Ordenar por campo
echo '[{"name":"Charlie","age":35},{"name":"Alice","age":30},{"name":"Bob","age":25}]' \
  | jq 'sort_by(.age)'

# Valores únicos
echo '["apple","banana","apple","cherry","banana"]' | jq 'unique'

# Suma
echo '[10,20,30,40]' | jq 'add'

# Promedio
echo '[10,20,30,40]' | jq 'add / length'

keys y has

bash
# Listar todas las claves
echo '{"name":"test","version":"1.0","license":"MIT"}' | jq 'keys'

# Verificar si existe una clave
echo '{"name":"test"}' | jq 'has("name")'

Casos de uso reales

Procesar respuestas de API

Combinar jq con curl para extraer datos de respuestas de API es uno de los casos de uso más comunes.

bash
curl -s https://api.github.com/repos/jqlang/jq \
  | jq '{name: .name, stars: .stargazers_count, forks: .forks_count, license: .license.spdx_id}'
json
{
  "name": "jq",
  "stars": 31000,
  "forks": 1600,
  "license": "MIT"
}

Combinar resultados de respuestas paginadas de API:

bash
for page in 1 2 3; do
  curl -s "https://api.github.com/users/octocat/repos?per_page=100&page=${page}"
done | jq -s 'flatten | map({name: .name, stars: .stargazers_count}) | sort_by(.stars) | reverse'

Parsear package.json

Inspecciona rápidamente las dependencias del proyecto sin abrir el archivo:

bash
# Listar nombres de dependencias
jq '.dependencies | keys' package.json
json
[
  "next",
  "react",
  "react-dom"
]
bash
# Mostrar paquetes con versiones
jq -r '.dependencies | to_entries[] | "\(.key)@\(.value)"' package.json
text
next@^16.0.0
react@^19.0.0
react-dom@^19.0.0
bash
# Contar dependencies vs devDependencies
jq '{deps: (.dependencies | length), devDeps: (.devDependencies | length)}' package.json

Análisis de logs JSON

Procesar archivos de log en formato JSON Lines (un objeto JSON por línea):

bash
# Extraer logs de error con marcas de tiempo
cat app.log \
  | jq -r 'select(.level == "error") | "\(.timestamp) [\(.level)] \(.message)"'
text
2026-03-08T10:15:30Z [error] Database connection timeout
2026-03-08T10:18:45Z [error] Failed to process request
bash
# Contar entradas por nivel de log
cat app.log \
  | jq -s 'group_by(.level) | map({level: .[0].level, count: length})'
json
[
  { "level": "error", "count": 5 },
  { "level": "info", "count": 142 },
  { "level": "warn", "count": 23 }
]

Editar archivos de configuración

Modifica archivos de configuración JSON desde la línea de comandos:

bash
# Actualizar un valor
jq '.database.port = 5433' config.json > tmp.json && mv tmp.json config.json
bash
# Añadir un campo
jq '.database.ssl = true' config.json > tmp.json && mv tmp.json config.json
bash
# Eliminar un campo
jq 'del(.debug)' config.json > tmp.json && mv tmp.json config.json
bash
# Fusionar dos archivos JSON (el override tiene precedencia)
jq -s '.[0] * .[1]' base.json override.json > merged.json

Ejemplos de scripting

Exportar repos de GitHub a CSV

bash
#!/bin/bash
# github-repos-to-csv.sh
# Exportar los repositorios públicos de un usuario a formato CSV

USERNAME="${1:?Uso: $0 <github-username>}"
OUTPUT="repos.csv"

echo "name,stars,forks,language,updated" > "${OUTPUT}"

page=1
while true; do
  response=$(curl -s "https://api.github.com/users/${USERNAME}/repos?per_page=100&page=${page}")

  count=$(echo "${response}" | jq 'length')
  if [ "${count}" -eq 0 ]; then
    break
  fi

  echo "${response}" \
    | jq -r '.[] | [.name, .stargazers_count, .forks_count, (.language // "N/A"), .updated_at[:10]] | @csv' \
    >> "${OUTPUT}"

  page=$((page + 1))
done

total=$(tail -n +2 "${OUTPUT}" | wc -l)
echo "Exportados ${total} repositorios a ${OUTPUT}"

Puntos clave:

  • El filtro @csv maneja el formateo CSV automáticamente, incluyendo comillas y escape
  • // "N/A" es el operador alternativo — devuelve un valor por defecto cuando un valor es null
  • La paginación se gestiona con un bucle simple hasta obtener una respuesta vacía

Fusionar y agregar informes JSON

bash
#!/bin/bash
# merge-json-reports.sh
# Combinar archivos de informes JSON y generar estadísticas resumen

REPORT_DIR="${1:?Uso: $0 <directorio-de-informes>}"

if [ ! -d "${REPORT_DIR}" ]; then
  echo "Error: Directorio '${REPORT_DIR}' no encontrado" >&2
  exit 1
fi

file_count=$(find "${REPORT_DIR}" -name "*.json" -type f | wc -l)
if [ "${file_count}" -eq 0 ]; then
  echo "Error: No se encontraron archivos JSON en '${REPORT_DIR}'" >&2
  exit 1
fi

# Fusionar todos los archivos JSON y calcular resumen
find "${REPORT_DIR}" -name "*.json" -type f -exec cat {} + \
  | jq -s '{
    total_files: length,
    total_records: (map(.records // 0) | add),
    total_errors: (map(.errors // 0) | add),
    avg_duration_ms: (map(.duration_ms // 0) | add / length | floor),
    statuses: (group_by(.status) | map({status: .[0].status, count: length})),
    date_range: {
      earliest: (map(.timestamp) | sort | first),
      latest: (map(.timestamp) | sort | last)
    }
  }'

echo ""
echo "Procesados ${file_count} archivos de ${REPORT_DIR}"

Puntos clave:

  • -s (slurp) combina múltiples objetos JSON en un solo array
  • group_by y map juntos producen conteos por estado
  • // 0 proporciona valores de respaldo para campos ausentes
  • floor trunca los decimales en el promedio

Nota de seguridad

Al procesar JSON no confiable, ten en cuenta estos puntos:

  • Mantén jq actualizado — las vulnerabilidades del parser se descubren periódicamente. Si jq --version muestra algo por debajo de 1.8.1, actualiza inmediatamente
  • Limita el tamaño de entrada — procesar archivos JSON muy grandes puede consumir mucha memoria. Usa head -c antes en el pipeline para limitar el tamaño de entrada, o considera el parser streaming (--stream) para conjuntos de datos grandes
  • Protégete contra inyección de shell — nunca canalices la salida de jq directamente a eval o bash -c. Siempre captura los valores en variables entre comillas
bash
# Peligroso (nunca hagas esto)
eval $(curl -s https://example.com/config.json | jq -r '.command')

# Seguro (capturar en una variable)
value=$(curl -s https://example.com/config.json | jq -r '.setting')
echo "Configuración: ${value}"

Conclusión

jq es la herramienta esencial para el procesamiento de JSON en la línea de comandos.

  • Básico. para pretty-printing, .campo para extracción, -r para salida de cadenas sin comillas
  • Filtrosselect para filtrado condicional, map para transformación, pipes para encadenar
  • Mundo real — procesamiento de respuestas de API, edición de archivos de configuración, análisis de logs
  • Scripting — conversión con @csv, -s para fusionar, integración con scripts de shell

Combina curl para la obtención de datos, jq para el procesamiento JSON y sed & awk para el formateo de texto — y tendrás un potente pipeline de procesamiento de datos directamente en tu terminal. Empieza con curl ... | jq '.' y construye desde ahí.

Para una visión más amplia de herramientas CLI, consulta el Kit de herramientas CLI.