Trilhas
/
jq
jq
/
Programa
/
Noções básicas
No

Noções básicas em jq

1 exercício

Sobre Noções básicas

O jq funciona passando os dados JSON de entrada por uma única expressão (escrita como um pipeline de filtros) para chegar aos dados transformados que você quer.

Uma introdução rápida às opções de linha de comando do jq

A linguagem jq é implementada pelo programa jq. Esse programa oferece várias opções de linha de comando práticas para controlar como a entrada é consumida e como a saída é apresentada.

Nos exemplos abaixo, você vai encontrar:

  • -n ou --null-input

    Normalmente, o programa jq recebe um arquivo para ler, ou você envia dados para a entrada dele. A opção --null-input permite gerar dados JSON sem nenhuma entrada.

  • -c ou --compact-output

    Por padrão, o jq formata a saída de um jeito legível. Isso é extremamente útil para humanos verem os dados quando estão bem formatados. Mas isso não é necessário para as máquinas: a opção --compact-output remove os espaços em branco da formatação para minimizar o tamanho do JSON resultante.

  • -f filename ou --from-file filename

    Lê o programa jq a partir de filename em vez de fornecê-lo na linha de comando. O sed e o awk usam a opção -f para o mesmo propósito. Você vai ver isso nos scripts de teste dos exercícios de prática.

Veja o manual para detalhes sobre todas as opções.

Filtros e pipes

Filtros também são conhecidos como expressões.

Um filtro recebe uma entrada e produz uma saída. Assim como você faz em um shell unix, pode unir filtros com um pipe | para conectar a saída de um filtro à entrada de outro.

O filtro de identidade: .

Este é o filtro mais simples. Ele simplesmente passa a entrada para a saída. Por exemplo, o jq formata a saída de um jeito legível por padrão, então passar JSON pelo filtro . dá uma saída bem formatada de graça!

$ echo '[1, 2, 3]' | jq '.'
[
  1,
  2,
  3
]

Arrays

Esta é uma introdução rápida ao trabalho com arrays. Vamos ver esse assunto em mais detalhes depois.

Os elementos de um array são acessados com colchetes e são indexados a partir de zero.

$ echo '[10, 20, 30]' | jq '.[1]'
20

Um filtro pode construir um array envolvendo uma expressão em [ e ]

  • com uma lista conhecida de elementos:

    jq -n '[1, 2, 3]'
    
  • para coletar um fluxo de elementos: por exemplo, range é uma função que produz um fluxo de números

    $ jq -n 'range(10; 70; 15)'
    10
    25
    40
    55
    

    Usar [] coleta os resultados da expressão em um array

    $ jq -c -n '[range(10; 70; 15)]'
    [10,25,40,55]
    

A vírgula é um operador

A vírgula não é só uma sintaxe que separa elementos de um array. A vírgula é um operador que junta fluxos.

Por exemplo, [1, 2, 3] é um filtro que usa o construtor de array [] para coletar o resultado de juntar as três expressões 1, 2 e 3.

Você reparou nos pontos e vírgulas em range(10; 70; 15) acima? Como as vírgulas têm um propósito específico na linguagem jq, funções que recebem vários argumentos usam pontos e vírgulas para separar os argumentos.

Objetos

Uma introdução rápida aos objetos.

Como em muitas linguagens de programação, use pontos para acessar as propriedades de um objeto

$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"

Colchetes também podem ser usados com objetos, mas aí as aspas são necessárias para strings literais. Esse é um jeito de trabalhar com chaves que contêm espaços.

$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"

Você pode construir um objeto com {} e pares key: value. As aspas não são obrigatórias em volta de chaves que são strings "simples".

jq -n '{question: (6 * 9), answer: 42}'

produz

{
  "question": 54,
  "answer": 42
}

Para tratar a chave como uma expressão, você precisa envolvê-la em parênteses (o código a seguir também produz a mesma saída que o anterior).

echo '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
Note

É bem comum querer extrair um subconjunto de chaves de um objeto grande. Por exemplo, para extrair id e name de

{
    "id": 101,
    "name": "alpha widget",
    "specifications": {...}
}

Poderíamos escrever

{id: .id, name: .name}

Mas isso é tão comum que existe uma sintaxe abreviada para isso:

{id, name}

Pipelines

Por exemplo, dado um file.json que contém

{
  "key1": "value1",
  "key2": [5, 15, 25]
}

Vamos calcular o comprimento do array key2:

$ jq '.key2 | length' file.json
3

Estamos passando a saída da expressão .key2 por um pipe para a entrada de length, que, sem surpresa, produz o número de elementos do array.

Caution

Este é um aspecto do jq que leva um tempo para se acostumar: a maioria (mas não todas) das funções age como filtros, em que você passa os dados para a entrada do filtro, e não como argumento.

