Jq per sostituire il testo direttamente sul file (come sed -i)

Ho un file JSON che deve essere aggiornato su una determinata condizione.

Esempio JSON

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

Sto scrivendo uno script che fa uso di Jq per abbinare un valore e un aggiornamento, come mostrato di seguito

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

Uscita (stampata sul terminale)

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

Mentre questo comando apporta le modifiche necessarie, emette l’intero json sul terminale e non modifica il file stesso.

    Si prega di avvisare se esiste un’opzione per fare jq apportare modifiche direttamente al file (simile a sed -i).

    Questo post affronta la domanda sull’assenza dell’equivalente dell’opzione “-i” di sed, e in particolare la situazione descritta:

    Ho un sacco di file e scrivere ognuno in un file separato non sarebbe facile.

    Ci sono diverse opzioni, almeno se stai lavorando in un Mac o Linux o ambiente simile. I loro pro e contro sono discussi su http://backreference.org/2011/01/29/in-place-editing-of-files/ quindi mi concentrerò su tre sole tecniche:

    Uno è semplicemente usare “&&” lungo le linee di:

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

    Un altro è usare l’utility sponge (parte di GNU moreutils ):

     jq ... INPUT | sponge INPUT 

    La terza opzione potrebbe essere utile se è vantaggioso evitare di aggiornare un file se non ci sono modifiche ad esso. Ecco uno script che illustra tale funzione:

     #!/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 

    Dovrai aggiornare gli oggetti azione senza modificare il contesto. Con la pipa lì, stai cambiando il contesto per ogni singola azione. Puoi controllarlo con alcune parentesi.

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

    Questo dovrebbe produrre:

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

    È ansible redirect i risultati in un file per sostituire il file di input. Non farà aggiornamenti sul posto di un file come fa sed.

    Usando la mia risposta a una domanda doppia

    Assignment stampa l’intero object con l’assegnazione eseguita in modo da poter assegnare un nuovo valore a .Actions dell’array Actions modificato

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

    Ho usato un’istruzione if, ma possiamo usare il tuo codice per fare la stessa cosa

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

    Quanto sopra riprodurrà l’intero json con le .Actions modificate. jq non ha la funzionalità di sed -i like, ma tutto quello che devi fare è rimetterlo in una spugna sul file con | sponge | sponge

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