Como analisar arquivos JSON na linha de comando do Linux com jq

Um prompt de terminal em um PC Linux.
Fatmawati Achmad Zaenuri / Shutterstock

JSON é um dos formatos mais populares para transferência de dados baseados em texto na web. Está em toda parte, e você com certeza vai encontrar. Mostraremos como lidar com isso na linha de comando do Linux usando o jqcomando.

JSON e jq

JSON significa JavaScript Object Notation . É um esquema que permite que os dados sejam codificados em arquivos de texto simples, de forma autodescritiva. Não há comentários em um arquivo JSON – o conteúdo deve ser autoexplicativo. Cada valor de dados tem uma string de texto chamada “nome” ou “chave”. Isso informa qual é o valor dos dados. Juntos, eles são conhecidos como pares nome: valor ou pares chave: valor. Dois pontos ( :) separam uma chave de seu valor.

Um “objeto” é uma coleção de pares chave: valor. Em um arquivo JSON, um objeto começa com uma chave aberta ( {) e termina com uma chave de fechamento ( }). JSON também oferece suporte a “matrizes”, que são listas ordenadas de valores. Uma matriz começa com um colchete de abertura ( [) e termina com um de fechamento ( ]).

A partir dessas definições simples, é claro, pode surgir uma complexidade arbitrária. Por exemplo, objetos podem ser aninhados em objetos. Os objetos podem conter arrays e os arrays também podem conter objetos. Todos os quais podem ter níveis abertos de aninhamento.

Na prática, porém, se o layout dos dados JSON for complicado, o design do layout dos dados provavelmente deve ser repensado. Claro, se você não está gerando os dados JSON, apenas tentando usá-los, você não tem voz no layout. Nesses casos, infelizmente, você apenas tem que lidar com isso.

A maioria das linguagens de programação tem bibliotecas ou módulos que permitem analisar dados JSON. Infelizmente, o shell Bash não tem essa funcionalidade .

A necessidade sendo a mãe da invenção, porém, jqnasceu a utilidade! Com jq, podemos  facilmente analisar JSON no shell Bash. E não importa se você tem que trabalhar com um JSON elegante e bem projetado ou com o material de que são feitos os pesadelos.

Como instalar o jq

Tivemos que instalar jq em todas as distribuições Linux que usamos para pesquisar este artigo.

Para instalar jqno Ubuntu, digite este comando:

sudo apt-get install jq

O comando "sudo apt-get install jq" em uma janela de terminal.

Para instalar jqno Fedora, digite este comando:

sudo dnf install jq

O comando "sudo dnf install jq" em uma janela de terminal.

Para instalar jqno Manjaro, digite este comando:

sudo pacman -Sy jq

O comando "sudo pacman -Sy jq" em uma janela de terminal.

Como tornar JSON legível

JSON não se preocupa com o espaço em branco e o layout não o afeta. Desde que siga as regras da gramática JSON , os sistemas que processam JSON podem lê-lo e entendê-lo. Por causa disso, JSON é frequentemente transmitido como uma string simples e longa, sem nenhuma consideração de layout. Isso economiza um pouco de espaço porque tabulações, espaços e caracteres de nova linha não precisam ser incluídos no JSON. Claro, a desvantagem de tudo isso é quando um humano tenta ler.

Vamos retirar um objeto JSON curto do  site da  NASA que nos diz a posição da Estação Espacial Internacional . Usaremos curl, que pode baixar arquivos  para recuperar o objeto JSON para nós.

Não nos importamos com nenhuma das mensagens de status  curl geralmente geradas, então digitaremos o seguinte, usando a -sopção (silenciosa):

curl -s http://api.open-notify.org/iss-now.json

O comando "curl -s http://api.open-notify.org/iss-now.json" em uma janela de terminal.

Agora, com um pouco de esforço, você pode ler isso. Você tem que escolher os valores dos dados, mas não é fácil ou conveniente. Vamos repetir isso, mas desta vez vamos canalizar jq.

jqusa filtros para analisar JSON, e o mais simples desses filtros é um ponto ( .), que significa “imprimir o objeto inteiro”. Por padrão, jq imprime bem a saída.

