Regex con comando sed para analizar texto json

15

Tengo este texto json:

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

Quiero extraer el estado general de buildStatus, es decir, el resultado esperado era "ERROR"

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

Intenté la expresión sed a continuación, pero no funciona, regresa OK:

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

¿Qué estoy haciendo mal?

usuario1876040
fuente

Respuestas:

16

No analice estructuras de datos anidados complejos como JSON o XML con expresiones regulares, use un analizador JSON adecuado, como jshon.

Primero necesitas instalarlo:

sudo apt-get install jshon

Luego, debe proporcionarle los datos JSON para analizarlos a través de la entrada estándar, para que pueda redirigir la salida de otro comando allí con una tubería ( |) o redirigir un archivo a él ( < filename).

Los argumentos que necesita para extraer los datos que desea se ven así:

jshon -e "buildStatus" -e "status" -u
  • -e "buildStatus" selecciona el elemento con el índice "buildStatus" del diccionario de nivel superior.
  • -e "status" selecciona el elemento con el índice de "estado" del diccionario de segundo nivel seleccionado anteriormente.
  • -u convierte los datos seleccionados de JSON en datos sin formato (es decir, aquí elimina las comillas alrededor de la cadena)

Entonces, el comando que ejecuta, dependiendo de dónde obtiene los datos, se parece a uno de esos:

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

Para obtener más información jshon, puede leer su página de manual accesible en línea aquí o simplemente escribiendo man jshon.

Byte Commander
fuente
66
También hay jq:jq -r .buildStatus.status
muru
@HTNW Nunca me ha gustado esa respuesta, porque la "etiqueta abierta XML única" (que es lo que hace la pregunta) es un lenguaje normal (y, en principio, podría crear un analizador XML completo utilizando expresiones regulares para unir etiquetas, comentarios, cdata secciones, y usando una pila simple para manejar el contexto anidado). Sin embargo, el lenguaje regular más 'interesante' en JSON es un literal de cadena.
Aleatorio832
10

Trabajo para jq:

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

Se puede acortar a:

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

-r( --raw-output) genera la cadena sin jsonformato de cadena, es decir, sin comillas.

Ejemplo:

% 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

Si aún no está instalado, instálelo por (disponible en el repositorio de Universe):

sudo apt-get install jq 
heemayl
fuente
8

Como se ha mencionado, es preferible analizar datos estructurados complejos con la API adecuada. Python tiene un jsonmódulo para eso, que personalmente utilizo mucho en mis scripts, y es bastante fácil extraer los campos deseados que desee de esta manera:

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

Lo que sucede aquí es que redirigimos el archivo de entrada al stdin de Python y lo leemos con json.load(). Eso se convierte en un diccionario de Python con la clave "buildStatus", y contiene otro diccionario de Python con la clave "status". Por lo tanto, simplemente estamos imprimiendo el valor de una clave en un diccionario que está almacenado en otro diccionario. Bastante sencillo.

Además de la simplicidad, otra ventaja es que python y esta API están preinstalados y vienen con Ubuntu de forma predeterminada.

Sergiy Kolodyazhnyy
fuente
6

De hecho, puede hacerlo sed, pero le recomiendo encarecidamente que use un lenguaje más sofisticado que tenga herramientas escritas para manejar datos JSON. Puedes probar perl o python, por ejemplo.

Ahora, en su ejemplo simple, todo lo que desea es la primera aparición de "status", por lo que podría hacer:

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

El truco es usar -npara evitar la impresión, luego, si la línea coincide status( /status/), elimina todo menos la parte que desea s/.*:\s*"(.*)",/\1/, penjuague la línea y quit.


Personalmente, este comando grep equivalente me parece mucho más simple:

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

O este:

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

Sin embargo, si planea analizar archivos JSON, no intente hacerlo manualmente. Use un analizador JSON adecuado.

terdon
fuente
o este:grep -m 1 status file.json | tr -cd '[[:alnum:]]:' | cut -f2 -d':'
slowko
1
@ user1876040 de nada. Recuerde aceptar una de las respuestas (recomiendo ByteCommander , la suya es una mejor solución) para que la pregunta pueda marcarse como respondida).
terdon
6

No digo que debas usar sed(creo que alguien me ha votado negativamente solo por no escribir una advertencia obligatoria) pero, si necesitas buscar algo en la siguiente línea, buildStatusya que parece que estás intentando en tu propio intento, debes decirle sedque lea la siguiente línea con el Ncomando

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

Notas:

  • -n no imprima nada hasta que lo solicitemos
  • -rusar ERE (igual que -E)
  • /buildStatus/N encuentra este patrón y lee la siguiente línea también
  • s/old/new/reemplazar oldconnew
  • .* cualquier número de caracteres en la línea
  • \n nueva línea
  • : "(.*)",guardar los caracteres que ocurran entre : "y",
  • \1 referencia posterior al patrón guardado
  • p imprime la parte en la que trabajamos
Zanna
fuente
0

Hay una explicación típica de por qué sedy herramientas de procesamiento de flujo de texto similares no están bien equipadas para analizar datos estructurados como JSON y XML. No tengo eso a mano, pero está ahí afuera, y creo que el punto es que las expresiones necesarias en todas, pero probablemente en la menor cantidad de situaciones, rápidamente se vuelven muy complejas, mientras que las herramientas alternativas creadas específicamente para analizar la estructura son más elegante, legible y eficiente al mismo análisis.

Como Muru ha puesto en un comentario , jqdebería ser la herramienta adecuada para el trabajo. También puedo dar fe de que personalmente estoy muy emocionado de ver que se reemplaza varias veces en las que he intentado analizar los mismos datos casi sin éxito o con un gran peso. Incluso contiene una gran capacidad para formatear y controlar la salida. Lo prefiero jsontoolpor una razón o más que actualmente olvido.

Byte Commander parece recomendar jshonen otra respuesta . No he usado esa herramienta, pero me recuerda xmlstarlety su sintaxis, también con alguna presentación personalizable para la salida.

Pisis
fuente
Probablemente estés hablando de stackoverflow.com/a/1732454/2072269
muru
3
Considere mejorar su respuesta mostrando un ejemplo de cómo jsontoolse puede usar para el caso específico de OP
Sergiy Kolodyazhnyy,
Lol @muru, correcto, ¡esa es una de las publicaciones que intenta disuadir a los usuarios de analizar XML / JSON con Regex! Estaba recomendando más jqque muru y heemayl describan que ya tienen ejemplos, y solo publiquen el razonamiento detrás de esto: askubuntu.com/a/863948/230721
Pysis
0

Simplemente otra herramienta Json llamada json ( https://github.com/trentm/json )

$ json buildStatus.status < file.json
ERROR

Este estudio de caso es engañoso: parece que las herramientas no funcionan. También puede usar jsonpara cambiar archivos json:

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

o incluso...

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

documentación en: http://trentm.com/json/


si no está instalado:

  • instalar nodo
  • y sudo npm install -g json

fuente