jq — processor JSON com DSL

TL;DR

jq é processor JSON com DSL própria — pipeline de filtros (|), seleção (select), transformação (map), agregação (reduce). Lê JSON do stdin, escreve JSON do stdout. Flag -r emite strings sem aspas (essencial pra pipar pra cd/ssh); -s agrupa inputs num array; -e retorna exit não-zero quando filtro vazio. Essencial pra qualquer fluxo com API REST ou kubectl.

O que é / Como funciona

Filosofia: filtros componíveis em pipeline

jq modela processamento JSON como um pipeline de filtros. Cada filtro recebe um valor e emite 0, 1 ou N valores. O operador | encadeia filtros da esquerda pra direita — output de um vira input do próximo. Processamento é streaming: jq processa input-por-input sem carregar tudo na memória (exceto com -s).

Filtros essenciais

FiltroDescrição
.Identity — repassa input sem modificar
.fooAcessa campo foo do objeto; retorna null se ausente
.foo.barAcesso encadeado; ERROR se .foo for null
.[0]Primeiro elemento do array; .[-1] último
.[]Itera todos elementos do array ou valores do objeto
.foo?Como .foo, mas suprime erro em tipo errado
.[]?Como .[], mas suprime erros
.[2:4]Slice — subarray/substring (início inclusivo, fim exclusivo)

Operadores

OperadorDescrição
|Pipe — encadeia filtros
,Combina outputs — emite resultado de A e de B pra mesmo input
+Soma, concatenação de strings/arrays/objetos
-Subtração; remove elementos de array
*Multiplicação; merge recursivo de objetos
/Divisão; split de string
//Alternativa — retorna esquerda a menos que seja false/null

Funções built-in importantes

FunçãoDescrição
select(cond)Passa o valor adiante se cond for true; descarta se false
map(f)Aplica filtro f a cada elemento do array; retorna array
reduce STREAM as $v (INIT; UPDATE)Fold — acumula valores num único resultado
keysArray de chaves do objeto (sorted) ou índices do array
valuesArray de valores do objeto
lengthTamanho de string, array, objeto, ou 0 pra null
typeTipo do valor como string ("object", "array", "string", "number", "boolean", "null")
to_entriesConverte objeto em array de {key, value}
from_entriesInverso de to_entries
addSoma/concatena todos elementos do array
sort_by(f)Ordena array por expressão
group_by(f)Agrupa por expressão; retorna array de arrays
uniqueRemove duplicatas (requer array sorted)
flattenAchata arrays aninhados

Flags principais

FlagDescrição
-r, --raw-outputStrings sem aspas externas — essencial pra pipar pra cd/ssh/xargs
-c, --compact-outputUma linha por valor (sem pretty-print)
-s, --slurpAgrupa todos inputs num único array antes de processar
-e, --exit-statusExit 0 se output ≠ false/null; exit 1 se false/null; exit 4 se vazio
-n, --null-inputUsa null como input (útil com --arg/--argjson)
--arg name valueDefine variável $name como string
--argjson name jsonDefine variável $name como valor JSON
-f fileLê programa do arquivo

Exit code semantics

Por padrão, jq sai com código 0 sempre que parse é OK — independente do output. Com -e, a semântica muda: exit 0 se o último output foi algo que não false nem null; exit 1 se foi false/null; exit 4 se não houve output algum. Útil em scripts pra usar jq como asserção em condicionais.

Na prática

Receitas básicas

# Pretty-print qualquer JSON
curl -s https://api.github.com/users/jqlang | jq .
 
# Acessar campo
echo '{"name":"alice","age":30}' | jq '.name'
# → "alice"  (com aspas — JSON)
 
# Acessar campo como string pura (sem aspas)
echo '{"name":"alice"}' | jq -r '.name'
# → alice  (sem aspas — texto)
 
# Iterar array
echo '[1,2,3]' | jq '.[]'
# → 1
# → 2
# → 3
 
# Filtrar com select
echo '{"users":[{"name":"alice","active":true},{"name":"bob","active":false}]}' \
  | jq '.users[] | select(.active)'
# → {"name":"alice","active":true}
 
# Projetar campos
echo '{"users":[{"id":1,"name":"alice","email":"a@x.com"},{"id":2,"name":"bob","email":"b@x.com"}]}' \
  | jq '.users | map({id, name})'
# → [{"id":1,"name":"alice"},{"id":2,"name":"bob"}]
 
# Extrair emails como texto puro
curl -s https://api.example.com/users | jq -r '.users[].email'

Receitas avançadas

# Variável string com --arg
jq --arg user alice '.users[] | select(.name == $user)' users.json
 
# Variável JSON com --argjson
jq --argjson minAge 18 '.users[] | select(.age >= $minAge)' users.json
 
# Slurp — agregar múltiplos arquivos JSON
jq -s 'add' file1.json file2.json
# Junta dois arrays JSON em um
 
# -e como asserção em script
if curl -s https://api/health | jq -e '.ok == true' > /dev/null; then
  echo "API saudável"
else
  echo "API com problema"
fi
 
# CSV de campos selecionados
jq -r '.users[] | [.name, .email, .age | tostring] | @csv' users.json
 
