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