JQ - Instalação e Explicação

JQ - Instalação e Explicação

Compartilho como instalei o JQ e fiz uma explicação inicial que possibilitará você a começar a usar essa excelente ferramenta


Introdução

JSON é um formato de dados estruturado amplamente utilizado na maioria das APIs e serviços modernos para compartilhar dados. É particularmente popular em aplicativos da web devido à sua natureza leve e compatibilidade como seu próprio nome sugere, o JavaScript.

Infelizmente terminais de comando como o bash não podem interpretar e trabalhar com json diretamente. Isso significa que trabalhar com json por meio da linha de comando pode ser complicado, vejamos por exemplo uma chamada para a API do filme Star Wars:

curl https://swapi.dev/api/people

E obtemos uma resposta em json com informações sobre os personagens do filme:

image.png

Bom, podemos ver que o json retornado não é muito amigável para nossa leitura, mas não se preocupe porque há uma forma simples e fácil de resolver isso, e o nome da solução se chama jq que acabará se tornando uma das suas ferramentas favoritas!

2- Veja como o mesmo request ficaria usando o jq:

curl https://swapi.dev/api/people | jq '.'

image.png

WOW! Visualmente falando, ficou muito melhor né? 🥳

Instalação

1- Abra seu terminal e digite o comando:

sudo apt install jq

image.png

2- Verifique a instalação com o comando:

jq --version

image.png


Como usar o JQ

JQ é mais do que apenas "embelezar" o json em seu bash, ele foi construído em torno do conceito de filtros, como veremos abaixo e existem muitos filtros que podemos usar e combinar sem esforços usando o caractere "pipe" | para manipular os dados do json.

Então vamos começar pelo filtro mais simples de todos, que também será o que você acabará usando com mais frequência:

curl https://swapi.dev/api/people | jq '.'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos ao final do nosso comando | jq '.'

  • Esse '.' é um filtro que estamos dizendo ao jq "hey, pegue todos os dados e imprima em um formato json visualmente mais bonito para mim".


Avançando...

Ok, já conseguimos ver o json melhor agora, mas imagino que você deve estar se perguntando nesse momento "Certo, é só isso? Mas como que eu pego apenas o nome de um personagem por exemplo?".

Se você se questionou sobre isso, estamos no caminho certo aqui. Vamos detalhar como podemos filtrar apenas o nome do personagem Star Wars no meio de toda essa informação:

1- Se olharmos bem, um json é constituído de "chave":"valor" ou no inglês "key":"value":

image.png

2- E a key name está dentro de um array (matriz) chamado results:

image.png

3- Então para filtrarmos, poderíamos começar com:

curl https://swapi.dev/api/people | jq '.results'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos ao final do nosso comando | jq '.results'

  • Esse '.results' é um filtro que estamos dizendo ao jq "hey, pegue todos os dados do array (matriz) chamado results e imprima o json desses itens".

  • Com isso as primeiras linhas que não fazem parte de results foram digamos assim "ignoradas".


Avançando ainda mais...

4- Se continuarmos olhando com atenção, poderemos ver que esse array (matriz) results possui uma lista de itens, onde:

  • a primeira posição do array (matriz) é o index (índice) 0 e o nome é Luke Skywalker

  • a segunda posição do array (matriz) é o index (índice) 1 e o nome é o C-3PO

Então podemos limitar nosso filtro passando como index (índice) 0 e isso nos retornará o nome Luke Skywalker como previsto:

curl https://swapi.dev/api/people | jq '.results[0]'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos ao final do nosso comando | jq '.results[0]'

  • Esse '.results[0]' é um filtro que estamos dizendo ao jq "hey, pegue todos os dados do array (matriz) chamada results e imprima o index (índice) 0 (primeira posição da matriz) em um json".

  • Com isso o segundo item dessa matriz, no caso o C-3PO é digamos assim "ignorado", pois limitamos para o index (índice) 0 em nosso filtro.


Avançando mais e mais...

5- Até agora já descobrimos como chegar nesse valor, mas lembra que queríamos apenas o nome?

Bom, então só precisamos informar essa propriedade para o jq:

curl https://swapi.dev/api/people | jq '.results[0] | .name'

image.png

WOW, finalmente chegamos lá! 🥳

Mas aqui vale mais um pouco da sua atenção, vamos dividir nosso comando para entendermos melhor:

  • curl https://swapi.dev/api/people → faz um request para a API do filme Star Wars

  • | jq '.results[0] → filtra os dados do index 0 no array results

  • | .name' → imprime na saída a propriedade name do index 0 no array results

Observe que depois do filtro, adicionamos novamente um "pipe" |, esse último bloco é responsável por "imprimir" as informações para nós como definirmos, isso é a mesma coisa que stdout. Grave essa informação, pois ela será útil em todos os próximos comandos, onde vamos mudar e manipular a forma que esses dados serão apresentados para nós.


Agora ao invés de listar apenas o nome do index (índice) 0 do array (primeira posição da matriz), vamos listar todos os nomes dessa matriz, para isso é só removermos o 0 de .results[0], ficando assim:

curl https://swapi.dev/api/people | jq '.results[] | .name'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos ao final do nosso comando | jq '.results[] | .name'

  • Esse '.results[] | .name' é um filtro que estamos dizendo ao jq "hey, pegue todos os dados do array (matriz) chamada results (ele inteiro sem qualquer índice) e imprima a propriedade name".


