Básico do jq: Como Processar JSON no Shell
O Que Você Vai Aprender
- Como formatar JSON de
curlou respostas de API em formato legível - O vocabulário essencial do jq que você realmente precisa:
select,map,-re mais - Padrões seguros que evitam as armadilhas comuns: aspas perdidas vazando em variáveis do shell, propagação de null e escritas de arquivo quebradas
Referência Rápida (padrões de produção)
- Apenas formatar ->
jq . - Extrair um valor ->
jq -r '.field' - Explodir um array em um JSON por linha ->
jq -c '.[]' - Filtrar por condição ->
jq '.[] | select(.status=="ok")' - Evitar propagação de
null->jq '.field // empty'
Ambiente Assumido
- jq 1.6 ou posterior (Ubuntu 20.04+
apt install jqé suficiente; diferenças da 1.7 indicadas inline) - Referência oficial: jq Manual
O Que é o jq?
jq é uma linguagem de filtro e ferramenta de linha de comando com reconhecimento de JSON. Diferente de forçar grep/awk em JSON, jq entende a estrutura, o que significa menos desastres com regex em pipelines de API, agregação de logs e scripts de CI.
- Segue o modelo de pipe Unix: stdin -> filtro -> stdout
- Expressões de filtro são avaliadas da esquerda para a direita, assim como pipes do shell
- Tem um sistema de tipos real: números, strings, arrays, objetos,
nulle booleanos
Como Instalar o jq?
O caminho mais rápido é o gerenciador de pacotes do seu sistema. Se você usar jq em scripts de CI, fixe a versão com jq --version porque o comportamento difere entre releases principais.
# Ubuntu / Debian $ sudo apt update && sudo apt install jq # RHEL / Rocky / AlmaLinux $ sudo dnf install jq # macOS (Homebrew) $ brew install jq # Verificar $ jq --version
Em CentOS 7 mais antigo, você precisa do epel-release primeiro. Para runners de CI, o binário estático único da página oficial de releases colocado em /usr/local/bin/jq é a opção mais portável.
Como Ler um Filtro Básico?
Um filtro jq é uma transformação aplicada à sua entrada. O filtro vazio . é a identidade (formata e retorna). Use .field para ler uma chave de objeto e .[] para explodir um array.
1. Formatação
$ echo '{"name":"linny","age":3}' | jq .{
"name": "linny",
"age": 3
}
2. Extrair uma chave
$ echo '{"name":"linny","age":3}' | jq '.name'"linny"
3. Iterar sobre um array
$ echo '[{"id":1},{"id":2}]' | jq '.[]'{"id":1}
{"id":2}
Sempre envolva filtros em aspas simples. Aspas duplas deixam o shell interpretar $ e crases, o que corrompe sua expressão jq. '...' é a regra.
Como Trabalhar com Objetos e Arrays?
Combine .[] (explodir array), , (avaliação paralela) e | (encadear filtros). Parece exatamente com grep | awk.
Extrair um campo de cada elemento
$ echo '[{"id":1,"tag":"a"},{"id":2,"tag":"b"}]' \
| jq '.[].tag'"a" "b"
Construir uma tupla de campos selecionados
$ echo '[{"id":1,"tag":"a"},{"id":2,"tag":"b"}]' \
| jq '.[] | {id, tag}'{"id":1,"tag":"a"}
{"id":2,"tag":"b"}
{id, tag} é abreviação para {id: .id, tag: .tag}. Funciona apenas para chaves de identificador simples.
Descer com segurança em campos aninhados
$ echo '{"a":{"b":{"c":42}}}' | jq '.a.b.c'
42
# Chaves ausentes produzem null; o ? suprime erros de iteracao
$ echo '{}' | jq '.a.b?.c?'
nullComo Filtrar com select?
select(condition) passa adiante apenas valores onde a condição é verdadeira. O padrão é explodir um array com .[] primeiro, depois encadear com select. Pense em SQL WHERE.
Filtro de igualdade
$ echo '[{"s":"ok"},{"s":"ng"},{"s":"ok"}]' \
| jq '.[] | select(.s=="ok")'{"s":"ok"}
{"s":"ok"}
Condições numéricas e compostas
$ jq '.[] | select(.score >= 80 and .active)'
$ jq '.[] | select(.tag=="a" or .tag=="b")'
$ jq '.[] | select(.tag | startswith("v1"))'Verificar a presenca de uma chave
$ echo '[{"a":1},{"b":2}]' \
| jq '.[] | select(has("a"))'{"a":1}
O padrão "filtrar depois extrair"
jq '.[] | select(.status=="error") | .message'
Mantenha a ordem: explodir -> filtrar -> extrair. É mais fácil depurar passo a passo. Adicione // empty se quiser pular nulls silenciosamente.
O Que map, length e keys Fazem?
map(f) aplica f a cada elemento de um array, length retorna o tamanho e keys lista as chaves de um objeto. Estas são as operações em massa para trabalhar em um array como um todo.
map: transformar um array
$ echo '[1,2,3]' | jq 'map(. * 10)'
[ 10, 20, 30 ]
map(f) é equivalente a [.[] | f]. A diferença: .[] | f transmite um elemento por vez, enquanto map(f) retorna o array intacto.
length: contar elementos
$ echo '[{"id":1},{"id":2},{"id":3}]' | jq 'length'
3Para strings retorna a contagem de caracteres, para objetos o número de chaves, para null retorna 0. Sensível ao tipo.
keys: listar as chaves
$ echo '{"a":1,"c":3,"b":2}' | jq 'keys'[ "a", "b", "c" ]
keys é ordenado, keys_unsorted preserva a ordem de inserção. Use keys quando precisar de saída determinística para testes.
Como Formatar a Saída? (-r / -c)
jq . é para humanos, -r (raw) é para variáveis do shell, e -c (compacto) é para encadear com ferramentas orientadas a linhas. Oito em cada dez bugs de jq vêm de escolher o modo de saída errado.
-r: remover aspas da string para saída raw
$ echo '{"name":"linny"}' | jq '.name'
"linny"
$ echo '{"name":"linny"}' | jq -r '.name'
linnyQuando você atribui a uma variável do shell com NAME=$(...), sempre use -r. Caso contrário, o literal "linny" (com aspas) vai para a variável.
-c: um JSON por linha
$ echo '[{"id":1},{"id":2}]' | jq -c '.[]'{"id":1}
{"id":2}
Combine -c com loops while read line ou xargs -I {}. É a ponte entre ferramentas JSON e ferramentas Unix tradicionais.
Saída TSV / CSV (@tsv / @csv)
$ echo '[{"id":1,"name":"a"},{"id":2,"name":"b"}]' \
| jq -r '.[] | [.id, .name] | @tsv'1 a 2 b
@csv coloca aspas em valores string; @tsv usa separadores de tabulação. Empacote valores em um array primeiro, depois encadeie para o formatador.
-r só remove aspas de strings. Números e objetos ainda saem como JSON. Para transformar um array em linhas, você deve explodi-lo com .[] primeiro.
Padrões Práticos para o Trabalho Diário
Formatação de respostas de API, agregação de logs e atualização de arquivos de configuração: três padrões que você vai reutilizar para sempre. Memorize os templates e adapte.
Buscar com curl e extrair campos específicos
$ curl -sS "https://api.example.com/users" \ | jq -r '.users[] | "\(.id)\t\(.name)"'
-sS significa silencioso em sucesso, barulhento em erro. A interpolação de string "\(.id)\t\(.name)" permite escolher qualquer separador.
Agregar com group_by
$ jq '[.[] | {status}] | group_by(.status) | map({status: .[0].status, count: length})'group_by(f) retorna um array de arrays agrupados por f. Envolva com map({key: ..., count: length}) para produzir uma tabela de contagem.
Reescrever parte de um arquivo de configuração
# Atualizar versao em package.json $ jq '.version = "1.2.3"' package.json > package.json.tmp \ && mv package.json.tmp package.json
jq ... file > file esvazia o arquivo. O shell abre > antes do jq ler, truncando-o. Sempre passe por um arquivo temporário, ou use sponge do moreutils.
# Alternativa mais segura com sponge $ jq '.version = "1.2.3"' package.json | sponge package.json
Adicionar a um array
$ echo '{"items":[1,2]}' | jq '.items += [3]'{
"items": [
1,
2,
3
]
}
|= é reatribuição, += é adicionar e atribuir. Combine com caminhos profundos: .items |= map(. * 2).
Quais São as Armadilhas Comuns?
Três armadilhas pegam quase todo mundo: propagação de null, erros de tipo e passar valores do shell para filtros. Ler a mensagem de erro antes de pesquisar corta o tempo de depuração pela metade.
1. Cannot iterate over null
# .users esta ausente
$ echo '{}' | jq '.users[]'
jq: error (at <stdin>:1): Cannot iterate over null (null)Solução: fornecer um padrão com // [].
$ echo '{}' | jq '.users // [] | .[]'
# (sem saida, exit 0)2. Incorporar variáveis do shell em um filtro
# Ruim: aspas colidem $ KEY="name" $ jq ".$KEY" file.json # apos expansao do shell vira '."name"' em alguns shells # Bom: passe via --arg $ jq --arg key "$KEY" '.[$key]' file.json
--arg passa o valor como uma string; --argjson passa como JSON. Use --argjson para números, arrays ou booleanos.
3. "Parece um array mas .[] falha"
$ echo '"abc"' | jq '.[]'
jq: error (at <stdin>:1): Cannot iterate over string ("abc")Uma string não é um array. Use o filtro type para confirmar o que você realmente tem.
$ echo '"abc"' | jq 'type' "string"
4. Controlar um script com o código de saída
jq -e retorna código de saída 1 quando a saída é false ou null, permitindo encadear com || para fluxo de controle.
$ echo '{"ok":false}' | jq -e '.ok' || echo "falhou"
false
falhouTemplates copiar e colar
# Formatar e paginar
jq . file.json | less
# Capturar em uma variavel (sempre use -r)
NAME=$(curl -sS "$URL" | jq -r '.name')
# Transmitir elementos de array como JSON de 1 linha para xargs
curl -sS "$URL" | jq -c '.users[]' \
| xargs -I {} sh -c 'echo "USER: {}"'
# Extracao null-safe com padrao
jq -r '.path.to.value // "DEFAULT"'