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. El código fuente está disponible en GitHub bajo la licencia MIT.
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
# 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:
jq --version
jq-1.8.1
Uso básico
Pretty-print de JSON
El filtro más simple es . — toma la entrada y la imprime formateada.
echo '{"name":"test","value":42,"active":true}' | jq '.'
{
"name": "test",
"value": 42,
"active": true
}
Para leer desde un archivo, pasa el nombre del archivo como argumento:
jq '.' data.json
Extraer campos
Usa notación de punto para acceder a campos específicos:
echo '{"name":"jq","version":"1.8.1"}' | jq '.name'
"jq"
Para salida de cadena sin comillas, usa -r (salida raw):
echo '{"name":"jq","version":"1.8.1"}' | jq -r '.name'
jq
Campos anidados
Encadena puntos para acceder a valores profundamente anidados:
echo '{"data":{"users":[{"name":"Alice","age":30}]}}' | jq '.data.users[0].name'
"Alice"
Operaciones con arrays
# 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
# 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:
echo '{"users":[{"name":"Alice"},{"name":"Bob"},{"name":"Charlie"}]}' \
| jq '.users[].name'
"Alice"
"Bob"
"Charlie"
select — filtrado condicional
select() mantiene solo los elementos que cumplen una condición:
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25},{"name":"Charlie","age":35}]}' \
| jq '.users[] | select(.age > 28)'
{
"name": "Alice",
"age": 30
}
{
"name": "Charlie",
"age": 35
}
map — transformar arrays
map() aplica un filtro a cada elemento y devuelve un nuevo array:
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
| jq '.users | map(.name)'
[
"Alice",
"Bob"
]
Construcción de objetos
Construye nuevos objetos con solo los campos que necesitas:
echo '{"users":[{"name":"Alice","age":30,"email":"alice@example.com","role":"admin"}]}' \
| jq '.users[] | {name: .name, email: .email}'
{
"name": "Alice",
"email": "alice@example.com"
}
Interpolación de cadenas
Usa \() para incrustar valores en cadenas. Combina con -r para salida limpia:
echo '{"users":[{"name":"Alice","email":"alice@example.com"},{"name":"Bob","email":"bob@example.com"}]}' \
| jq -r '.users[] | "\(.name): \(.email)"'
Alice: alice@example.com
Bob: bob@example.com
Expresiones condicionales
echo '{"status":"ok","data":"hello"}' \
| jq 'if .status == "ok" then .data else "error: \(.status)" end'
"hello"
Ordenación, agrupación y agregación
# 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
# 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.
curl -s https://api.github.com/repos/jqlang/jq \
| jq '{name: .name, stars: .stargazers_count, forks: .forks_count, license: .license.spdx_id}'
{
"name": "jq",
"stars": 31000,
"forks": 1600,
"license": "MIT"
}
Combinar resultados de respuestas paginadas de API:
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:
# Listar nombres de dependencias
jq '.dependencies | keys' package.json
[
"next",
"react",
"react-dom"
]
# Mostrar paquetes con versiones
jq -r '.dependencies | to_entries[] | "\(.key)@\(.value)"' package.json
next@^16.0.0
react@^19.0.0
react-dom@^19.0.0
# 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):
# Extraer logs de error con marcas de tiempo
cat app.log \
| jq -r 'select(.level == "error") | "\(.timestamp) [\(.level)] \(.message)"'
2026-03-08T10:15:30Z [error] Database connection timeout
2026-03-08T10:18:45Z [error] Failed to process request
# Contar entradas por nivel de log
cat app.log \
| jq -s 'group_by(.level) | map({level: .[0].level, count: length})'
[
{ "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:
# Actualizar un valor
jq '.database.port = 5433' config.json > tmp.json && mv tmp.json config.json
# Añadir un campo
jq '.database.ssl = true' config.json > tmp.json && mv tmp.json config.json
# Eliminar un campo
jq 'del(.debug)' config.json > tmp.json && mv tmp.json config.json
# Fusionar dos archivos JSON (el override tiene precedencia)
jq -s '.[0] * .[1]' base.json override.json > merged.json
Técnicas avanzadas
Pasar variables de shell de forma segura (--arg / --argjson)
Frecuentemente necesitarás usar variables de shell dentro de filtros jq. La interpolación directa de cadenas es frágil — usa --arg (cadenas) y --argjson (números, arrays, objetos) en su lugar.
# --arg: pasar como cadena
username="Alice"
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
| jq --arg name "${username}" '.users[] | select(.name == $name)'
{
"name": "Alice",
"age": 30
}
# --argjson: pasar como número o valor JSON (sin comillas)
min_age=28
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
| jq --argjson min "${min_age}" '.users[] | select(.age >= $min)'
# Construir dinámicamente un objeto JSON
key="version"
value="2.0"
jq -n --arg k "${key}" --arg v "${value}" '{($k): $v}'
{
"version": "2.0"
}
--arg siempre pasa el valor como cadena. Si necesitas comparación numérica, usa --argjson. Pasar "28" vía --arg no coincidirá con .age (número 28) en una comparación como select(.age >= $min) porque los tipos difieren.
Manejo de errores (try-catch / operador ?)
Esencial al procesar JSON incompleto o campos que pueden no existir.
# Operador ?: suprimir errores y continuar (atajo para try)
echo 'null' | jq '.foo?'
.foo? no genera un error de acceso a campo sobre null — devuelve null silenciosamente. Útil cuando las estructuras de datos varían en un pipeline.
# Operador //: devolver un respaldo cuando el valor es null o false
echo '{"a":1} {"b":2} {"a":3}' | jq '.a // empty'
1
3
# try-catch: devolver un valor de respaldo en caso de error
echo '{"data":"not-a-number"}' \
| jq 'try (.data | tonumber) catch "parse error"'
"parse error"
# Manejar arrays donde algunos elementos carecen de campos
echo '[{"name":"Alice","email":"a@example.com"},{"name":"Bob"}]' \
| jq '.[] | {name, email: (.email // "N/A")}'
{
"name": "Alice",
"email": "a@example.com"
}
{
"name": "Bob",
"email": "N/A"
}
Cadenas de formato (@base64 / @uri / @html / @tsv)
jq tiene formateadores incorporados para convertir la salida a formatos específicos.
# @base64: codificar en Base64
echo '{"token":"hello:world"}' | jq -r '.token | @base64'
aGVsbG86d29ybGQ=
# @base64d: decodificar Base64
echo '"aGVsbG86d29ybGQ="' | jq -r '@base64d'
hello:world
# @uri: codificar URL (útil para construir parámetros de consulta)
echo '{"q":"jq filter examples","lang":"ja"}' \
| jq -r '"https://example.com/search?q=\(.q | @uri)&lang=\(.lang)"'
https://example.com/search?q=jq%20filter%20examples&lang=ja
# @tsv: convertir arrays a TSV (valores separados por tabulaciones)
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' \
| jq -r '.[] | [.name, .age] | @tsv'
Alice 30
Bob 25
Procesamiento streaming de JSON grandes (--stream)
Procesar un archivo JSON de cientos de megabytes con jq '.' carga todo en memoria. --stream lo procesa incrementalmente como pares de ruta-valor.
# --stream: procesamiento incremental (ahorra memoria)
echo '{"users":[{"name":"Alice"},{"name":"Bob"}]}' \
| jq --stream 'select(.[0][-1] == "name") | .[1]'
"Alice"
"Bob"
# Extraer una clave específica de un archivo enorme sin cargarlo completo
jq --stream 'select(.[0][0] == "error_count") | .[1]' huge_report.json
El modo streaming usa una sintaxis de filtros diferente al modo normal, así que tiene una curva de aprendizaje. Pero cuando el modo regular se queda sin memoria, esta es tu alternativa.
Acceder a variables de entorno ($ENV / env)
Útil en pipelines CI/CD y scripts donde necesitas incorporar variables de entorno en JSON.
export APP_VERSION="1.5.0"
export APP_ENV="production"
# $ENV: acceder a variables de entorno específicas
jq -n '{version: $ENV.APP_VERSION, env: $ENV.APP_ENV}'
{
"version": "1.5.0",
"env": "production"
}
# env: acceder a todas las variables de entorno como objeto
jq -n 'env | keys | map(select(startswith("APP_")))'
[
"APP_ENV",
"APP_VERSION"
]
$ENV vs env: $ENV.KEY accede directamente a una clave específica. env devuelve todas las variables de entorno como objeto, permitiendo filtrar con keys o select.
Ejemplos de scripting
Integrar jq en scripts de shell facilita el procesamiento automatizado de JSON. Combínalo con xargs para canalizar valores extraídos a otros comandos en paralelo.
Exportar repos de GitHub a CSV
#!/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
@csvmaneja el formateo CSV automáticamente, incluyendo comillas y escape // "N/A"es el operador alternativo — devuelve un valor por defecto cuando un valor esnullofalse- La paginación se gestiona con un bucle simple hasta obtener una respuesta vacía
Fusionar y agregar informes JSON
#!/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 arraygroup_byymapjuntos producen conteos por estado// 0proporciona valores de respaldo para campos ausentesfloortrunca 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 --versionmuestra 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 -cantes 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
evalobash -c. Siempre captura los valores en variables entre comillas
# 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}"
Artículos relacionados
- Guía completa de curl —
curl | jqes el flujo de trabajo más común con jq. Desde comunicación con APIs hasta manipulación de encabezados - Guía práctica de grep y ripgrep — cuando necesitas buscar texto en datos extraídos con jq
- Guía práctica de sed y awk — para post-procesar texto después de convertir JSON a CSV o TSV
- Guía práctica de shell scripting — conocimiento base para integrar jq en scripts
- Guía práctica de xargs — canaliza valores extraídos con jq a otros comandos en paralelo
- Kit de herramientas CLI — el panorama general de herramientas CLI y cuándo usar cada una
FAQ
¿Cuál es la diferencia entre jq y yq?
jq es un procesador exclusivo de JSON. yq también maneja YAML, XML y TOML, usando una sintaxis similar a jq internamente. Para trabajar solo con JSON, jq es más rápido y ligero. Si necesitas procesar YAML u otros formatos, vale la pena considerar yq.
¿Puedo usar jq sin instalarlo?
Sí — jq play es un playground online donde puedes probar filtros en tu navegador. Es genial para aprender antes de instalar o depurar expresiones complejas. Solo no pegues datos sensibles en él.
jq se queda sin memoria con archivos JSON grandes. ¿Qué puedo hacer?
Usa la opción --stream para procesar JSON incrementalmente como pares de ruta-valor, lo que reduce significativamente el uso de memoria. La sintaxis de filtros difiere del modo normal, pero es la solución ideal para archivos de cientos de megabytes o más.
¿Cómo convierto la salida de jq a CSV?
Usa el formateador @csv. Construye un array y pásalo a @csv: jq -r '.[] | [.name, .age] | @csv'. Maneja las comillas y el escape automáticamente. Para salida TSV, usa @tsv en su lugar.
¿Cómo uso variables de shell dentro de filtros jq?
Usa --arg (para cadenas) o --argjson (para números, arrays, objetos). La interpolación directa de cadenas es frágil y propensa a errores. Para comparaciones numéricas, debes usar --argjson — de lo contrario, la incompatibilidad de tipos entre una cadena y un número causará resultados inesperados.
¿Cómo manejo null o campos faltantes de forma segura en jq?
El operador // (operador alternativo) proporciona un valor de respaldo: .email // "N/A". El operador ? suprime errores: .foo?. Para más control, usa try-catch: try (.data | tonumber) catch "parse error".
¿Debería usar jq o el módulo json de Python?
Para one-liners y procesamiento en scripts de shell, jq es significativamente más rápido. Para lógica compleja (muchas ramificaciones, integración con bases de datos, etc.), Python ofrece mejor legibilidad. En pipelines CI/CD, se prefiere jq por sus dependencias mínimas.
Conclusión
jq es la herramienta esencial para el procesamiento de JSON en la línea de comandos.
- Básico —
.para pretty-printing,.campopara extracción,-rpara salida de cadenas sin comillas - Filtros —
selectpara filtrado condicional,mappara 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,-spara 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í.
Artículos relacionados: