Pregunta Analizando JSON con herramientas Unix


Estoy tratando de analizar JSON devuelto de una solicitud curl, así:

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]}'

Lo anterior divide el JSON en campos, por ejemplo:

% ...
"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
% ...

¿Cómo imprimo un campo específico (indicado por -v k=text)?


532
2017-12-23 21:46


origen


Respuestas:


Hay una serie de herramientas diseñadas específicamente para manipular JSON desde la línea de comandos, y será mucho más fácil y más confiable que hacerlo con Awk, como jq:

curl -s 'https://api.github.com/users/lambda' | jq -r '.name'

También puede hacer esto con herramientas que probablemente ya estén instaladas en su sistema, como Python usando el json módulo, y así evitar cualquier dependencia adicional, mientras que todavía tiene el beneficio de un analizador JSON adecuado. A continuación, suponga que desea utilizar UTF-8, en el que debe codificarse el JSON original y es lo que también utilizan los terminales más modernos:

Python 2:

export PYTHONIOENCODING=utf8
curl -s 'https://api.github.com/users/lambda' | \
    python -c "import sys, json; print json.load(sys.stdin)['name']"

Python 3:

curl -s 'https://api.github.com/users/lambda' | \
    python3 -c "import sys, json; print(json.load(sys.stdin)['name'])"

Notas históricas

Esta respuesta originalmente recomendada jsawk, que aún debería funcionar, pero es un poco más engorroso de usar que jqy depende de que se instale un intérprete autónomo de JavaScript que es menos común que un intérprete de Python, por lo que las respuestas anteriores son probablemente preferibles:

curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name'

Esta respuesta también usó originalmente la API de Twitter a partir de la pregunta, pero esa API ya no funciona, por lo que es difícil copiar los ejemplos para probar, y la nueva API de Twitter requiere claves API, así que he cambiado a usar la API GitHub que se puede usar fácilmente sin claves API. La primera respuesta para la pregunta original sería:

curl 'http://twitter.com/users/username.json' | jq -r '.text'

612
2017-12-23 21:59



Para extraer rápidamente los valores de una clave en particular, personalmente me gusta usar "grep -o", que solo devuelve la coincidencia de la expresión regular. Por ejemplo, para obtener el campo "texto" de los tweets, algo como:

grep -Po '"text":.*?[^\\]",' tweets.json

Esta expresión regular es más sólida de lo que piensas; por ejemplo, funciona bien con cadenas que tienen comas incrustadas y comillas escapadas dentro de ellas. Creo que con un poco más de trabajo podrías hacer uno que esté realmente garantizado para extraer el valor, si es atómico. (Si tiene anidamiento, entonces una expresión regular no puede hacerlo, por supuesto).

Y para seguir limpiando (aunque manteniendo el escape original de la cuerda) puede usar algo como: | perl -pe 's/"text"://; s/^"//; s/",$//'. (Hice esto por este análisis.)

Para todos los enemigos que insisten en que se debe usar un analizador JSON real, sí, eso es esencial para la corrección, pero

  1. Para hacer un análisis realmente rápido, como contar valores para verificar los errores de limpieza de datos o obtener una idea general de los datos, golpear algo en la línea de comandos es más rápido. Abrir un editor para escribir un guión es una distracción.
  2. grep -o es órdenes de magnitud más rápido que el estándar de Python json biblioteca, al menos cuando se hace esto para tweets (que son ~ 2 KB cada uno). No estoy seguro de si esto es solo porque json es lento (debería compararlo con el yajl en algún momento); pero en principio, una expresión regular debería ser más rápida ya que es un estado finito y mucho más optimizable, en lugar de un analizador sintáctico que debe admitir la recursión, y en este caso, gasta muchos CPU construyendo árboles para estructuras que no te importan. (Si alguien escribiera un transductor de estado finito que hiciera un análisis JSON apropiado (con profundidad limitada), ¡sería fantástico! Mientras tanto tenemos "grep -o").

Para escribir código mantenible, siempre uso una biblioteca de análisis real. No lo he intentado jsawk, pero si funciona bien, eso abordaría el punto n. ° 1.

Una última, más extraña, solución: escribí un script que usa Python json y extrae las claves que desea, en columnas separadas por tabuladores; luego canalizo un envoltorio alrededor awk que permite el acceso con nombre a las columnas. Aquí: los guiones json2tsv y tsvawk. Entonces para este ejemplo sería:

json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'

