Regex com comando sed para analisar o texto de json

11

Eu tenho este texto json:

{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

Eu quero extrair o status geral do buildStatus, ou seja, a saída esperada era "ERROR"

"buildStatus" : {
    "status" : "ERROR",
    ....
}

Eu tentei a expressão sed abaixo, mas não está funcionando, ela retorna OK :

status= sed -E 's/.*\"buildStatus\":.*\"status\":\"([^\"]*)\",.*//' jsonfile

O que estou fazendo de errado?

    
por user1876040 23.12.2016 / 14:19

7 respostas

15

Não analise estruturas de dados aninhadas complexas como JSON ou XML com expressões regulares, use um analisador JSON adequado, como jshon .

Primeiro você precisa instalá-lo:

sudo apt-get install jshon

Depois, é necessário fornecer os dados JSON para analisar por meio da entrada padrão, para que você possa redirecionar a saída de outro comando para lá com um canal ( | ) ou redirecionar um arquivo para ele ( < filename ).

Os argumentos necessários para extrair os dados desejados são assim:

jshon -e "buildStatus" -e "status" -u
  • -e "buildStatus" escolhe o elemento com o índice "buildStatus" do dicionário de nível superior.
  • -e "status" escolhe o elemento com o índice "status" do dicionário de segundo nível escolhido acima.
  • -u converte os dados selecionados do JSON para dados simples (isto é, remove as aspas ao redor da string)

Assim, o comando executado, dependendo de onde você obtém os dados, parece um desses:

jshon -e "buildStatus" -e "status" -u < YOUR_INPUT_FILE
YOUR_JSON_PRODUCING_COMMAND | jshon -e "buildStatus" -e "status" -u

Para saber mais sobre jshon , leia a manpage acessível on-line aqui ou simplesmente digitando man jshon .

    
por Byte Commander 23.12.2016 / 14:37
9

Trabalho para jq :

jq -r '.["buildStatus"]["status"]' file.json

Pode ser encurtado para:

jq -r '.buildStatus.status' file.json

-r ( --raw-output ) produz a string sem a formatação de json , ou seja, sem aspas.

Exemplo:

% cat file.json                   
{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

% jq -r '.["buildStatus"]["status"]' file.json
ERROR

% jq -r '.buildStatus.status' file.json       
ERROR

Se ainda não estiver instalado, instale-o por (disponível no repositório Universe):

sudo apt-get install jq 
    
por heemayl 23.12.2016 / 19:37
9

Como foi mencionado, a análise de dados estruturados complexos é preferível com a API apropriada. O Python tem o módulo json para isso, que eu pessoalmente uso bastante nos meus scripts, e é muito fácil extrair os campos desejados da seguinte forma:

$ python -c 'import sys,json;print json.load(sys.stdin)["buildStatus"]["status"]' <  input.txt
ERROR

O que acontece aqui é que redirecionamos o arquivo de entrada para o stdin do python e lemos isso com json.load() . Isso se torna um dicionário python com a chave "buildStatus", e contém outro dicionário python com a chave "status". Assim, estamos meramente imprimindo o valor de uma chave em um dicionário armazenado em outro dicionário. Razoavelmente simples.

Além da simplicidade, outra vantagem é que python e esta API são todos pré-instalados e vêm com o Ubuntu por padrão.

    
por Sergiy Kolodyazhnyy 23.12.2016 / 17:56
6

Você pode fazer isso em sed , mas recomendo enfaticamente que você use uma linguagem mais sofisticada que tenha ferramentas escritas para manipular dados JSON. Você poderia tentar perl ou python, por exemplo.

Agora, no seu exemplo simples, tudo o que você quer é a primeira ocorrência de "status" , então você pode fazer:

$ sed -nE '/status/{s/.*:\s*"(.*)",//p;q}' file.json 
ERROR

O truque é usar -n para evitar a impressão e, se a linha corresponder a status ( /status/ ), você removerá tudo, exceto a parte desejada s/.*:\s*"(.*)",// , p rint da linha e q uit.

Pessoalmente, acho este comando grep equivalente muito mais simples:

$ grep -m1 -oP '"status"\s*:\s*"\K[^"]+' file.json 
ERROR

Ou este aqui:

$ perl -ne 'if(s/.*"status"\s*:\s*"([^"]+).*//){print;exit}' file.json 
ERROR

Falando sério, se você planeja analisar arquivos JSON, não tente fazer isso manualmente. Use um analisador JSON adequado.

    
por terdon 23.12.2016 / 14:35
4

Não estou dizendo que você deveria usar sed (acho que alguém me rebaixou apenas por não ter escrito uma advertência obrigatória), mas, se você precisar procurar por algo no próximo linha para buildStatus como você parece estar tentando em sua própria tentativa, você precisa dizer sed para ler a próxima linha com o comando N

$ sed -rn '/buildStatus/N;s/.*buildStatus.*\n.*: "(.*)",//p' file
ERROR

Notas:

  • -n não imprime nada até pedirmos
  • -r usa ERE (o mesmo que -E )
  • /buildStatus/N encontra este padrão e lê a próxima linha também
  • s/old/new/ replace old com new
  • .* qualquer número de caracteres na linha
  • \n newline
  • : "(.*)", salva os caracteres que ocorrem entre : " e ",
  • de referência para o padrão salvo
  • p imprime a parte em que trabalhamos
por Zanna 23.12.2016 / 15:16
0

Há uma explicação típica de por que sed e ferramentas similares de processamento de fluxo de texto não estão bem equipadas para analisar dados estruturados como JSON e XML. Eu não tenho isso na mão, mas está lá fora, e acredito que as expressões necessárias em todas, mas provavelmente a menor quantidade de situações, rapidamente se tornam muito complexas, enquanto ferramentas alternativas construídas especificamente para analisar a estrutura são mais elegante, legível e eficiente na mesma análise.

Como o muru colocou em um comentário , jq deve ser a ferramenta certa para o trabalho. Eu também posso atestar pessoalmente estar muito animado em vê-lo substituir várias vezes em que tentei analisar os mesmos dados para um sucesso quase nulo ou sobrecarregado. Ele ainda contém uma grande capacidade de formatação e controle da saída. Eu prefiro a jsontool por um motivo ou mais que eu atualmente esqueço.

Byte Commander parece recomendar jshon em outra resposta . Eu não usei essa ferramenta, mas isso me lembra de xmlstarlet e sua sintaxe, também com alguma apresentação personalizável para a saída.

    
por Pysis 23.12.2016 / 16:47
0

Apenas outra ferramenta da Json chamada json ( link )

$ json buildStatus.status < file.json
ERROR

Este estudo de caso é enganoso: parece que as ferramentas não estão funcionando. Você também pode usar json para alterar arquivos json:

$ json -e 'this.buildStatus.status="not error"' < file.json > new.json

ou até mesmo ...

$ json -e 'this.buildStatus.status="no errors"' < file.json | json -e 'this.buildStatus.status
no errors

documentação em: link

se não estiver instalado:

  • nó de instalação
  • e sudo npm install -g json
por JJoao 17.01.2017 / 09:55