json bash edit jq in-place

json - Jq para reemplazar el texto directamente en el archivo(como sed-i)



bash edit (3)

Tengo un archivo json que debe actualizarse en una determinada condición.

Muestra json

{ "Actions" : [ { "value" : "1", "properties" : { "name" : "abc", "age" : "2", "other ": "test1" } }, { "value" : "2", "properties" : { "name" : "def", "age" : "3", "other" : "test2" } } ] }

Estoy escribiendo un script que utiliza Jq para que coincida con un valor y una actualización, como se muestra a continuación

cat sample.json | jq ''.Actions[] | select (.properties.age == "3") .properties.other = "no-test"''

Salida (impresa al terminal)

{ "value": "1", "properties": { "name": "abc", "age": "2", "other ": "test1" } } { "value": "2", "properties": { "name": "def", "age": "3", "other": "no-test" } }

Si bien este comando realiza el cambio necesario, genera el json completo en el terminal y no realiza cambios en el archivo en sí.

Indique si hay una opción para que jq realice cambios en el archivo directamente (similar a sed -i).


Esta publicación aborda la pregunta sobre la ausencia del equivalente de la opción "-i" de sed, y en particular la situación descrita:

Tengo un montón de archivos y escribir cada uno en un archivo separado no sería fácil.

Hay varias opciones, al menos si está trabajando en una Mac o Linux o un entorno similar. Sus ventajas y desventajas se analizan en http://backreference.org/2011/01/29/in-place-editing-of-files/ así que me centraré en solo tres técnicas:

Una es simplemente usar "&&" en la línea de:

jq ... INPUT > INPUT.tmp && mv INPUT.tmp INPUT

Otra es usar la utilidad de sponge (parte de GNU moreutils ):

jq ... INPUT | sponge INPUT

La tercera opción puede ser útil si es ventajoso evitar actualizar un archivo si no hay cambios en él. Aquí hay un script que ilustra dicha función:

#!/bin/bash function maybeupdate { local f="$1" cmp -s "$f" "$f.tmp" if [ $? = 0 ] ; then /bin/rm $f.tmp else /bin/mv "$f.tmp" "$f" fi } for f do jq . "$f" > "$f.tmp" maybeupdate "$f" done


Querrá actualizar los objetos de acción sin cambiar el contexto. Al tener la tubería allí, estás cambiando el contexto a cada acción individual. Puedes controlar eso con algunos paréntesis.

$ jq --arg age "3" / ''(.Actions[] | select(.properties.age == $age).properties.other) = "no-test"'' sample.json

Esto debería producir:

{ "Actions": [ { "value": "1", "properties": { "name": "abc", "age": "2", "other ": "test1" } }, { "value": "2", "properties": { "name": "def", "age": "3", "other": "no-test" } } ] }

Puede redirigir los resultados a un archivo para reemplazar el archivo de entrada. No realizará actualizaciones in situ a un archivo como lo hace sed.


Usando mi respuesta a una pregunta duplicada

La asignación imprime todo el objeto con la asignación ejecutada para que pueda asignar un nuevo valor a .Actions de la matriz de acciones modificada

.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])

Usé una declaración if pero podemos usar su código para hacer lo mismo

.Actions=[.Actions[] | select (.properties.age == "3").properties.other = "no-test"]

Lo anterior generará todo el json con .Actions editadas. jq no tenía la funcionalidad sed -i like, pero todo lo que necesita hacer es volver a colocarlo en una sponge al archivo con | sponge | sponge

jq ''.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])'' sample.json | sponge sample.json