Estou tentando analisar JSON retornado de uma solicitação de curl, assim:
curl 'http://twitter.com/users/username.json' |
sed -e 's/[{}]/''/g' |
awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'
O exemplo acima divide o JSON em campos, por exemplo:
% ...
"geo_enabled":false
"friends_count":245
"profile_text_color":"000000"
"status":"in_reply_to_screen_name":null
"source":"web"
"truncated":false
"text":"My status"
"favorited":false
% ...
Como imprimo um campo específico (indicado por -v k=text
)?
grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json
. Isso resolve a tarefa facilmente e somente com grep e funciona perfeitamente para JSONs simples. Para JSONs complexos, você deve usar um analisador adequado.Respostas:
Existem várias ferramentas projetadas especificamente com o objetivo de manipular JSON a partir da linha de comando e serão muito mais fáceis e confiáveis do que com o Awk, como
jq
:Você também pode fazer isso com ferramentas que provavelmente já estão instaladas em seu sistema, como Python usando o
json
módulo , para evitar dependências extras, enquanto ainda beneficia de um analisador JSON adequado. O seguinte pressupõe que você deseja usar o UTF-8, no qual o JSON original deve ser codificado e é o que a maioria dos terminais modernos também usa:Python 3:
Python 2:
Notas históricas
Essa resposta originalmente recomendava o jsawk , que ainda deve funcionar, mas é um pouco mais complicado de usar do que
jq
e depende da instalação de um interpretador JavaScript independente, que é menos comum do que um interpretador Python; portanto, as respostas acima provavelmente são preferíveis:Essa resposta também originalmente usou a API do Twitter a partir da pergunta, mas essa API não funciona mais, dificultando a cópia dos exemplos a serem testados e a nova API do Twitter requer chaves da API. Por isso, mudei para o uso da API do GitHub que pode ser usado facilmente sem chaves de API. A primeira resposta para a pergunta original seria:
fonte
print
instrução sempre codificará para ASCII porque você está usando o Python em um pipe. InsiraPYTHONIOENCODING=<desired codec>
no comando para definir uma codificação de saída diferente, adequada para o seu terminal. No Python 3, o padrão é UTF-8 nesse caso (usando aprint()
função ).curl -s
é equivalente acurl --silent
, enquantojq -r
significa,jq --raw-output
ie, sem aspas.Para extrair rapidamente os valores de uma chave específica, eu pessoalmente gosto de usar "grep -o", que retorna apenas a correspondência da regex. Por exemplo, para obter o campo "texto" dos tweets, algo como:
Esse regex é mais robusto do que você imagina; por exemplo, lida bem com seqüências de caracteres com vírgulas incorporadas e aspas de escape dentro delas. Eu acho que com um pouco mais de trabalho você poderia fazer um que garantisse extrair o valor, se for atômico. (Se houver aninhamento, uma regex não poderá fazê-lo, é claro.)
E para ainda mais limpo (embora mantendo escape original do string) você pode usar algo como:
| perl -pe 's/"text"://; s/^"//; s/",$//'
. (Fiz isso para esta análise .)Para todos os odiadores que insistem em usar um analisador JSON real - sim, isso é essencial para a correção, mas
grep -o
é uma ordem de magnitude mais rápida que ajson
biblioteca padrão do Python , pelo menos ao fazer isso para tweets (com aproximadamente 2 KB cada). Não tenho certeza se isso é apenas porquejson
é lento (eu devo comparar com o yajl em algum momento); mas, em princípio, um regex deve ser mais rápido, pois é um estado finito e muito mais otimizável, em vez de um analisador que precisa oferecer suporte à recursão e, nesse caso, gasta muitas árvores de construção de CPU para estruturas com as quais você não se importa. (Se alguém escrevesse um transdutor de estado finito que fizesse uma análise JSON adequada (com profundidade limitada), isso seria fantástico! Enquanto isso, temos "grep -o".)Para escrever código de manutenção, eu sempre uso uma biblioteca de análise real. Eu não tentei o jsawk , mas se funcionar bem, isso abordaria o ponto 1.
Uma última solução, mais estranha: escrevi um script que usa Python
json
e extrai as chaves que você deseja, em colunas separadas por tabulação; então eu atravesso um invólucroawk
que permite o acesso nomeado às colunas. Aqui: os scripts json2tsv e tsvawk . Portanto, para este exemplo, seria:Essa abordagem não aborda o número 2, é mais ineficiente do que um único script Python e é um pouco frágil: força a normalização de novas linhas e guias nos valores das strings, para que seja agradável com a visão de mundo / campo delimitada por registros do awk. Mas permite que você permaneça na linha de comando, com mais correção do que
grep -o
.fonte
grep -Po '"text":(\d*?,|.*?[^\\]",)'
jq .name
funciona na linha de comando e não requer "abrir um editor para escrever um script". 2. Não importa o quão rápido o seu regex pode produzir erradas resultados| grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
-P
está faltando a opção. Eu testei no OSX 10.11.5 egrep --version
foigrep (BSD grep) 2.5.1-FreeBSD
. Eu consegui trabalhar com a opção "regex estendida" no OSX. O comando de cima seriagrep -Eo '"text":.*?[^\\]",' tweets.json
.Considerando que algumas das recomendações aqui (especialmente nos comentários) sugeriram o uso do Python, fiquei desapontado por não encontrar um exemplo.
Então, aqui está uma lista para obter um valor único de alguns dados JSON. Ele pressupõe que você está canalizando os dados (de algum lugar) e, portanto, deve ser útil em um contexto de script.
fonte
pythonpy
( github.com/russell91/pythonpy é quase sempre uma alternativa melhorpython -c
, embora precise ser instalado com o pip. basta canalizar o json parapy --ji -x 'x[0]["hostname"]'
. Se você não quiser usar o suporte json_input embutido, ainda poderá obter aqueles importação automaticamente comopy 'json.loads(sys.stdin)[0]["hostname"]'
jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }
para que eu pudesse escrever:curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'
& mais coisas assustadoras similares ... Aliás,obj[0]
parece desnecessário, parece queobj
funciona apenas nos casos padrão (?).jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
obj[0]
causa um erro ao analisar{ "port":5555 }
. Funciona bem após a remoção[0]
.Seguindo a liderança de MartinR e Boecko:
Isso lhe dará uma saída extremamente amigável para grep. Muito conveniente:
fonte
| grep field
. Obrigado!jq
normalmente não é instalado enquanto o python estiver. Além disso, uma vez que seu em Python que você pode também ir toda a maneira e analisá-lo comimport json...
Você pode simplesmente baixar o
jq
binário para sua plataforma e executar (chmod +x jq
):Extrai o
"name"
atributo do objeto json.jq
A página inicial diz que ésed
para dados JSON.fonte
jq
é uma ferramenta incrível.curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
Usando o Node.js
Se o sistema tiver nóinstalado, é possível usar os sinalizadores de script de
-p
impressão e avaliação para extrair qualquer valor necessário.-e
JSON.parse
Um exemplo simples usando a string JSON
{ "foo": "bar" }
e retirando o valor de "foo":Como temos acesso
cat
e outros utilitários, podemos usá-lo para arquivos:Ou qualquer outro formato, como um URL que contém JSON:
fonte
node -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
Use o suporte JSON do Python em vez de usar o awk!
Algo assim:
fonte
json.load(sys.stdin)['"key']"
como exemplo, como:curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
.Você perguntou como se dar um tiro no pé e eu estou aqui para fornecer a munição:
Você poderia usar em
tr -d '{}'
vez desed
. Mas deixá-los de fora completamente parece ter o efeito desejado também.Se você deseja retirar as aspas externas, canalize o resultado acima
sed 's/\(^"\|"$\)//g'
Eu acho que outros soaram alarme suficiente. Estarei esperando com um telefone celular para chamar uma ambulância. Dispare quando pronto.
fonte
Usando o Bash com Python
Crie uma função bash no seu arquivo .bash_rc
Então
Aqui está a mesma função, mas com verificação de erros.
Onde $ # -ne 1 garante pelo menos 1 entrada e -t 0 garante que você está redirecionando de um canal.
O bom dessa implementação é que você pode acessar valores json aninhados e obter json em troca! =)
Exemplo:
Se você quiser ser realmente chique, pode imprimir os dados:
fonte
curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
sys.stdout.write()
se você quer que ele funcione tanto com python 2 e 3.getJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
TickTick é um analisador JSON escrito em bash (<250 linhas de código)
Aqui está o trecho do autor de seu artigo, Imagine um mundo onde o Bash suporta JSON :
fonte
Analisando JSON com PHP CLI
Indiscutivelmente fora de tópico, mas como a precedência reina, essa pergunta permanece incompleta sem uma menção ao nosso fiel e fiel PHP, estou certo?
Usando o mesmo exemplo JSON, mas vamos atribuí-lo a uma variável para reduzir a obscuridade.
Agora, para o PHP, usando file_get_contents e o php: // stdin stream wrapper.
ou conforme indicado usando fgets e o fluxo já aberto na constante STDIN da CLI .
nJoy!
fonte
$argn
vez defgets(STDIN)
$argn
trabalha com o -E ou opção -R e somente se o conteúdo JSON está em uma linha ...Versão nativa do Bash: também funciona bem com barras invertidas (\) e aspas (")
fonte
Versão que usa Ruby e http://flori.github.com/json/
ou mais concisamente:
fonte
;
não é necessária no Ruby (é usada apenas para concatenar instruções que normalmente estariam em linhas separadas em uma única linha).Infelizmente, a resposta mais votada que usa
grep
retorna a correspondência completa que não funcionou no meu cenário, mas se você souber que o formato JSON permanecerá constante, poderá usar lookbehind e lookahead para extrair apenas os valores desejados.fonte
Se alguém apenas deseja extrair valores de objetos JSON simples sem a necessidade de estruturas aninhadas, é possível usar expressões regulares sem sair do bash.
Aqui está uma função que eu defini usando expressões regulares do bash com base no padrão JSON :
Advertências: objetos e matrizes não são suportados como valor, mas todos os outros tipos de valores definidos no padrão são suportados. Além disso, um par será correspondido, independentemente da profundidade do documento JSON, desde que tenha exatamente o mesmo nome da chave.
Usando o exemplo do OP:
fonte
Existe uma maneira mais fácil de obter uma propriedade de uma string json. Usando um
package.json
arquivo como exemplo, tente o seguinte:Estamos usando
process.env
porque isso coloca o conteúdo do arquivo no node.js como uma string, sem risco de conteúdo malicioso escapar das aspas e ser analisado como código.fonte
require()
pode realmente executar código estrangeiro, JSON.parse não.JSON.parse()
e sim, você é inequivocamente seguro ... mas aqui, o tempo de execução JSON está recebendo o conteúdo (não confiável) em banda com o código (confiável).JSON.parse()
, também estará seguro, mas isso também não está acontecendo aqui.JSON.parse()
, no código . Você está assumindo que colocar backticks literais manterá o conteúdo literal, mas isso é uma suposição completamente insegura, porque backticks literais podem existir no conteúdo do arquivo (e, portanto, na variável) e, portanto, podem terminar a citação e entrar em um contexto não citado, onde valores são executados como código.Agora que o Powershell é multiplataforma, pensei em abrir caminho, pois acho que é bastante intuitivo e extremamente simples.
ConvertFrom-Json converte o JSON em um objeto personalizado do Powershell, para que você possa trabalhar facilmente com as propriedades desse ponto em diante. Se você quiser apenas a propriedade 'id', por exemplo, basta fazer o seguinte:
Se você quiser invocar a coisa toda a partir do Bash, precisará chamar assim:
É claro que existe uma maneira pura do Powershell de fazer isso sem enrolar, o que seria:
Por fim, há também 'ConvertTo-Json', que converte um objeto personalizado em JSON com a mesma facilidade. Aqui está um exemplo:
O que produziria JSON legal como este:
}
É certo que usar um shell do Windows no Unix é um pouco sacrílego, mas o Powershell é realmente bom em algumas coisas, e a análise de JSON e XML são algumas delas. Esta é a página do GitHub para a versão multiplataforma https://github.com/PowerShell/PowerShell
fonte
Alguém que também tenha arquivos xml, pode querer dar uma olhada no meu Xidel . É um processador JSONiq livre de dependência e cli . (ou seja, ele também suporta XQuery para processamento xml ou json)
O exemplo na pergunta seria:
Ou com minha própria sintaxe de extensão não padrão:
fonte
xidel -s https://api.github.com/users/lambda -e 'name'
(ou-e '$json/name'
, ou-e '($json).name'
).Não posso usar nenhuma das respostas aqui. Sem jq disponível, sem matrizes de shell, sem declarar, sem grep -P, sem lookbehind e lookahead, sem Python, sem Perl, sem Ruby, não - nem mesmo Bash ... As respostas restantes simplesmente não funcionam bem. O JavaScript parecia familiar, mas a lata diz Nescaffe - então também não é possível :) Mesmo se disponíveis, para minha simples necessidade - eles seriam um exagero e lentos.
No entanto, é extremamente importante para mim obter muitas variáveis da resposta formatada em json do meu modem. Estou fazendo isso em um sh com o BusyBox muito aparado nos meus roteadores! Não há problemas ao usar o awk sozinho: basta definir delimitadores e ler os dados. Para uma única variável, isso é tudo!
Lembra que não tenho matrizes? Eu tive que atribuir dentro dos dados analisados do awk às 11 variáveis necessárias em um script de shell. Onde quer que eu olhasse, isso era uma missão impossível. Não tem problema com isso também.
Minha solução é simples. Este código irá: 1) analisar o arquivo .json da pergunta (na verdade, peguei emprestada uma amostra de dados de trabalho da resposta mais votada) e seleciono os dados citados, além de 2) criar variáveis de shell a partir do awk, designando shell nomeado gratuito nomes de variáveis.
Sem problemas com espaços em branco no interior. No meu uso, o mesmo comando analisa uma saída longa de linha única. Como o eval é usado, esta solução é adequada apenas para dados confiáveis. É simples adaptá-lo para coletar dados não citados. Para um grande número de variáveis, o ganho marginal de velocidade pode ser alcançado usando else if. A falta de matriz obviamente significa: não há vários registros sem mexer extra. Mas onde as matrizes estão disponíveis, adaptar esta solução é uma tarefa simples.
@maikel sed resposta quase funciona (mas não posso comentar). Para meus dados bem formatados - funciona. Não muito com o exemplo usado aqui (aspas ausentes o impedem). É complicado e difícil de modificar. Além disso, não gosto de fazer 11 chamadas para extrair 11 variáveis. Por quê? Cronometrei 100 loops para extrair 9 variáveis: a função sed levou 48,99 segundos e minha solução levou 0,91 segundos! Não é justo? Fazendo apenas uma extração única de 9 variáveis: 0,51 vs. 0,02 seg.
fonte
Você pode tentar algo assim -
fonte
Você pode usar
jshon
:fonte
aqui está uma maneira de fazer isso com o awk
fonte
Para uma análise JSON mais complexa, sugiro usar o módulo python jsonpath (de Stefan Goessner) -
sudo easy_install -U jsonpath
Exemplo file.json (de http://goessner.net/articles/JsonPath ) -
Analise-o (extraia todos os títulos dos livros com preço <10) -
Saída -
NOTA: A linha de comando acima não inclui a verificação de erros. Para obter uma solução completa com verificação de erro, você deve criar um pequeno script python e agrupar o código com try-except.
fonte
jsonpath
para instalar e instalarjsonpath_rw
, então, aqui está algo semelhante que você pode tentar se o acima não funcionar: 1)/usr/bin/python -m pip install jsonpath-rw
2)cat ~/trash/file.json | /usr/bin/python -c "from jsonpath_rw import jsonpath, parse; import sys,json; jsonpath_expr = parse('store.book[0]'); out = [match.value for match in jsonpath_expr.find(json.load(sys.stdin))]; print out;"
(usei o caminho completo para o binário python porque estava tendo alguns problemas com vários pythons instalado).Se você tem php :
Por exemplo:
temos um recurso que fornece ao json códigos iso de países: http://country.io/iso3.json e podemos vê-lo facilmente em um shell com curl:
mas parece não muito conveniente e não legível, melhor analise json e veja a estrutura legível:
Este código imprimirá algo como:
se você tiver matrizes aninhadas, essa saída ficará muito melhor ...
Espero que isso seja útil ...
fonte
Também existe uma ferramenta de processamento JSON CLI muito simples, mas poderosa, fx - https://github.com/antonmedv/fx
Exemplos
Use a função anônima:
Se você não passar a função anônima param => ..., o código será automaticamente transformado em função anônima. E você pode obter acesso ao JSON por esta palavra-chave:
Ou apenas use a sintaxe do ponto:
Você pode transmitir qualquer número de funções anônimas para reduzir o JSON:
Você pode atualizar o JSON existente usando o operador spread:
Apenas JavaScript simples . Não precisa aprender nova sintaxe.
ATUALIZAÇÃO 10/11/2018
fx
agora tem o modo interativo ( ! )https://github.com/antonmedv/fx
fonte
Este é mais um
bash
epython
resposta híbrido. Postei esta resposta porque queria processar uma saída JSON mais complexa, mas reduzindo a complexidade do meu aplicativo bash. Quero abrir o seguinte objeto JSON em http://www.arcgis.com/sharing/rest/info?f=json embash
:No exemplo a seguir, criei minha própria implementação
jq
eunquote
alavancagempython
. Você notará que, depois de importar o objeto python dejson
para um dicionário python, podemos usar a sintaxe python para navegar no dicionário. Para navegar acima, a sintaxe é:data
data[ "authInfo" ]
data[ "authInfo" ][ "tokenServicesUrl" ]
Usando magia no bash, omitimos
data
e fornecemos apenas o texto python à direita dos dados, ou seja,jq
jq '[ "authInfo" ]'
jq '[ "authInfo" ][ "tokenServicesUrl" ]'
Observe que, sem parâmetros,
jq
atua como um pré-identificador JSON. Com os parâmetros, podemos usar a sintaxe python para extrair o que quisermos do dicionário, incluindo a navegação em subdicionários e elementos de matriz.Aqui está um exemplo de trabalho que demonstra o acima:
fonte
Eu fiz isso, "analisando" uma resposta json para um valor específico, da seguinte maneira:
Claramente, $ url aqui seria o Twitter, e $ var seria "texto" para obter a resposta para esse var.
Realmente, acho que a única coisa que deixei de fora do OP é grep para a linha com a variável específica que ele procura. Awk pega o segundo item da linha e, com sed, retiro as aspas.
Alguém mais esperto do que eu provavelmente poderia pensar com awk ou grep.
Agora, você pode fazer tudo com apenas sed:
assim, sem awk, sem grep ... Não sei por que não pensei nisso antes. Hummm ...
fonte
grep | awk | sed
esed | sed | sed
são antipadrões de desperdício. Seu último exemplo pode ser facilmente reescrito,curl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'
mas, como outros já apontaram, é uma abordagem propensa a erros e quebradiça, que não deve ser recomendada em primeiro lugar.A análise do JSON é dolorosa em um script de shell. Com uma linguagem mais apropriada, crie uma ferramenta que extraia atributos JSON de maneira consistente com as convenções de script de shell. Você pode usar sua nova ferramenta para resolver o problema imediato de script de shell e adicioná-lo ao seu kit para situações futuras.
Por exemplo, considere uma ferramenta jsonlookup tal que, se eu disser
jsonlookup access token id
, retornará o ID do atributo definido no token de atributo definido no acesso ao atributo do stdin, que é presumivelmente dados JSON. Se o atributo não existir, a ferramenta não retornará nada (status de saída 1). Se a análise falhar, saia do status 2 e de uma mensagem para stderr. Se a pesquisa for bem-sucedida, a ferramenta imprimirá o valor do atributo.Após criar uma ferramenta unix com a finalidade precisa de extrair valores JSON, você pode usá-la facilmente em scripts de shell:
Qualquer idioma serve para a implementação do jsonlookup . Aqui está uma versão python bastante concisa:
fonte
Uma linha dupla que usa python. Funciona particularmente bem se você estiver gravando um único arquivo .sh e não quiser depender de outro arquivo .py. Ele também aproveita o uso de tubos
|
.echo "{\"field\": \"value\"}"
pode ser substituído por qualquer coisa que imprima um json no stdout.fonte
Esta é uma boa base de dados para pythonpy :
fonte