jq One-Liners Filtrar y transformar JSON desde la línea de comandos sin escribir un script
Siete recetas prácticas de jq para desarrolladores backend y DevOps — que abarcan select, map, to_entries, del, group_by y la reestructuración de APIs en entornos reales. Se muestra la entrada y la salida para cada patrón.
Estás viendo una respuesta de API minificada en tu terminal. Necesitas extraer 3 campos, filtrar los nulos y pipear el resultado a algo más. Escribir un script en Python para esto sería 20 líneas y 2 minutos que no tienes. jq Lo maneja en una sola orden — si conoces los patrones.
Siete recetas a continuación. Cada una cubre un patrón que verás repetidamente al trabajar con respuestas de API o archivos de registro. Se muestra la entrada, el comando y la salida para cada receta.
Instalar
brew install jq # macOS
apt-get install jq # Ubuntu/Debian
apk add jq # Alpine (Docker images)
Receta 1: Extraer un campo de cada objeto en un array
La cosa más común que harás con jq: extraer un campo de cada elemento en un array de JSON.
Entrada (users.json)
[
{"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"},
{"id": 2, "name": "Bob", "email": "bob@example.com", "role": "user"},
{"id": 3, "name": "Carol", "email": "carol@example.com", "role": "user"}
]
jq '.[].name' users.json
# → "Alice"
# "Bob"
# "Carol" (newline-separated stream)
.[] itera sobre cada elemento del array; .name extrae el campo. La salida es un flujo — útil para pipear a otros comandos. Si necesitas un array de JSON completo:
jq 'map(.name)' users.json
# → ["Alice", "Bob", "Carol"]
map(.name) es abreviatura de [.[] | .name] — aplica la expresión a cada elemento y envuelve los resultados en un array. Lo usarás mucho. map() mucho.
Receta 2: Filtrar con select
Mantén solo los objetos que cumplen con una condición; descarta el resto.
jq 'map(select(.role == "admin"))' users.json
[
{"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"}
]
select(expr) envía el elemento solo cuando la expresión es verdadera — todo lo demás desaparece. Envolverlo en map() mantiene la salida como un array. Más ejemplos:
# Numeric comparison
jq 'map(select(.score > 80))' results.json
# Not-null check
jq 'map(select(.error != null))' events.json
# String contains (requires test)
jq 'map(select(.message | test("timeout")))' logs.json
Receta 3: Rediseñar objetos con map
Selecciona solo los campos que necesitas y opcionalmente renombra en la misma pasada.
jq 'map({name: .name, role: .role})' users.json
[
{"name": "Alice", "role": "admin"},
{"name": "Bob", "role": "user"},
{"name": "Carol", "role": "user"}
]
Estás construyendo un nuevo objeto literal para cada elemento. Para renombrar una clave, cambia el lado izquierdo de la coma:
jq 'map({username: .name, access_level: .role})' users.json
Una abreviatura que ahorra teclas: {name} es equivalente a {name: .name}. Si quieres un campo tal como está, no necesitas escribir la clave dos veces:
jq 'map({id, name, role})' users.json
Receta 4: Renombrar claves con to_entries y from_entries
map es útil cuando sabes cuáles claves existen. Cuando necesitas transformar las claves mismas — añadir un prefijo, convertir convenciones de nomenclatura o renombrar según una búsqueda — to_entries es el patrón adecuado.
Entrada (config.json)
{"api_url": "https://api.example.com", "timeout": 30, "retry_count": 3}
# Add cfg_ prefix to every key
jq 'to_entries | map(.key = "cfg_" + .key) | from_entries' config.json
{"cfg_api_url": "https://api.example.com", "cfg_timeout": 30, "cfg_retry_count": 3}
to_entries convierte {"k": "v"} en [{"key": "k", "value": "v"}]. Después de modificar los elementos en el array resultante, from_entries reconstruye el objeto. Para renombrar una clave específica sin tocar las demás:
jq 'to_entries | map(if .key == "api_url" then .key = "endpoint" else . end) | from_entries' config.json
Receta 5: Eliminar campos con del
La inversa de rediseñar: mantener todo y eliminar algunos campos específicos. El caso principal es eliminar datos sensibles antes de registrarlos o antes de pasar un payload a un servicio de terceros.
jq 'map(del(.email))' users.json
[
{"id": 1, "name": "Alice", "role": "admin"},
{"id": 2, "name": "Bob", "role": "user"},
{"id": 3, "name": "Carol", "role": "user"}
]
Eliminar múltiples campos de forma simultánea, o eliminar rutas anidadas:
# Multiple top-level fields at once
jq 'del(.password, .token, .refresh_token)' user.json
# Nested path
jq 'del(.user.internal_id)' response.json
# All fields named "debug" at any depth (recursive descent)
jq 'del(.. | .debug? // empty)' response.json
Receta 6: Filtrar, rediseñar y ordenar en una sola pasada
La verdadera recompensa es encadenar estos elementos básicos. Aquí hay un patrón que aparece constantemente al trabajar con respuestas paginadas de API: descender en un array anidado, filtrarlo, rediseñar los objetos y ordenar el resultado.
Entrada (repos.json)
{
"total_count": 3,
"items": [
{"id": 1, "name": "repo-alpha", "stargazers_count": 142, "language": "Go", "private": false},
{"id": 2, "name": "repo-beta", "stargazers_count": 89, "language": "Python", "private": true},
{"id": 3, "name": "repo-gamma", "stargazers_count": 310, "language": "Go", "private": false}
]
}
jq '.items
| map(select(.private == false and .language == "Go"))
| sort_by(-.stargazers_count)
| map({name, stars: .stargazers_count})' repos.json
[
{"name": "repo-gamma", "stars": 310},
{"name": "repo-alpha", "stars": 142}
]
El pipeline:
.items— descender en el array anidadomap(select(...))— filtrar en una sola pasada;andencadena condicionessort_by(-.stargazers_count)— negar el valor para orden descendente;sort_by(.field)solamente da orden ascendentemap({name, stars: .stargazers_count})— rediseño final;{name}es abreviatura de{name: .name}
Receta 7: Contar niveles de registro con group_by
Análisis de frecuencia en salida estructurada de registros — sin necesidad de base de datos, sin ejercicios de awk.
Entrada (logs.json)
[
{"level": "error", "msg": "connection timeout", "service": "auth"},
{"level": "info", "msg": "request received", "service": "api"},
{"level": "error", "msg": "null pointer exception", "service": "worker"},
{"level": "warn", "msg": "slow query detected", "service": "db"},
{"level": "error", "msg": "rate limit exceeded", "service": "api"}
]
jq 'group_by(.level) | map({level: .[0].level, count: length}) | sort_by(-.count)' logs.json
[
{"level": "error", "count": 3},
{"level": "info", "count": 1},
{"level": "warn", "count": 1}
]
group_by(.level) devuelve un array de arrays — cada sub-array contiene todos los elementos que comparten el mismo valor de nivel. .[0].level extrae el nombre del nivel del primer elemento del grupo; length cuenta cuántos elementos hay en el grupo.
Añadir un desglose por servicio en la misma consulta:
jq 'group_by(.level) | map({
level: .[0].level,
count: length,
services: map(.service) | unique
})' logs.json
Guía rápida
Los patrones anteriores en forma de tabla para cuando necesitas un recordatorio rápido:
| Lo que deseas | Expresión de jq |
|---|---|
| Todos los valores de un campo | map(.field) |
| Filtrar elementos coincidentes | map(select(.field == "val")) |
| Rediseñar cada objeto | map({newKey: .oldKey}) |
| Renombrar todas las claves | to_entries | map(.key = ...) | from_entries |
| Eliminar campos | map(del(.field1, .field2)) |
| Ordenar ascendentemente | sort_by(.field) |
| Ordenar descendente | sort_by(-.field) |
| Contar por grupo | group_by(.field) | map({key: .[0].field, count: length}) |
| Obtener todas las claves de un objeto | keys |
| Contar elementos en un array | length |
| Valores únicos de un campo | map(.field) | unique |
Antes de comenzar: Dominar el bloque
Las respuestas de API a menudo llegan minificadas o profundamente anidadas. Si no estás seguro del camino que necesitas, pega el JSON en el Formateador JSON — lo imprime de forma legible con nodos colapsables para que puedas identificar el camino antes de escribir la jq expresión. Después de aplicar una transformación, la Comparar JSON herramienta es útil para verificar la diferencia antes y después cuando estás rediseñando o eliminando campos y deseas confirmar que nada cambió inesperadamente.
Estos siete patrones cubren la mayor parte de las tareas de manipulación de JSON que harás en la línea de comandos. El verdadero poder está en encadenarlos — una vez que puedas filtrar, rediseñar y ordenar, podrás procesar la mayoría de las respuestas de API sin tocar un archivo de script.
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta se agregó el 9 de junio de 2026
