Estoy tratando de analizar JSON devuelto por 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 el -v k=text
)?
grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json
. Esto resuelve la tarea fácilmente y solo con grep y funciona perfectamente para JSON simples. Para los JSON complejos, debe usar un analizador adecuado.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
:También puede hacer esto con herramientas que probablemente ya estén instaladas en su sistema, como Python que usa el
json
módulo , y así evitar cualquier dependencia adicional, mientras sigue teniendo el beneficio de un analizador JSON adecuado. Lo siguiente asume que desea usar UTF-8, en el que debe codificarse el JSON original y es lo que usan también los terminales más modernos:Python 3:
Python 2:
Notas históricas
Esta respuesta originalmente recomendaba jsawk , que aún debería funcionar, pero es un poco más engorroso de usar que
jq
, y depende de la instalación de un intérprete de JavaScript independiente que es menos común que un intérprete de Python, por lo que las respuestas anteriores probablemente sean preferibles:Esta respuesta también usó originalmente la API de Twitter de la pregunta, pero esa API ya no funciona, lo que dificulta la copia de los ejemplos para probar, y la nueva API de Twitter requiere claves de API, así que he cambiado a usar la API de GitHub que se puede usar fácilmente sin claves API. La primera respuesta para la pregunta original sería:
fuente
print
instrucción siempre codificará a ASCII porque está utilizando Python en una tubería. InsertePYTHONIOENCODING=<desired codec>
en el comando para establecer una codificación de salida diferente, adecuada para su terminal. En Python 3, el valor predeterminado es UTF-8 en este caso (usando laprint()
función ).curl -s
es equivalente acurl --silent
, mientras quejq -r
significa,jq --raw-output
es decir, sin comillas.Para extraer rápidamente los valores de una clave en particular, personalmente me gusta usar "grep -o", que solo devuelve la coincidencia de expresiones regulares. Por ejemplo, para obtener el campo "texto" de los tweets, algo como:
Esta expresión regular es más robusta de lo que piensas; por ejemplo, trata bien con cadenas que tienen comas incrustadas y comillas escapadas dentro de ellas. Creo que con un poco más de trabajo podría hacer uno que realmente garantice extraer el valor, si es atómico. (Si tiene anidamiento, entonces una expresión regular no puede hacerlo, por supuesto).
Y más limpia (pero manteniendo el escape original de la cadena) se puede usar algo como:
| perl -pe 's/"text"://; s/^"//; s/",$//'
. (Hice esto para este análisis ).Para todos los que odian que insisten en que debes usar un analizador JSON real, sí, eso es esencial para la corrección, pero
grep -o
es un orden de magnitud más rápido que lajson
biblioteca estándar de Python , al menos al hacer esto para tweets (que son ~ 2 KB cada uno). No estoy seguro de si esto es solo porquejson
es lento (debería compararme con 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 que debe admitir la recursividad, y en este caso, gasta muchos árboles de CPU para estructuras que no le importan. (Si alguien escribiera un transductor de estado finito que hiciera un análisis JSON adecuado (limitado en profundidad), ¡sería fantástico! Mientras tanto tenemos "grep -o").Para escribir código mantenible, siempre uso una biblioteca de análisis real. No he probado jsawk , pero si funciona bien, eso abordaría el punto # 1.
Una última solución, más alocada: escribí un script que usa Python
json
y extrae las claves que desea, en columnas separadas por tabuladores; luego canalizo a través de un contenedorawk
que permite el acceso con nombre a las columnas. Aquí: los scripts json2tsv y tsvawk . Entonces para este ejemplo sería:Este enfoque no aborda el n. ° 2, es más ineficiente que un solo script de Python, y es un poco frágil: obliga a la normalización de nuevas líneas y pestañas en los valores de cadena, para jugar bien con el campo de awk / vista delimitada por registros del mundo. Pero te permite permanecer en la línea de comando, con más exactitud que
grep -o
.fuente
grep -Po '"text":(\d*?,|.*?[^\\]",)'
jq .name
funciona en la línea de comandos y no requiere "abrir un editor para escribir un script". 2. No importa qué tan rápido su expresión regular pueda producir resultados incorrectos| grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
-P
falta la opción. Probé en OSX 10.11.5 ygrep --version
fuegrep (BSD grep) 2.5.1-FreeBSD
. Lo conseguí trabajando con la opción "regex extendido" en OSX. El comando desde arriba seríagrep -Eo '"text":.*?[^\\]",' tweets.json
.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 una línea para obtener un valor único de algunos datos JSON. Se supone que está canalizando los datos (desde algún lugar) y, por lo tanto, debería ser útil en un contexto de secuencias de comandos.
fuente
pythonpy
( github.com/russell91/pythonpy es casi siempre una mejor alternativa parapython -c
, aunque tiene que instalarse con pip. simplemente conecte el json apy --ji -x 'x[0]["hostname"]'
. Si no desea utilizar el soporte integrado json_input, aún puede obtener los importan automáticamente comopy 'json.loads(sys.stdin)[0]["hostname"]'
jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }
para poder escribir:curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'
y más cosas similares de miedo ... Por cierto,obj[0]
parece innecesario, parece queobj
funciona bien en los casos predeterminados (?).jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
obj[0]
provoca un error al analizar{ "port":5555 }
. Funciona bien después de quitar[0]
.Siguiendo el ejemplo de MartinR y Boecko:
Eso le dará una salida extremadamente amigable con grep. Muy conveniente:
fuente
| grep field
. ¡Gracias!jq
normalmente no se instala mientras que python sí lo está. Además, una vez que esté en Python, también podría ir todo el camino y analizarloimport json...
Simplemente puede descargar el
jq
binario para su plataforma y ejecutar (chmod +x jq
):Extrae
"name"
atributo del objeto json.jq
La página de inicio dice que es comosed
para los datos JSON.fuente
jq
es una herramienta increíble.curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
Usando Node.js
Si el sistema tiene nodoinstalado, es posible usar las marcas de script
-p
print y-e
evaulate conJSON.parse
para extraer cualquier valor que sea necesario.Un ejemplo simple usando la cadena JSON
{ "foo": "bar" }
y extrayendo el valor de "foo":Debido a que tenemos acceso
cat
y otras utilidades, podemos usar esto para archivos:O cualquier otro formato, como una URL que contiene JSON:
fuente
node -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
Use el soporte JSON de Python en lugar de usar awk!
Algo como esto:
fuente
json.load(sys.stdin)['"key']"
como ejemplo como:curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
.Has preguntado cómo dispararte en el pie y estoy aquí para proporcionarte la munición:
Podrías usar en
tr -d '{}'
lugar desed
. Pero dejarlos completamente fuera parece tener también el efecto deseado.Si desea quitar las comillas externas, canalice el resultado de lo anterior a través de
sed 's/\(^"\|"$\)//g'
Creo que otros han sonado alarmados. Estaré esperando con un teléfono celular para llamar a una ambulancia. Fuego cuando esté listo.
fuente
Usando Bash con Python
Cree una función bash en su archivo .bash_rc
Entonces
Aquí está la misma función, pero con comprobación de errores.
Donde $ # -ne 1 asegura al menos 1 entrada, y -t 0 asegura que estás redirigiendo desde una tubería.
Lo bueno de esta implementación es que puede acceder a valores json anidados y obtener json a cambio. =)
Ejemplo:
Si quieres ser realmente elegante, puedes imprimir los datos:
fuente
curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
sys.stdout.write()
si quieres que funcione con python 2 y 3.getJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
TickTick es un analizador JSON escrito en bash (<250 líneas de código)
Aquí está el fragmento del autor de su artículo, Imagine un mundo en el que Bash admite JSON :
fuente
Analizando JSON con PHP CLI
Posiblemente fuera de tema, pero dado que la precedencia reina, esta pregunta permanece incompleta sin una mención de nuestro PHP confiable y fiel, ¿estoy en lo cierto?
Usando el mismo ejemplo JSON pero vamos a asignarlo a una variable para reducir la oscuridad.
Ahora por bondad de PHP, usando file_get_contents y el envoltorio de flujo php: // stdin .
o como se señaló utilizando fgets y la secuencia ya abierta en CLI STDIN constante .
nJoy!
fuente
$argn
lugar defgets(STDIN)
$argn
funciona con el indicador -E o -R y solo si el contenido JSON está en una línea ...Versión Native Bash: también funciona bien con barras invertidas (\) y comillas (")
fuente
Versión que usa Ruby y http://flori.github.com/json/
o más concisamente:
fuente
;
no se requiere en Ruby (solo se usa para concatenar declaraciones que normalmente estarían en líneas separadas en una sola línea).Por desgracia, la respuesta más votadas que utiliza
grep
devuelve el total de los partidos que no funcionaba en mi escenario, pero si se conoce el formato JSON se mantendrá constante puede utilizar de búsqueda hacia atrás y la búsqueda hacia delante para extraer sólo los valores deseados.fuente
Si alguien solo quiere extraer valores de objetos JSON simples sin la necesidad de estructuras anidadas, es posible usar expresiones regulares sin siquiera salir del bash.
Aquí hay una función que definí usando expresiones regulares bash basadas en el estándar JSON :
Advertencias: los objetos y las matrices no se admiten como valor, pero se admiten todos los demás tipos de valores definidos en el estándar. Además, se emparejará un par, sin importar cuán profundo esté en el documento JSON, siempre que tenga exactamente el mismo nombre de clave.
Usando el ejemplo de OP:
fuente
Hay una manera más fácil de obtener una propiedad de una cadena json. Usando un
package.json
archivo como ejemplo, intente esto:Lo estamos usando
process.env
porque esto lleva el contenido del archivo a node.js como una cadena sin riesgo de que el contenido malicioso escape de su cita y sea analizado como código.fuente
require()
que en realidad puede ejecutar código extranjero, JSON.parse no puede.JSON.parse()
y sí, es inequívocamente seguro ... pero aquí, el tiempo de ejecución JSON está recibiendo el contenido (no confiable) en banda con el código (confiable).JSON.parse()
, también está a salvo, pero eso tampoco está sucediendo aquí.JSON.parse()
, en el código . Está asumiendo que poner retrocesos literales mantendrá el contenido literal, pero esa es una suposición completamente insegura, porque pueden existir retrocesos literales en el contenido del archivo (y, por lo tanto, la variable), y así pueden terminar la cita y entrar en un contexto sin comillas donde el Los valores se ejecutan como código.Ahora que Powershell es multiplataforma, pensé en abrirme camino, ya que me parece bastante intuitivo y extremadamente simple.
ConvertFrom-Json convierte el JSON en un objeto personalizado de Powershell, para que pueda trabajar fácilmente con las propiedades desde ese punto en adelante. Si solo quisiera la propiedad 'id' por ejemplo, simplemente haría esto:
Si quisieras invocar todo desde Bash, entonces deberías llamarlo así:
Por supuesto, hay una forma pura de Powershell para hacerlo sin rizo, que sería:
Finalmente, también está 'ConvertTo-Json' que convierte un objeto personalizado a JSON con la misma facilidad. Aquí hay un ejemplo:
Lo que produciría un buen JSON como este:
}
Es cierto que usar un shell de Windows en Unix es algo sacrílego, pero Powershell es realmente bueno en algunas cosas, y analizar JSON y XML son algunas de ellas. Esta es la página de GitHub para la versión multiplataforma https://github.com/PowerShell/PowerShell
fuente
Alguien que también tenga archivos xml, puede que quiera mirar mi Xidel . Es un procesador JSONiq sin dependencia de cli . (es decir, también es compatible con XQuery para el procesamiento xml o json)
El ejemplo en la pregunta sería:
O con mi propia sintaxis de extensión no estándar:
fuente
xidel -s https://api.github.com/users/lambda -e 'name'
(o-e '$json/name'
, o-e '($json).name'
).No puedo usar ninguna de las respuestas aquí. Sin jq disponible, sin matrices de shell, sin declaración, sin grep -P, sin mirar atrás y sin mirar atrás, sin Python, sin Perl, sin Ruby, no, ni siquiera Bash ... Las respuestas restantes simplemente no funcionan bien. JavaScript sonaba familiar, pero la lata dice Nescaffe, por lo que es un no ir también :) Incluso si estuviera disponible, para mi simple necesidad, serían excesivos y lentos.
Sin embargo, es extremadamente importante para mí obtener muchas variables de la respuesta con formato json de mi módem. ¡Lo estoy haciendo en un sh con BusyBox muy recortado en mis enrutadores! No hay problemas con el uso de awk solo: solo configure delimitadores y lea los datos. ¡Para una sola variable, eso es todo!
¿Recuerdas que no tengo matrices? Tuve que asignar dentro de los datos analizados awk a las 11 variables que necesito en un script de shell. Dondequiera que mirara, se decía que era una misión imposible. No hay problema con eso también.
Mi solución es simple. Este código: 1) analizará el archivo .json de la pregunta (en realidad, tomé prestada una muestra de datos de trabajo de la respuesta más votada) y elegirá los datos citados, más 2) creará variables de shell desde el awk asignando un shell con nombre libre nombres de variables
No hay problemas con espacios en blanco. En mi uso, el mismo comando analiza una salida larga de una sola línea. A medida que se utiliza eval, esta solución es adecuada solo para datos confiables. Es sencillo adaptarlo para recoger datos no citados. Para una gran cantidad de variables, la ganancia de velocidad marginal se puede lograr usando else if. La falta de matriz obviamente significa: no hay registros múltiples sin violín adicional. Pero donde hay arreglos disponibles, adaptar esta solución es una tarea simple.
La respuesta de @maikel sed casi funciona (pero no puedo comentarla). Para mis datos bien formateados, funciona. No tanto con el ejemplo utilizado aquí (las comillas faltantes lo descartan). Es complicado y difícil de modificar. Además, no me gusta tener que hacer 11 llamadas para extraer 11 variables. ¿Por qué? Calculé 100 bucles extrayendo 9 variables: ¡la función sed tardó 48,99 segundos y mi solución tardó 0,91 segundos! ¿No es justo? Haciendo una sola extracción de 9 variables: 0.51 vs. 0.02 seg.
fuente
Puedes probar algo como esto:
fuente
Puedes usar
jshon
:fuente
aquí hay una manera de hacerlo con awk
fuente
Para un análisis JSON más complejo, sugiero usar el módulo python jsonpath (por Stefan Goessner):
sudo easy_install -U jsonpath
Ejemplo file.json (de http://goessner.net/articles/JsonPath ) -
Analízalo (extrae todos los títulos de libros con precio <10) -
Saldrá -
NOTA: La línea de comando anterior no incluye verificación de errores. Para obtener una solución completa con comprobación de errores, debe crear un pequeño script de Python y ajustar el código con try-except.
fuente
jsonpath
para instalarlojsonpath_rw
, así que aquí hay algo similar que puedes probar si lo anterior no funciona: 1)/usr/bin/python -m pip install jsonpath-rw
2)cat ~/trash/file.json | /usr/bin/python -c "from jsonpath_rw import jsonpath, parse; import sys,json; jsonpath_expr = parse('store.book[0]'); out = [match.value for match in jsonpath_expr.find(json.load(sys.stdin))]; print out;"
(Utilicé la ruta completa al binario de Python porque estaba teniendo algunos problemas con múltiples pitones instalado).Si tienes php :
Por ejemplo:
tenemos un recurso que proporciona a json códigos iso de países: http://country.io/iso3.json y podemos verlo fácilmente en un shell con curl:
pero no parece muy conveniente, y no es legible, mejor analice json y vea la estructura legible:
Este código imprimirá algo como:
Si tiene matrices anidadas, esta salida se verá mucho mejor ...
Espero que esto sea útil ...
fuente
También hay una muy simple pero potente JSON CLI procesamiento herramienta fx - https://github.com/antonmedv/fx
Ejemplos
Use la función anónima:
Si no pasa la función anónima param => ..., el código se transformará automáticamente en función anónima. Y puede obtener acceso a JSON con esta palabra clave:
O simplemente use la sintaxis de puntos también:
Puede pasar cualquier cantidad de funciones anónimas para reducir JSON:
Puede actualizar JSON existente utilizando el operador de propagación:
Simplemente JavaScript . No es necesario aprender una nueva sintaxis.
ACTUALIZACIÓN 2018-11-06
fx
ahora tiene modo interactivo ( ! )https://github.com/antonmedv/fx
fuente
Esta es otra
bash
ypython
respuesta híbrido. Publiqué esta respuesta porque quería procesar resultados JSON más complejos, pero reduciendo la complejidad de mi aplicación bash. Quiero abrir el siguiente objeto JSON de http://www.arcgis.com/sharing/rest/info?f=json enbash
:En el siguiente ejemplo, creé mi propia implementación
jq
yunquote
apalancamientopython
. Notarás que una vez que importamos el objeto python dejson
un diccionario python, podemos usar la sintaxis de python para navegar por el diccionario. Para navegar por lo anterior, la sintaxis es:data
data[ "authInfo" ]
data[ "authInfo" ][ "tokenServicesUrl" ]
Al usar magic en bash, omitimos
data
y solo suministramos el texto de python a la derecha de los datos, es decirjq
jq '[ "authInfo" ]'
jq '[ "authInfo" ][ "tokenServicesUrl" ]'
Tenga en cuenta que, sin parámetros,
jq
actúa como un prettifier JSON. Con los parámetros podemos usar la sintaxis de Python para extraer todo lo que queramos del diccionario, incluida la navegación de subdiccionarios y elementos de matriz.Aquí hay un ejemplo de trabajo que demuestra lo anterior:
fuente
He hecho esto, "analizando" una respuesta json para un valor particular, de la siguiente manera:
Claramente, $ url aquí sería la url de twitter, y $ var sería "texto" para obtener la respuesta para esa var.
Realmente, creo que lo único que estoy haciendo que el OP ha dejado fuera es grep para la línea con la variable específica que busca. Awk toma el segundo elemento en la línea y con sed elimino las comillas.
Alguien más inteligente que yo probablemente podría pensar todo con awk o grep.
Ahora, puedes hacerlo todo con solo sed:
por lo tanto, no awk, no grep ... No sé por qué no pensé en eso antes. Hmmm ...
fuente
grep | awk | sed
ysed | sed | sed
son antipatrones derrochadores. Su último ejemplo puede reescribirse fácilmente,curl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'
pero como otros han señalado, este es un enfoque propenso a errores y frágil que no debería recomendarse en primer lugar.Analizar JSON es doloroso en un script de shell. Con un lenguaje más apropiado, cree una herramienta que extraiga atributos JSON de una manera consistente con las convenciones de scripting de shell. Puede usar su nueva herramienta para resolver el problema inmediato de scripts de shell y luego agregarlo a su kit para futuras situaciones.
Por ejemplo, considere una herramienta jsonlookup de modo que si digo
jsonlookup access token id
que devolverá el ID de atributo definido dentro del token de atributo definido dentro del acceso de atributo desde stdin, que presumiblemente son datos JSON. Si el atributo no existe, la herramienta no devuelve nada (estado de salida 1). Si falla el análisis, salga del estado 2 y envíe un mensaje a stderr. Si la búsqueda tiene éxito, la herramienta imprime el valor del atributo.Después de haber creado una herramienta Unix con el propósito preciso de extraer valores JSON, puede usarla fácilmente en scripts de shell:
Cualquier lenguaje servirá para la implementación de jsonlookup . Aquí hay una versión de Python bastante concisa:
fuente
Un trazador de líneas que usa python. Funciona particularmente bien si está escribiendo un solo archivo .sh y no desea depender de otro archivo .py. También aprovecha el uso de tubería
|
.echo "{\"field\": \"value\"}"
se puede reemplazar por cualquier cosa que imprima un json en la salida estándar.fuente
Este es un buen caso de uso para pythonpy :
fuente