Recomendado:  Os arquivos NTFS compactados são descompactados para disco ou memória?

Juntamos tudo e digitamos o seguinte:

curl -s http://api.open-notify.org/iss-now.json | jq.

O "curl -s http://api.open-notify.org/iss-now.json | jq."  comando em uma janela de terminal.

Isso é muito melhor! Agora, podemos ver exatamente o que está acontecendo.

Todo o objeto é envolvido por colchetes. Ele contém duas chaves: pares de nomes: messagee timestamp. Ele também contém um objeto chamado iss_position, que contém dois pares de chave: valor:  longitudee latitude.

Vamos tentar mais uma vez. Desta vez, digitaremos o seguinte e redirecionaremos a saída para um arquivo chamado “iss.json”:

curl -s http://api.open-notify.org/iss-now.json | jq. > iss.json
cat iss.json

Os comandos "curl -s http://api.open-notify.org/iss-now.json | jq.> Iss.json" e "cat iss.json" em uma janela de terminal.

Isso nos dá uma cópia bem definida do objeto JSON em nosso disco rígido.

Acessando valores de dados

Como vimos acima,  jqpode extrair valores de dados canalizados de JSON. Ele também pode funcionar com JSON armazenado em um arquivo. Vamos trabalhar com arquivos locais para que a linha de comando não fique entulhada de curlcomandos. Isso deve tornar um pouco mais fácil de seguir.

A maneira mais simples de extrair dados de um arquivo JSON é fornecer um nome de chave para obter seu valor de dados. Digite um ponto e o nome da chave sem espaço entre eles. Isso cria um filtro a partir do nome da chave. Também precisamos informar jqqual arquivo JSON usar.

Nós digitamos o seguinte para recuperar o messagevalor:

jq .message iss.json

O comando "jq .message iss.json" em uma janela de terminal.

jqimprime o texto do message valor na janela do terminal.

Se você tiver um nome de chave que inclua espaços ou pontuação, deverá colocar o filtro entre aspas. Geralmente, toma-se cuidado ao usar caracteres, números e sublinhados apenas para que os nomes de chave JSON não sejam problemáticos.

Primeiro, digitamos o seguinte para recuperar o timestampvalor:

jq .timestamp iss.json

O comando "jq .timestamp iss.json" em uma janela de terminal.

O valor do carimbo de data / hora é recuperado e impresso na janela do terminal.

Mas como podemos acessar os valores dentro do  iss_positionobjeto? Podemos usar a notação de ponto JSON. Incluiremos o iss_positionnome do objeto no “caminho” para o valor da chave. Para fazer isso, o nome do objeto dentro da chave precederá o nome da própria chave.

Nós latitudedigitamos o seguinte, incluindo o nome da chave (observe que não há espaços entre “.iss_position” e “.latitude”):

jq .iss_position.latitude iss.json

O comando "jq .iss_position.latitude iss.json" em uma janela de terminal.