Este enfoque no aborda el # 2, es más ineficiente que un solo script de Python, y es un poco frágil: fuerza la normalización de nuevas líneas y pestañas en valores de cadena, para jugar bien con la vista del mundo de awk / campo delimitado por registros. Pero te permite mantenerte en la línea de comando, con más corrección que grep -o.


219
2017-07-27 23:24



Sobre la base de que algunas de las recomendaciones aquí (especialmente en los comentarios) sugirieron el uso de Python, me decepcionó no encontrar un ejemplo.

Entonces, aquí hay un trazador de líneas para obtener un solo valor de algunos datos JSON. Supone que estás conectando los datos (desde algún lugar) y, por lo tanto, debería ser útil en un contexto de scripting.

echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj[0]["hostname"]'

146
2017-12-06 13:05



Siguiendo el ejemplo de MartinR y Boecko:

$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool

Eso le dará una salida amigable extremadamente grep. Muy conveniente:

$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool | grep my_key

116
2018-06-12 08:04



Podrías simplemente descargar jq binario para su plataforma y correr (chmod +x jq)

$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'

Extrae "name" atributo del objeto json.

jq página principal dice que es como sed para datos JSON.


108
2018-05-30 13:59



Utilizar Soporte JSON de Python en lugar de usar awk!

Algo como esto:

curl -s http://twitter.com/users/username.json | \
    python -c "import json,sys;obj=json.load(sys.stdin);print obj['name'];"

85
2017-12-23 22:28



Usando Node.js

Si el sistema tiene  instalado, es posible usar -p imprimir y -e Evaluar banderas de script con JSON.parse para sacar cualquier valor que se necesite

Un ejemplo simple usando la cadena JSON { "foo": "bar" } y sacando el valor de "foo":

$ node -pe 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
bar

Porque tenemos acceso a cat y otras utilidades, podemos usar esto para los archivos:

$ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)"
bar

O cualquier otro formato, como una URL que contenga JSON:

$ node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)"
Trevor Senior

74
2017-08-27 15:11



Has preguntado cómo dispararte en el pie y estoy aquí para proporcionarte la munición:

curl -s 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^text/ {print $2}'

Podrías usar tr -d '{}' en lugar de sed. Pero dejarlos afuera parece tener también el efecto deseado.

Si desea quitar las cotizaciones externas, canalice el resultado de lo anterior a través de sed 's/\(^"\|"$\)//g'

Creo que otros han sonado suficiente alarma. Estaré esperando con un teléfono celular para llamar a una ambulancia. Fuego cuando esté listo.


42
2017-12-24 00:08



Usando Bash con Python

Crea una función bash en tu archivo .bash_rc

function getJsonVal () { 
    python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))"; 
}

Entonces

$ curl 'http://twitter.com/users/username.json' | getJsonVal "['text']"
My status
$ 

Aquí está la misma función, pero con la comprobación de errores.

function getJsonVal() {
   if [ \( $# -ne 1 \) -o \( -t 0 \) ]; then
       cat <<EOF
Usage: getJsonVal 'key' < /tmp/
 -- or -- 
 cat /tmp/input | getJsonVal 'key'
EOF
       return;
   fi;
   python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))";
}

Donde $ # -ne 1 asegura al menos 1 entrada, y -t 0 asegúrese de que está redirigiendo desde un conducto.

Lo bueno de esta implementación es que puedes acceder a los valores json anidados y obtener json a cambio. =)

Ejemplo:

$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' |  getJsonVal "['foo']['a'][1]"
2

Si quieres ser realmente elegante, podrías imprimir bastante los datos:

function getJsonVal () { 
    python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1, sort_keys=True, indent=4))"; 
}

$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' |  getJsonVal "['foo']"
{
    "a": [
        1, 
        2, 
        3
    ], 
    "bar": "baz"
}

36
2018-02-06 05:53



Analizando JSON con PHP CLI

Posiblemente fuera de tema, pero desde que reina la precedencia, esta pregunta permanece incompleta sin una mención de nuestro confiable y fiel PHP, ¿estoy en lo cierto?

Usando el mismo ejemplo de JSON pero permitiéndole asignarlo a una variable para reducir la oscuridad.

$ export JSON='{"hostname":"test","domainname":"example.com"}'

Ahora para la bondad PHP, usando file_get_contents y el php: // stdin envoltura de la corriente.

$ echo $JSON|php -r 'echo json_decode(file_get_contents("php://stdin"))->hostname;'

o como se señaló usando Fgets y el flujo ya abierto en la constante CLI STDIN.

$ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;'

nJoy!


19
2018-01-23 21:35