Jq: processando JSON no terminal
1. Introdução ao jq e instalação
O jq é um processador de JSON leve e flexível para linha de comando, essencial para qualquer profissional que trabalhe com Bash/Shell Script. Ele permite extrair, filtrar, transformar e formatar dados JSON diretamente no terminal, sem necessidade de linguagens mais pesadas como Python ou Node.js.
No contexto de shell script, o jq brilha ao processar saídas de APIs REST, arquivos de configuração, logs estruturados e qualquer fluxo de dados JSON. Sua filosofia é similar à de ferramentas Unix clássicas como sed, awk e grep, mas especificamente projetada para JSON.
Instalação
Linux (Debian/Ubuntu):
sudo apt update && sudo apt install jq
Linux (RHEL/CentOS/Fedora):
sudo yum install jq # CentOS 7
sudo dnf install jq # Fedora/RHEL 8+
macOS (Homebrew):
brew install jq
Verificação:
jq --version
# Saída esperada: jq-1.7.1 (ou similar)
Primeiro teste:
echo '{"chave":"valor"}' | jq .
# Saída:
# {
# "chave": "valor"
# }
2. Estrutura básica de comandos e filtros
A sintaxe fundamental do jq é: jq 'filtro' entrada. A entrada pode ser um arquivo ou pipe.
Filtro identidade (.)
O ponto simples retorna o JSON intacto, formatado:
echo '{"nome":"João","idade":30}' | jq .
Acesso a chaves
echo '{"pessoa":{"nome":"Maria","endereco":{"cidade":"SP"}}}' | jq '.pessoa.nome'
# "Maria"
echo '{"pessoa":{"nome":"Maria","endereco":{"cidade":"SP"}}}' | jq '.pessoa.endereco.cidade'
# "SP"
Acesso a arrays
echo '[10,20,30,40]' | jq '.[0]' # 10
echo '[10,20,30,40]' | jq '.[-1]' # 40 (último)
echo '[10,20,30,40]' | jq '.[]' # 10 20 30 40 (cada elemento em linha)
3. Filtros avançados de seleção
Seleção condicional com select()
echo '[{"nome":"Ana","idade":25},{"nome":"João","idade":35},{"nome":"Maria","idade":28}]' | \
jq '.[] | select(.idade > 28)'
# {
# "nome": "João",
# "idade": 35
# }
Operadores lógicos
echo '[{"nome":"Ana","idade":25,"cidade":"SP"},{"nome":"João","idade":35,"cidade":"RJ"}]' | \
jq '.[] | select(.idade > 25 and .cidade == "RJ")'
# {
# "nome": "João",
# "idade": 35,
# "cidade": "RJ"
# }
Filtro recursivo (..) e acesso opcional (.a?)
# Encontrar todos os valores numéricos em qualquer profundidade
echo '{"a":1,"b":{"c":2,"d":{"e":3}}}' | jq '.. | numbers'
# 1
# 2
# 3
# Acesso opcional (não gera erro se chave não existir)
echo '{"a":1}' | jq '.b?'
# null (sem erro)
4. Transformação e construção de dados
Criação de objetos
echo '{"nome":"Carlos","sobrenome":"Silva","idade":40}' | \
jq '{nome_completo: (.nome + " " + .sobrenome), idade: .idade}'
# {
# "nome_completo": "Carlos Silva",
# "idade": 40
# }
Criação de arrays
echo '{"x":10,"y":20,"z":30}' | jq '[.x, .y, .z]'
# [10, 20, 30]
Operador pipe (|) e funções
echo '[5,3,8,1,9]' | jq 'add' # 26 (soma)
echo '[5,3,8,1,9]' | jq 'length' # 5
echo '{"a":1,"b":2}' | jq '. | length' # 2 (número de chaves)
# map() - aplica filtro a cada elemento
echo '[1,2,3,4]' | jq 'map(. * 2)'
# [2, 4, 6, 8]
5. Formatação de saída e opções úteis
Saída compacta vs. indentada
# Compacta (útil para pipes)
echo '{"a":1,"b":2}' | jq -c '.'
# {"a":1,"b":2}
# Indentação personalizada
echo '{"a":1,"b":2}' | jq --tab '.' # usa tab
echo '{"a":1,"b":2}' | jq --indent 4 '.' # 4 espaços
Saída raw (-r)
Remove as aspas ao redor de strings, essencial para usar com outros comandos:
echo '{"nome":"João"}' | jq -r '.nome'
# João (sem aspas)
Lendo filtros de arquivo (-f)
# filtro.jq contendo: {nome, idade}
echo '{"nome":"Ana","idade":30,"cidade":"SP"}' | jq -f filtro.jq
# {
# "nome": "Ana",
# "idade": 30
# }
Silenciar erros com -e
Útil em scripts onde o erro não deve interromper o fluxo:
echo '{"a":1}' | jq -e '.b' 2>/dev/null || echo "Chave não encontrada"
6. Trabalhando com funções embutidas
Inspeção de objetos
echo '{"nome":"João","idade":30,"cidade":"SP"}' | jq 'keys'
# ["cidade", "idade", "nome"]
echo '{"nome":"João","idade":30}' | jq 'has("idade")'
# true
Estatísticas em arrays
echo '[5,3,8,1,9,3,5]' | jq '{min: min, max: max, sort: sort, unique: unique}'
# {
# "min": 1,
# "max": 9,
# "sort": [1, 3, 3, 5, 5, 8, 9],
# "unique": [1, 3, 5, 8, 9]
# }
Conversão de tipos
echo '"123"' | jq 'tonumber' # 123 (número)
echo '42' | jq 'tostring' # "42" (string)
echo '["a","b","c"]' | jq 'join("-")' # "a-b-c"
echo '"a-b-c"' | jq 'split("-")' # ["a","b","c"]
Conversão entre string JSON e objeto
echo '{"dados":"{\"valor\":42}"}' | jq '.dados | fromjson'
# {
# "valor": 42
# }
echo '{"valor":42}' | jq 'tojson'
# "{\"valor\":42}"
7. Exemplos práticos no terminal
Extrair dados de API REST
# Supondo uma API que retorna usuários
curl -s "https://api.exemplo.com/usuarios" | jq '.[] | {nome: .nome, email: .email}'
# Com saída raw para usar em loop
curl -s "https://api.exemplo.com/usuarios" | jq -r '.[] | "\(.nome): \(.email)"'
Filtrar logs JSON
# log.json contendo: {"nivel":"erro","mensagem":"Falha na conexão"}
# {"nivel":"info","mensagem":"Serviço iniciado"}
jq 'select(.nivel == "erro")' log.json
Combinar com xargs
# Baixar arquivos listados em JSON
echo '["doc1.pdf","doc2.pdf","doc3.pdf"]' | \
jq -r '.[]' | xargs -I {} wget "https://cdn.exemplo.com/{}"
Atualizar arquivos JSON (com sponge)
# Altere o campo "versao" para "2.0" no arquivo config.json
jq '.versao = "2.0"' config.json | sponge config.json
# Requer: sudo apt install moreutils
8. Boas práticas e armadilhas comuns
Aspas simples vs. duplas
Sempre use aspas simples para o filtro no shell, para evitar que o shell interprete caracteres especiais ($, \, "):
# Correto
echo '{"nome":"João"}' | jq '.nome'
# Incorreto (pode falhar)
echo '{"nome":"João"}' | jq ".nome"
Se precisar usar variáveis do shell, feche as aspas simples:
nome="João"
echo '{"nome":"João"}' | jq '.nome == "'$nome'"'
Escapando caracteres especiais
Dentro do filtro (aspas simples), use \" para aspas duplas e \\ para barra invertida:
echo '{"path":"C:\\Users"}' | jq '.path'
Diferença entre jq .arquivo e jq -f arquivo
jq .arquivo— processa o arquivo de dados chamado.arquivojq -f arquivo— lê o filtro do arquivo chamadoarquivo
Nunca confunda: jq filtro.jq dados.json funciona, mas jq .filtro.jq tenta ler um arquivo chamado .filtro.jq.
Performance com arquivos grandes
Para JSONs enormes (gigabytes), use --stream para processar sem carregar tudo em memória:
jq --stream 'select(.[0] | length == 2) | .[1]' megaarquivo.json
Referências
- Documentação oficial do jq — Manual completo com todos os filtros, funções e opções.
- jq play (online) — Ambiente interativo para testar filtros jq no navegador.
- Bash One-Liners Explained: jq — Artigo da Baeldung com exemplos práticos de jq no shell.
- How to Parse JSON with jq (DigitalOcean) — Tutorial passo a passo da DigitalOcean sobre transformação de dados JSON.
- jq Cheat Sheet (LZone) — Folha de consulta rápida com os comandos mais usados do jq.
- Process JSON from the Command Line with jq (Linux Journal) — Artigo clássico da Linux Journal sobre processamento de JSON no terminal.
- Moreutils: sponge — Documentação do sponge (moreutils), usado para edição in-place de arquivos com jq.