Para extrair vários valores, você deve fazer o seguinte:

  • Liste os nomes das chaves na linha de comando.
  • Separe-os com vírgulas ( ,).
  • Coloque-os entre aspas ( ") ou apóstrofos ( ').

Com isso em mente, digitamos o seguinte:

jq ".iss_position.latitude, .timestamp" iss.json

O comando "jq" .iss_position.latitude, .timestamp "iss.json" em uma janela de terminal.

Os dois valores são impressos na janela do terminal.

Trabalho com matrizes

Vamos pegar um objeto JSON diferente da NASA.

Desta vez, usaremos uma lista dos astronautas que estão no espaço agora :

curl -s http://api.open-notify.org/astros.json

O comando "curl -s http://api.open-notify.org/astros.json" em uma janela de terminal.

Ok, funcionou, então vamos fazer de novo.

Vamos digitar o seguinte para jqencaminhá-lo e redirecioná-lo para um arquivo chamado “astro.json”:

curl -s http://api.open-notify.org/astros.json | jq. > astro.json

O comando "curl -s http://api.open-notify.org/astros.json | jq.> Astros.json" em uma janela de terminal.

Agora vamos digitar o seguinte para verificar nosso arquivo:

menos astro.json

O comando "less astros.json" em uma janela de terminal.

Conforme mostrado abaixo, agora vemos a lista de astronautas no espaço, bem como suas naves espaciais.

Saída de "less astros.json" em uma janela de terminal.

Este objeto JSON contém uma matriz chamada people. Sabemos que é uma matriz por causa do colchete de abertura ( [) (destacado na imagem acima). É uma matriz de objetos em que cada um contém duas chaves: pares de valores:   namee craft.

Como fizemos anteriormente, podemos usar a notação de ponto JSON para acessar os valores. Devemos também incluir os colchetes ( []) no nome do array.

Com tudo isso em mente, digitamos o seguinte:

jq ".people []. name" astro.json

O comando "jq" .people []. Name "astros.json" em uma janela de terminal.

Desta vez, todos os valores de nome são impressos na janela do terminal. O que pedimos jqpara fazer foi imprimir o valor do nome para cada objeto no array. Muito legal, hein?

Podemos recuperar o nome de um único objeto se colocarmos sua posição no array entre colchetes ( []) na linha de comando. A matriz usa indexação de deslocamento zero , o que significa que o objeto na primeira posição da matriz é zero.

Recomendado:  Como proteger sua privacidade no Facebook

Para acessar o último objeto na matriz, você pode usar -1; para obter o penúltimo objeto na matriz, você pode usar -2 e assim por diante.

Às vezes, o objeto JSON fornece o número de elementos no array, que é o caso deste. Junto com a matriz, ele contém um par chave: nome chamado numbercom um valor de seis.

O seguinte número de objetos está nesta matriz:

jq ".people [1] .name" astro.json
jq ".people [3] .name" astro.json
jq ".people [-1] .name" astro.json
jq ".people [-2] .name" astro.json

O "jq" .people [1] .name "astro.json," "jq" .people [3] .name "astro.json," "jq" .people [-1] .name "astro.json," e "jq" .people [-2] .name "astro.json" em uma janela de terminal.

Você também pode fornecer um objeto inicial e final dentro da matriz. Isso é chamado de “fatiar” e pode ser um pouco confuso. Lembre-se de que a matriz usa um deslocamento zero.

Para recuperar os objetos da posição de índice dois, até (mas não incluindo) o objeto na posição de índice quatro, digitamos o seguinte comando:

jq ".people [2: 4]" astro.json

O comando "jq" .people [2: 4] "astro.json" em uma janela de terminal.

Isso imprime os objetos no índice de array dois (o terceiro objeto no array) e três (o quarto objeto no array). Ele interrompe o processamento no índice de matriz quatro, que é o quinto objeto da matriz.

A maneira de entender melhor isso é experimentar na linha de comando. Você logo verá como funciona.

Como usar tubos com filtros

Você pode canalizar a saída de um filtro para outro e não precisa aprender um novo símbolo. O mesmo que a linha de comando do Linux,  jqusa a barra vertical ( |) para representar um tubo.

Diremos  jqpara canalizar o peoplearray para o .namefiltro, que deve listar os nomes dos astronautas na janela do terminal.

Nós digitamos o seguinte:

jq ".people [] | .name" astro.json

O "jq" .people [] |  .name "astros.json" comando em uma janela de terminal.

Criação de matrizes e modificação de resultados

Podemos usar jqpara criar novos objetos, como arrays. Neste exemplo, vamos extrair três valores e criar uma nova matriz que contém esses valores. Observe que os [colchetes de abertura ( ) e de fechamento ( ]) também são o primeiro e o último caracteres na string de filtro.

Nós digitamos o seguinte:

jq "[.iss-position.latitude, iss_position.longitude, .timestamp]" iss.json

O comando "jq" [.iss-position.latitude, iss_position.longitude, .timestamp] "iss.json" em uma janela de terminal.

A saída é colocada entre colchetes e separada por vírgulas, tornando-a uma matriz formada corretamente.

Os valores numéricos também podem ser manipulados à medida que são recuperados. Vamos extrair do timestamparquivo de posição ISS e extraí-lo novamente e alterar o valor que é retornado.

Para fazer isso, digitamos o seguinte:

jq ".timestamp" iss.json
jq ".timestamp - 1570000000" iss.json

Os comandos "jq" .timestamp "iss.json" e "jq" .timestamp - 1570000000 "iss.json" em uma janela de terminal.

Isso é útil se você precisar adicionar ou remover um deslocamento padrão de uma matriz de valores.

Vamos digitar o seguinte para nos lembrar do que o iss.jsonarquivo contém:

jq. iss.json

O comando "jq. Iss.json" em uma janela de terminal.

Digamos que queremos nos livrar do messagepar chave: valor. Não tem nada a ver com a posição da Estação Espacial Internacional. É apenas uma bandeira que indica que o local foi recuperado com sucesso. Se for excedente aos requisitos, podemos dispensá-lo. (Você também pode simplesmente ignorá-lo.)

Podemos usar jqa função delete de,,  del()para deletar um par chave: valor. Para excluir o par chave: valor da mensagem, digitamos este comando:

jq "del (.message)" iss.json

O comando "jq" del (.message) "iss.json" em uma janela de terminal.

Observe que isso não o exclui realmente do arquivo “iss.json”; apenas o remove da saída do comando. Se você precisar criar um novo arquivo sem o messagepar chave: valor nele, execute o comando e redirecione a saída para um novo arquivo.

Objetos JSON mais complicados

Vamos recuperar mais alguns dados da NASA. Desta vez, usaremos um objeto JSON que contém informações sobre locais de impacto de meteoros de todo o mundo. Este é um arquivo maior com uma estrutura JSON muito mais complicada do que aqueles com os quais lidamos anteriormente.

Primeiro, digitaremos o seguinte para redirecioná-lo para um arquivo chamado “strikes.json”:

curl -s https://data.nasa.gov/resource/y77d-th95.json | jq. > strikes.json

O comando "curl -s https://data.nasa.gov/resource/y77d-th95.json | jq.> Strikes.json" em uma janela de terminal.

Para ver a aparência do JSON, digitamos o seguinte:

menos strikes.json

O comando "less strikes.json" em less em uma janela de terminal.

Conforme mostrado abaixo, o arquivo começa com um colchete de abertura ( [), então o objeto inteiro é um array. Os objetos na matriz são coleções de pares chave: valor e há um objeto aninhado chamado geolocation. O geolocationobjeto contém mais pares chave: valor e uma matriz chamada coordinates.

Saída do comando "less strikes.json" em less em uma janela de terminal.

Vamos recuperar os nomes das colisões de meteoros do objeto na posição de índice 995 até o final da matriz.

Recomendado:  Como ativar a reprodução sem perdas no Apple Music

Vamos digitar o seguinte para canalizar o JSON por meio de três filtros:

jq ". [995:] |. [] | .name" strikes.json

O "jq". [995:] |  . [] |  .name "strikes.json" comando em uma janela de terminal.

Os filtros funcionam das seguintes maneiras:

  • .[995:]: Isso diz jqpara processar os objetos do índice de matriz 995 até o final da matriz. Nenhum número após os dois pontos ( :) é o que indica  jqpara continuar até o final da matriz.
  • .[]: Este iterador de array diz jqpara processar cada objeto no array.
  • .name: Este filtro extrai o valor do nome.

Com uma pequena mudança, podemos extrair os últimos 10 objetos do array. Um “-10” instrui jq para iniciar o processamento de objetos 10 de volta a partir do final da matriz.

Nós digitamos o seguinte:

jq ". [- 10:] |. [] | .name" strikes.json

O "jq". [- 10:] |  . [] |  .name "strikes.json" comando em uma janela de terminal.

Assim como nos exemplos anteriores, podemos digitar o seguinte para selecionar um único objeto:

jq ". [650] .name" strikes.json

O comando "jq". [650] .name "strikes.json" em uma janela de terminal.

Também podemos aplicar o fatiamento às cordas. Para fazer isso, digitaremos o seguinte para solicitar os primeiros quatro caracteres do nome do objeto no índice de matriz 234:

jq ". [234] .name [0: 4]" strikes.json

O comando "jq". [234] .name [0: 4] "strikes.json" em uma janela de terminal.

Também podemos ver um objeto específico em sua totalidade. Para fazer isso, digitamos o seguinte e incluímos um índice de matriz sem nenhuma chave: filtros de valor:

jq ". [234]" strikes.json

O comando "jq". [234] "strikes.json" em uma janela de terminal.

Se quiser ver apenas os valores, você pode fazer a mesma coisa sem os nomes das chaves.

Para nosso exemplo, digitamos este comando:

jq ". [234] []" strikes.json

O comando "jq" [234] [] "strikes.json" em uma janela de terminal.

Para recuperar vários valores de cada objeto, nós os separamos com vírgulas no seguinte comando:

jq ". [450: 455] |. [] | .name, .mass" strikes.json

O "jq". [450: 455] |  . [] |  .name, .mass "strikes.json" comando em uma janela de terminal.

Se você deseja recuperar valores aninhados, você deve identificar os objetos que formam o “caminho” para eles.

Por exemplo, para fazer referência aos coordinatesvalores, temos que incluir a matriz abrangente, o geolocationobjeto aninhado e a coordinatesmatriz aninhada , conforme mostrado abaixo.

O caminho para a matriz em um objeto JSON aninhado destacado em uma janela de terminal.

Para ver os coordinatesvalores do objeto na posição de índice 121 da matriz, digitamos o seguinte comando:

jq ". [121] .geolocation.coordinates []" strikes.json

O comando "jq". [121] .geolocation.coordinates [] "strikes.json" em uma janela de terminal.

A função comprimento

A jq lengthfunção fornece métricas diferentes de acordo com o que foi aplicado, como:

  • Strings : o comprimento da string em bytes.
  • Objetos : o número de pares chave: valor no objeto.
  • Matrizes : o número de elementos da matriz na matriz.

O comando a seguir retorna o comprimento do namevalor em 10 dos objetos na matriz JSON, começando na posição de índice 100:

jq ". [100: 110] |. []. nome | comprimento" strikes.json

O "jq". [100: 110] |  . []. nome |  comprimento do comando "strikes.json" em uma janela de terminal.

Para ver quantos pares chave: valor estão no primeiro objeto da matriz, digitamos este comando:

jq ". [0] | comprimento" strikes.json

O "jq". [0]  |  comprimento do comando "strikes.json" em uma janela de terminal.

As teclas de função

Você pode usar a função keys para descobrir mais sobre o JSON com o qual precisa trabalhar. Ele pode dizer quais são os nomes das chaves e quantos objetos existem em uma matriz.

Para encontrar as chaves do peopleobjeto no arquivo “astro.json”, digitamos este comando:

jq ".people. [0] | keys" astro.json

O "jq" .people. [0]  |  keys "astro.json" comando em uma janela de terminal.

Para ver quantos elementos estão na peoplematriz, digitamos este comando:

jq ".people | keys" astro.json

O "jq" .people |  keys "astro.json" comando em uma janela de terminal.

Isso mostra que há seis elementos da matriz de deslocamento zero, numerados de zero a cinco.

A função has ()

Você pode usar a has()função para interrogar o JSON e ver se um objeto tem um nome de chave específico. Observe que o nome da chave deve ser colocado entre aspas. Vamos envolver o comando de filtro entre aspas simples ( '), da seguinte maneira:

jq '. [] | has ("nametype") 'strikes.json

O "jq '. [] | Has (" nametype ")' strikes.json" em uma janela de terminal.

Cada objeto na matriz é verificado, conforme mostrado abaixo.

Saída do comando "jq '. [] | Has (" nametype ")' strikes.json" em uma janela de terminal.

Se você quiser verificar um objeto específico, inclua sua posição de índice no filtro de matriz, da seguinte maneira:

jq '. [678] | has ("nametype") 'strikes.json

O comando "jq '. [678] | has (" nametype ")' strikes.json" em uma janela de terminal.

Não chegue perto do JSON sem ele

O jqutilitário é o exemplo perfeito de software profissional, poderoso e rápido que torna a vida no mundo Linux um prazer.

Esta foi apenas uma breve introdução às funções comuns deste comando – há muito mais do que isso. Certifique-se de verificar o manual jq abrangente  se quiser se aprofundar.