Filtros podem ignorar a entrada

Neste exemplo, os dados JSON de entrada são ignorados e não têm impacto na saída:

$ echo '{"answer": 42}' | jq '6 * 9'
54

Filtros podem produzir fluxos de dados

Um filtro pode produzir mais de um valor. Por exemplo, o filtro .[] produz cada elemento de um array como um valor separado:

$ jq -n -c '[1, 2, 3]'
[1,2,3]

$ jq -n -c '[1, 2, 3] | .[]'
1
2
3

Passar esse filtro por um pipe para outro vai executar o segundo filtro para cada valor:

$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6

Isso é como uma iteração implícita. Quando você entender essa técnica, vai perceber que filtros jq muito poderosos podem ser bem concisos.

Parênteses

Parênteses são usados para agrupar subexpressões e garantir a ordem das operações, como em outras linguagens. No jq, a necessidade deles pode parecer um tanto surpreendente.

Por exemplo, digamos que queremos construir um array com 2 elementos: a raiz quadrada de 9; e e elevado à potência 1. As duas expressões individuais são 9 | sqrt e 1 | exp. Esperamos que a saída seja o array [3, 2.7...]

$ jq -n '[ 9|sqrt, 1|exp ]'
[
  20.085536923187668,
  2.718281828459045
]

Por que não obtivemos o que esperávamos? O jq interpreta isso assim:

[ ((9|sqrt), 1) | exp ]

O jq monta um fluxo de dois elementos (3 e 1), e cada um deles é passado para exp.

Precisamos garantir que exp receba apenas um número como entrada. Em outras palavras, precisamos garantir que o pipe seja avaliado antes da vírgula.

$ jq -n '[ 9|sqrt, (1|exp) ]'
[
  3,
  2.718281828459045
]

Tipos

Do manual

o jq oferece suporte ao mesmo conjunto de tipos de dados do JSON: números, strings, booleans, arrays, objetos (que, no jargão do JSON, são hashes com apenas chaves de string) e "null".

Você vai aprender mais sobre eles nos próximos exercícios.

Espaços em branco

Espaços em branco não são significativos no jq. Use espaços, tabulações e quebras de linha como preferir para formatar seu código. Não conhecemos nenhum guia de estilo de jq existente.

Valores imutáveis

Os valores no jq são imutáveis. Filtros que modificam um valor produzem um novo valor. Isso implica que o jq não tem variáveis globais: você vai precisar se acostumar a passar o estado de um filtro para outro.

"Veracidade"

Os valores false e null são considerados falsos. Qualquer outro valor (incluindo o número zero e a string/array/objeto vazios) é verdadeiro.

Funções e operadores

Sem entrar em muitos detalhes (funções serão tema de outro exercício), aqui estão algumas funções internas úteis:

  • length

    Dado um array como entrada, produz o número de elementos do array.

    $ jq -n '[10, 20, 30, 40] | length'
    4
    
  • +

    Esse operador faz coisas diferentes dependendo do tipo dos seus operandos: soma números, concatena strings, acrescenta arrays, mescla objetos.

    $ jq -c -n '
        3 + 4,
        "foo" + "bar",
        ["a", "b"] + ["c"],
        {"m": 10} + {"n": 20}
    '
    7
    "foobar"
    ["a","b","c"]
    {"m":10,"n":20}
    

    A add é uma função que recebe um array e retorna um item com todos os elementos somados de acordo com as regras do +. [1, 2, 3] | add produz 6.

  • map

    Dado um array como entrada e um filtro como argumento, produz um array em que o filtro é aplicado a cada elemento

    $ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
    [2,4,6,8]
    
  • select

    Dada alguma entrada e um filtro como argumento:

    • se o filtro aplicado ao argumento resultar em um valor verdadeiro, produz a entrada sem alterações
    • caso contrário, não produz nada (não o valor null, realmente nenhuma saída)

    Por exemplo, dados alguns números, selecione os que são divisíveis por 3

    $ jq -n 'range(10) | select(. % 3 == 0)'
    0
    3
    6
    9
    

    Lembre-se de que range produz um fluxo de números. select será chamado uma vez para cada número. Só os números que "passam" pela expressão são produzidos.

    Muitas vezes você precisa selecionar elementos de um array. Existem algumas formas de fazer isso.

    Com a entrada ["Anne", "Bob", "Cathy", "Dave"], selecione os nomes que têm comprimento 4.

    • use map e select juntos

      map(select(length == 4))
      
    • separe o array em elementos, use select nesse fluxo e colete os resultados

      [ .[] | select(length == 4) ]
      

Comentários

Comentários começam com o caractere # e vão até o fim da linha.

Editar via GitHub O link abre em uma nova janela ou aba

Aprenda Noções básicas