Avançando mais, mais e mais...

6- Legal, já conseguimos listar o nome de todos, mas eles ainda estão digamos assim "jogados", "soltos", vamos consertar isso:

curl https://swapi.dev/api/people | jq '.results[] | {name: .name}'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos ao final do nosso comando | jq '.results[] | {name: .name}'

  • Esse '.results[] | {name: .name}' é um filtro que estamos dizendo ao jq "hey, pegue todos os dados do array (matriz) chamada results (ele inteiro sem qualquer índice) e imprima a key (chave) name com o value (valor) da propriedade name entre {}".


Avançando mais, mais, mais e ainda mais...

7- Estamos melhorando, mas ainda podemos ver que essas informações estão "jogadas", não há formatação json e nem vírgulas no final de cada {}, vamos consertar isso:

curl https://swapi.dev/api/people | jq '[.results[] | {name: .name}]'

image.png

Vamos entender o que aconteceu aqui:

  • A única diferença é que adicionamos colchetes [] no início e final do nosso filtro '[.results[] | {name: .name}]'

  • Então estamos dizendo ao jq aqui "hey coloque o resultado desse filtro dentro de um array (matriz)".


Avançando e avançando...

8- Até agora vimos exemplos somente com uma propriedade name, mas e se quisermos usar várias propriedades juntas? A resposta é simples, separe por vírgulas, por exemplo:

curl https://swapi.dev/api/people | jq '[.results[] | {name: .name, eye_color: .eye_color}]'

image.png

Vamos entender o que aconteceu aqui:

  • A única diferença é que separamos as propriedades name e eye_color por vírgulas → ou seja → '[.results[] | {name: .name, eyecolor: .eye_color}]'

  • Então estamos dizendo ao jq aqui "hey coloque o resultado desse filtro dentro de um array (matriz) com as keys (chaves) name e eye_color com os values (valores) das respectivas propriedades".


Último avanço...

9- Mas e se já tivermos um valor e quisermos fazer uma pesquisa? Por exemplo queremos montar um novo array (matriz) com os nomes dos personagens que possuem olhos azuis, faremos um select:

curl https://swapi.dev/api/people | jq '[.results[] | select(.eye_color=="blue") | {name: .name}]'

image.png

Vamos entender o que aconteceu aqui:

  • A única diferença é que adicionamos select(.eye_color=="blue").

  • Então estamos dizendo ao jq aqui "hey vá para a array (matriz) results, ache o value (valor) blue (azul) na propriedade eye_color de cada item e coloque o resultado desse filtro dentro de um array (matriz) com a keys (chave) name com o seu respectivo value (valor).

E é isso, espero que com essa breve introdução e exemplos, eu possa ter conseguido demonstrar como o jq funciona e como você pode manipular a saída json para suas preferências ou caso de uso.

Use JQ para Scripts

No seu dia a dia você vai se deparar várias vezes com a necessidade de obter dados de uma saída json, mas isso não se aplica somente a sistemas web, você também vai notar que precisará disso principalmente para seus scripts funcionarem corretamente, por exemplo pode ser comum você precisar obter o ID de uma instância na AWS, então para isso você faria:

aws ec2 describe-instances

image.png

Esse comando do AWS CLI faz uma chamada na API da AWS e te retornaria uma saída json com muitas informações, por exemplo:

image.png

Mas nós só queremos o ID dessa instância, é aqui que entra o jq e tudo que vimos nesse artigo, então bastaria nós retornarmos o valor da key InstanceID:

aws ec2 describe-instances | jq '.Reservations[] | .Instances[] | {Id: .InstanceId}'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos ao final do nosso comando AWS CLI | jq '.Reservations[] | .Instances[] | {Id: .InstanceId}'

  • | jq '.Reservations[] → jq está indo no array Reservations

  • | .Instances[] → jq está indo no array Instances

  • | {Id: .InstanceId}' → jq está "imprimindo" a key Id com o value InstanceId

E essa informação poderia ser muito útil para um script que agenda o ligar/desligar dessa instância por exemplo.


Outro exemplo poderia ser se você estiver usando o AWS ECS Fargate para orquestrar seus contêineres e como parte de um Pipeline automático, você gostaria de fazer o Deploy da sua nova imagem em uma nova Task, para isso seria importante você descobrir o ID dessa Task, então você faria:

aws ecs list-tasks --cluster <clusterName>

image.png

Observe que é retornado um ARN que é o nome completo de um recurso da AWS, mas nós só precisamos do ID que está localizado depois do último /, então usaríamos o jq aqui para dividir esses caracteres assim:

aws ecs list-tasks --cluster <clusterName> | jq -Mr '.taskArns[] | split("/")[-1]'

image.png

Vamos entender o que aconteceu aqui:

  • Adicionamos no final do nosso comando AWS CLI | jq -Mr '.taskArns[] | split("/")[-1]'

  • | jq -Mr '.taskArns[] → jq está indo para o array taskArns

  • | split("/")[-1]' → jq está "imprimindo" o último bloco depois da / que é o ID


Conclusão

Esses foram alguns exemplos reais e práticos de como usar o jq e de como ele pode ser útil no seu dia a dia, obviamente você poderia fazer várias combinações e filtros diferentes e para diferentes ferramentas como inspecionar containers do docker, kubernetes e muitas outras coisas.

Espero que essas informações tenham sido úteis para você!