Regex avec la commande sed pour analyser le texte json


15

J'ai ce texte json:

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

Je veux extraire l'état général du buildStatus, c'est-à-dire que la sortie attendue était "ERREUR"

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

J'ai essayé l'expression sed ci-dessous, mais ça ne marche pas, ça revient OK:

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

Qu'est-ce que je fais mal?

Réponses:


16

N'analysez pas les structures de données imbriquées complexes comme JSON ou XML avec des expressions régulières, utilisez un analyseur JSON approprié, comme jshon.

Vous devez d'abord l'installer:

sudo apt-get install jshon

Ensuite, vous devez lui fournir les données JSON à analyser via une entrée standard, afin que vous puissiez rediriger la sortie d'une autre commande là-bas avec un tube ( |) ou rediriger un fichier vers elle ( < filename).

Les arguments dont il a besoin pour extraire les données souhaitées ressemblent à ceci:

jshon -e "buildStatus" -e "status" -u
  • -e "buildStatus" sélectionne l'élément avec l'index "buildStatus" dans le dictionnaire de niveau supérieur.
  • -e "status" sélectionne l'élément avec l'index "status" dans le dictionnaire de second niveau choisi ci-dessus.
  • -u convertit les données sélectionnées de JSON en données simples (c.-à-d. ici, il supprime les guillemets autour de la chaîne)

Ainsi, la commande que vous exécutez, selon l'endroit où vous obtenez les données, ressemble à l'une de celles-ci:

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

Pour en savoir plus jshon, vous pouvez lire sa page de manuel accessible en ligne ici ou en tapant simplement man jshon.


6
Il y a aussi jq:jq -r .buildStatus.status
muru


@HTNW Je n'ai jamais aimé cette réponse, car "une seule balise ouverte XML" (c'est ce que la question pose) est un langage normal (et vous pourriez en principe construire un analyseur XML complet en utilisant des expressions rationnelles pour faire correspondre les balises, les commentaires, les cdata et en utilisant une simple pile pour gérer le contexte imbriqué). Cependant, le langage régulier le plus «intéressant» en JSON est un littéral de chaîne.
Random832

10

Emploi pour jq:

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

Peut être raccourci à:

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

-r( --raw-output) génère la chaîne sans jsonformatage de chaîne, c'est-à-dire sans guillemets.

Exemple:

% 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

S'il n'est pas déjà installé, installez-le par (disponible dans le référentiel Universe):

sudo apt-get install jq 

8

Comme cela a été mentionné, l'analyse des données structurées complexes est préférable avec une API appropriée. Python a un jsonmodule pour cela, que j'utilise personnellement beaucoup dans mes scripts, et il est assez facile d'extraire les champs souhaités comme vous le souhaitez:

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

Ce qui se passe ici, c'est que nous redirigeons le fichier d'entrée vers stdin de python, et lisons cela avec json.load(). Cela devient un dictionnaire python avec la clé "buildStatus", et il contient un autre dictionnaire python avec la clé "status". Ainsi, nous imprimons simplement la valeur d'une clé dans un dictionnaire stocké dans un autre dictionnaire. Assez simple.

Mis à part la simplicité, un autre avantage est que python et cette API sont tous préinstallés et livrés avec Ubuntu par défaut.


6

Vous pouvez réellement le faire dans sed, mais je vous invite fortement à utiliser un langage plus sophistiqué qui a des outils écrits pour gérer les données JSON. Vous pouvez essayer perl ou python, par exemple.

Maintenant, dans votre exemple simple, tout ce que vous voulez, c'est la première occurrence de "status", vous pouvez donc faire:

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

L'astuce est d'utiliser -npour éviter d'imprimer, puis si la ligne correspond à status( /status/), vous supprimez tout sauf la partie que vous voulez s/.*:\s*"(.*)",/\1/, pimprimez la ligne et quit.


Personnellement, je trouve cette commande grep équivalente beaucoup plus simple:

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

Ou celui-ci:

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

Sérieusement, si vous prévoyez d'analyser des fichiers JSON, n'essayez pas de le faire manuellement. Utilisez un analyseur JSON approprié.


ou celui-ci:grep -m 1 status file.json | tr -cd '[[:alnum:]]:' | cut -f2 -d':'
slowko

1
@ user1876040 vous êtes les bienvenus. N'oubliez pas d'accepter l'une des réponses (je recommande celle de ByteCommander , c'est une meilleure solution) afin que la question puisse être marquée comme ayant été répondue).
terdon

6

Je ne dis pas que vous devriez utiliser sed(je pense que quelqu'un m'a déçu juste pour ne pas avoir écrit de mise en garde obligatoire) mais, si vous devez rechercher quelque chose sur la ligne suivantebuildStatus comme vous semblez essayer dans votre propre tentative, vous devez dire sedà lire la ligne suivante avec la Ncommande

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

Remarques:

  • -n n'imprimez rien avant de le demander
  • -rutiliser ERE (identique à -E)
  • /buildStatus/N trouver ce modèle et lire la ligne suivante aussi
  • s/old/new/remplacer oldparnew
  • .* un nombre quelconque de caractères sur la ligne
  • \n nouvelle ligne
  • : "(.*)",enregistrer tous les caractères apparaissant entre : "et",
  • \1 référence arrière au motif enregistré
  • p imprimer la partie sur laquelle nous avons travaillé

0

Il existe une explication typique de la raison sedpour laquelle des outils de traitement de flux de texte similaires ne sont pas bien équipés pour analyser des données structurées telles que JSON et XML. Je ne l'ai pas sous la main, mais cela existe, et je pense que le fait est que les expressions nécessaires dans toutes les situations, mais probablement le moins, deviennent rapidement très complexes, tandis que les outils alternatifs conçus spécifiquement pour analyser la structure sont plus élégant, lisible et efficace dans la même analyse.

Comme muru l' a fait dans un commentaire , jqdevrait être le bon outil pour le travail. Je peux également en témoigner personnellement étant très excité de le voir remplacer plusieurs fois où j'ai essayé d'analyser les mêmes données sans succès ou sans succès. Il contient même une grande capacité de formatage et de contrôle de la sortie. Je le préfère à jsontoolune raison ou plus que j'oublie actuellement.

Byte Commander semble recommander jshondans une autre réponse . Je n'ai pas utilisé cet outil, mais il me rappelle xmlstarletet sa syntaxe, également avec une présentation personnalisable pour la sortie.


Vous parlez probablement de stackoverflow.com/a/1732454/2072269
muru

3
Pensez à améliorer votre réponse en montrant exemple de la façon dont jsontoolpeut être utilisé pour le cas spécifique de OP
Sergiy Kolodyazhnyy

Lol @muru, correct, c'est l'un des messages tentant de dissuader les utilisations d'analyser XML / JSON avec Regex! Je recommandais plutôt jqque muru et heemayl décrivent qui ont déjà des exemples, et je poste simplement le raisonnement derrière cela: askubuntu.com/a/863948/230721
Pysis

0

Juste un autre outil Json appelé json ( https://github.com/trentm/json )

$ json buildStatus.status < file.json
ERROR

Cette étude de cas est trompeuse: il semble que les outils ne fonctionnent pas. Vous pouvez également utiliser jsonpour changer les fichiers json:

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

ou même...

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

documentation dans: http://trentm.com/json/


s'il n'est pas installé:

  • installer le nœud
  • et sudo npm install -g json
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.