# reduce — somar preços
echo '{"items":[{"price":10},{"price":25},{"price":5}]}' \
  | jq '[.items[].price] | reduce .[] as $p (0; . + $p)'
# → 40
 
# to_entries pra renomear chaves
echo '{"oldKey":"value"}' \
  | jq '.to_entries | map(.key = "newKey") | from_entries'
 
# Interpolação de string
echo '{"name":"alice","age":30}' \
  | jq -r '"Usuário: \(.name), idade: \(.age)"'
# → Usuário: alice, idade: 30
 
# rg --json | jq pra análise estruturada
rg --json 'TODO' | jq 'select(.type=="match") | .data.path.text' | sort -u

Versão

Exemplos testados com jq 1.7+; verifique jq --version localmente. A flag -e retorna exit 4 pra output vazio a partir da 1.6.

Armadilhas

(1) -r faltando — strings com aspas quebram pipe

Causa: sem -r, jq emite strings JSON com aspas ("alice" em vez de alice).

Sintoma: cd "$(jq '.path' file)" recebe "/home/alice" com aspas literais e falha com “No such file”.

Como detectar: output visível tem aspas ao redor do valor.

Solução: quando consumer é texto (cd, ssh, xargs, variável shell), SEMPRE use -r. Omita -r apenas quando o consumer espera JSON válido (outro jq, yq, parser).

(2) select vs map confundidos

Causa: select(cond) filtra — mantém o item se cond for true, descarta se false. map(f) transforma — aplica f a cada item e retorna array do mesmo tamanho.

Sintoma: “queria filtrar mas transformou” ou vice-versa.

Como detectar: select retorna o item original ou nada; map retorna SEMPRE array com resultado de f.

Solução: mnemonic — select = WHERE; map = SELECT (SQL). Combinado: .users | map(select(.active)) — filter dentro de transform.

(3) null em chave faltante vs erro

Causa: .foo em objeto sem a chave foo retorna null silenciosamente. .foo.bar quando .foo é null retorna ERROR: Cannot index null with string "bar".

Sintoma: stack trace no meio de um pipeline.

Como detectar: checar se objeto pode não ter a chave em questão.

Solução: operador opcional ?.foo?.bar? retorna null em vez de errar. Alternativa: if .foo then .foo.bar else null end. Ou select(.foo) | .foo.bar filtra antes de acessar.

(4) Números float vs int — perda de precisão

Causa: jq trata todos números como float64 (IEEE 754). Inteiros grandes (Twitter/X IDs, timestamps em nanosegundos) perdem precisão.

Sintoma: ID 123456789012345678 vira 123456789012345700 no output.

Como detectar: comparar byte-a-byte o input vs output de ..

Solução: preservar IDs grandes como string no JSON original; ou usar gojq (implementação Go) que suporta bigint nativo.

(5) UTF-8 BOM no input quebra parser

Causa: alguns Windows tools (PowerShell Export-Csv, Excel) prefixam BOM (EF BB BF) em JSON. jq não tolera BOM — falha imediatamente.

Sintoma: parse error (Invalid numeric literal at EOF at line 1, column 4) mesmo com JSON válido.

Como detectar: xxd file.json | head -1 mostra efbb bf7b no início.

Solução: strip BOM antes de pipar — sed '1s/^\xef\xbb\xbf//' file.json | jq ... ou iconv -f UTF-8-BOM -t UTF-8 file.json | jq ....

(6) -s slurp em stream gigante engole memória

Causa: -s agrupa TODO o input num único array antes de iniciar o processamento — em streams de log ou arquivos gigantes, tenta carregar tudo na RAM.

Sintoma: OOM killer mata jq, ou processo trava com memória subindo monotonicamente.

Como detectar: ps aux | grep jq mostra %MEM crescendo.

Solução: processar sem -s — pipas filtros linha a linha. Quando precisa de agregação sobre stream, use reduce que é incremental: jq -n 'reduce inputs as $x (0; . + $x.value)' big.ndjson.

Em inglês

  • pipeline de filtrosfilter pipeline. “jq modela processamento JSON como um filter pipeline onde output de um filtro vira input do próximo via |.”
  • identidadeidentity. “O filtro identity (.) repassa o input sem modificar — útil para pretty-print.”
  • saída brutaraw output. “raw output (-r) emite strings sem aspas JSON externas, essencial para pipar para cd ou ssh.”
  • slurpslurp. “O modo slurp (-s) lê todos os inputs e agrupa num array único antes de processar.”
  • selectselect. “A função select mantém ou descarta valores baseada em condição booleana.”
  • mapmap. “A função map transforma cada elemento do array aplicando um filtro e retorna array do mesmo tamanho.”
  • reducereduce. “reduce é o fold que acumula N valores em 1 resultado via função incremental.”
  • código de saídaexit code. “exit code é o status retornado ao shell; com -e, jq torna o exit code semanticamente significativo.”
  • projeçãoprojection. “projection constrói objeto com subconjunto de campos, como em map({id, name}).”
  • interpolaçãointerpolation. “string interpolation embute expressões dentro de strings via \"\\(.expr)\".”

Veja também

Referências