Regex con il comando sed per analizzare il testo JSON


15

Ho questo testo JSON:

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

Voglio estrarre lo stato generale di buildStatus, ovvero l'output previsto era "ERRORE"

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

Ho provato l'espressione sed di seguito, ma non funziona, restituisce OK:

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

Che cosa sto facendo di sbagliato?

Risposte:


16

Non analizzare strutture di dati nidificati complessi come JSON o XML con espressioni regolari, utilizzare un parser JSON appropriato, come jshon.

Per prima cosa devi installarlo:

sudo apt-get install jshon

Quindi devi fornire i dati JSON per l'analisi tramite l'input standard, quindi puoi reindirizzare l'output di un altro comando lì con una pipe (| ) o reindirizzare un file su di esso ( < filename).

Gli argomenti di cui ha bisogno per estrarre i dati desiderati sono i seguenti:

jshon -e "buildStatus" -e "status" -u
  • -e "buildStatus" seleziona l'elemento con l'indice "buildStatus" dal dizionario di livello superiore.
  • -e "status" seleziona l'elemento con l'indice "status" dal dizionario di secondo livello selezionato sopra.
  • -u converte i dati selezionati da JSON in dati semplici (ovvero qui rimuove le virgolette attorno alla stringa)

Quindi il comando che esegui, a seconda della provenienza dei dati, appare come uno di quelli:

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

Per saperne di più jshon, puoi leggere la sua manpage accessibile online qui o semplicemente digitando man jshon.


6
C'è anche jq:jq -r .buildStatus.status
Muru il


@HTNW Non mi è mai piaciuta quella risposta, perché il "singolo tag aperto XML" (che è quello che fa la domanda) è un linguaggio normale (e in linea di principio potresti costruire un parser XML completo usando regex per abbinare tag, commenti, cdata sezioni e l'utilizzo di un semplice stack per gestire il contesto nidificato). Tuttavia, la lingua normale più "interessante" in JSON è una stringa letterale.
Casuale 832

10

Lavoro per jq:

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

Può essere abbreviato in:

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

-r( --raw-output) genera la stringa senza jsonformattazione della stringa, cioè senza virgolette.

Esempio:

% 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 non è già installato, installarlo da (disponibile nel repository Universe):

sudo apt-get install jq 

8

Come è stato menzionato, l'analisi di dati strutturati complessi è preferibile con l'API appropriata. Python ha un jsonmodulo per questo, che personalmente uso parecchio nei miei script, ed è abbastanza facile estrarre i campi desiderati che vuoi così:

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

Quello che succede qui è che reindirizziamo il file di input allo stdin di Python e lo leggiamo con json.load() . Questo diventa un dizionario Python con chiave "buildStatus" e contiene un altro dizionario Python con chiave "status". Pertanto, stiamo semplicemente stampando il valore di una chiave in un dizionario memorizzato in un altro dizionario. Abbastanza semplice.

A parte la semplicità, un altro vantaggio è che Python e questa API sono preinstallati e vengono forniti con Ubuntu per impostazione predefinita.


6

È possibile effettivamente fare questo insed , ma esorto vivamente di utilizzare un linguaggio più sofisticato che dispone di strumenti scritti per gestire i dati JSON. Potresti provare perl o python, per esempio.

Ora, nel tuo semplice esempio, tutto ciò che vuoi è la prima occorrenza di "status", quindi puoi fare:

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

Il trucco è usare -nper evitare la stampa, quindi se la linea corrisponde status( /status/), rimuovi tutto tranne la parte che desideri s/.*:\s*"(.*)",/\1/, pstampa la linea e qprocedi.


Personalmente, trovo questo comando grep equivalente molto più semplice:

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

O questo:

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

Scherzi a parte, se prevedi di analizzare i file JSON, non provare a farlo manualmente. Utilizzare un parser JSON appropriato.


o questo:grep -m 1 status file.json | tr -cd '[[:alnum:]]:' | cut -f2 -d':'
slowko il

1
@utente1876040, prego. Ricorda di accettare una delle risposte (consiglio ByteCommander's , la sua è una soluzione migliore) in modo che la domanda possa essere contrassegnata come risposta).
terdon,

6

Non dire che dovresti usare sed(penso che qualcuno mi abbia declassato solo per non aver scritto avvertimenti obbligatori) ma, se hai bisogno di cercare qualcosa nella riga successiva a buildStatuscome sembri provare nel tuo tentativo, devi dire seddi leggere la riga successiva con il Ncomando

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

Appunti:

  • -n non stampare nulla fino a quando non lo chiediamo
  • -rusa ERE (uguale a -E)
  • /buildStatus/N trova questo schema e leggi anche la riga successiva
  • s/old/new/sostituire oldconnew
  • .* qualsiasi numero di caratteri sulla linea
  • \n nuova linea
  • : "(.*)",salva tutti i caratteri che si verificano tra : "e",
  • \1 riferimento indietro al modello salvato
  • p stampa la parte su cui abbiamo lavorato

0

C'è una spiegazione tipica del perché sed e simili strumenti di elaborazione del flusso di testo non sono ben equipaggiati per analizzare dati strutturati come JSON e XML. Non ce l'ho a portata di mano, ma è là fuori, e credo che il punto sia che le espressioni necessarie in tutte le situazioni, ma probabilmente il minor numero di volte, diventano rapidamente molto complesse, mentre gli strumenti alternativi creati appositamente per analizzare la struttura sono più elegante, leggibile ed efficiente allo stesso tempo.

Come Muru ha inserito un commento , jqdovrebbe essere lo strumento giusto per il lavoro. Posso anche garantire che personalmente è molto entusiasta di vederlo sostituire più volte in cui ho provato ad analizzare gli stessi dati per non avere quasi nulla o gravato sul successo. Contiene anche una grande capacità di formattazione e controllo dell'output. Lo preferisco jsontoolper un motivo o più che al momento dimentico.

Byte Commander sembra raccomandare jshonin un'altra risposta . Non ho usato questo strumento, ma mi ricorda xmlstarlete la sua sintassi, anche con una presentazione personalizzabile per l'output.


Probabilmente stai parlando di stackoverflow.com/a/1732454/2072269
Muru il

3
Prendi in considerazione di migliorare la tua risposta mostrando un esempio di come jsontoolpuò essere utilizzato per il caso specifico di OP
Sergiy Kolodyazhnyy,

Lol @muru, giusto, questo è uno dei post che tentano di dissuadere gli usi dall'analisi di XML / JSON con Regex! Stavo più raccomandando jqche Muru e Heemayl descrivessero che hanno già degli esempi, e sto solo postando il ragionamento che c'è dietro: askubuntu.com/a/863948/230721
Pysis,

0

Solo un altro strumento Json chiamato json ( https://github.com/trentm/json )

$ json buildStatus.status < file.json
ERROR

Questo caso di studio è fuorviante: sembra che gli strumenti non funzionino. È inoltre possibile utilizzare jsonper modificare i file json:

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

o anche...

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

documentazione in: http://trentm.com/json/


se non installato:

  • nodo di installazione
  • e sudo npm install -g json
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.