How can I add a property in the end of the object (text manipulation)

I’m doing some text manipulation and I want to add a property (at the end) to an object named "object" that is inside a configuration file

e.g

.
.
object = {
   "one": "one",
   "two": "two",
}
.
.

Should become

.
.
object = {
   "one": "one",
   "two": "two",
   "three": "three",
}
.
.

This is my attempt, but i’m wondering if there is a cleaner way of doing things.

#find line for last property   
line=$(grep -n "$(awk "/object/{f=1;next} /}/{f=0} f" file.txt | tail -n 1)" file.txt | awk -F : '{ print $1 }')
content='"three" = "three"'
awk 'NR=='$((line + 1))' {print "t '"$content"'"} 1' file.txt > file.temp && mv file.temp file.txt
Asked By: kyles

||

Using any awk:

$ content='"three" = "three"'
$ awk -v content="$content" '
    /^object = {/ { f=1 }
    f && /^}/ { print "   " content; f=0 }
    { print }
' file
.
.
object = {
   "one": "one",
   "two": "two",
   "three": "three",
}
.
.
Answered By: Ed Morton

Kinda cumbersome (as most Perl code, and by the moment I realized I was too invested to scrap the thing anyways), but it should be a fair attempt at parsing and modifying the right hand side of the option assignments as pseudo-JSONs in a proper manner, using Perl’s JSON module (on Debian-based distros you could install the module via sudo apt install libjson-perl, or, of course, on any distro, you could install the module via CPAN).

The code is a bit terse at times, I’ll just give a rough idea of what’s going on.

perl -MJSON -pse '
    BEGIN {$/ = "}"}
    s
    /^(([n]+)?object = )(.*)
    /$1 . to_json({ %{ from_json($3, {relaxed => 1}) }, split(/:/, $var) }, {pretty => 1})
    /se
' <input -- -var='foo:bar'

Just the Perl code, formatted in a more human-readable manner, with syntax highlighting:

BEGIN {
    $/ = "}"
}

s
/^(([n]+)?object = )(.*)
/$1 . to_json(
    {
        %{ from_json($3, {relaxed => 1}) },
        split(/:/, $var)
    },
    {pretty => 1}
)
/se

This will add a "foo":"bar" key / value pair to any valid JSON found on the right hand side of an option assignment found in the input file:

~ % cat input 
object = {
    "one": "one",
    "two": "two",
}
object = {
    "three": "three",
    "four": "four",
}
~ % perl -MJSON -pse '
        BEGIN {$/ = "}"}
        s
        /^(([n]+)?object = )(.*)/
        $1 . to_json({ %{ from_json($3, {relaxed => 1}) }, split(/:/, $var) }, {pretty => 1})
        /se
' <input -- -var='foo:bar'
object = {
    "one" : "one",
    "two" : "two",
    "foo" : "bar"
}

object = {
    "four" : "four",
    "three" : "three",
    "foo" : "bar"
}

I won’t break this down line by line, but the logic is:

  • Read in a a string foo:bar (which represents the key / value pair to add) and store it into a variable var
  • Read the file using an input record separator of }, so to split the file into multiple records delimited by the end of each JSON object
  • If a record matches an optional sequence of newlines followed by option = , treat the rest of the record as a valid "relaxed" JSON object (relaxed as in: commas are allowed after the last element of an object / array, which isn’t strictly valid JSON)
  • Deserialize the JSON string into a Perl hash, and add the key / value pair resulting from the splitting of var on :
  • Serialize the hash back into a JSON string, prettifying the output

Caveats:

  • If a supplied key already exists in the pseudo-JSON object, its value will be overridden by the supplied value;
  • The order of the key / value pairs in the input file won’t necessarily be preserved;
  • Likewise, the added key / value pair won’t necessarily end up being the last in the list;
  • Despite accepting pseudo-valid JSON objects as the JSON objects to process, this will output only valid JSON objects (i.e. the last key / value pair won’t include a trailing comma)
Answered By: kos

If it’s to insert "three": "three", before the first } line that follows a object = { line, then that could be:

sed '
/^object = {$/,/^}$/{
  /^}$/i
   "three": "three",
}'

Or same with awk:

awk '$0 == "object = {", $0 == "}" {
       if ($0 == "}") print "   "three": "three""
     }
     {print}'

Or:

awk '$0 == "object = {", $0 == "}" && $0 = "   "three": "three"n" $0 {};1'

Relying on the fact that that $0 = ... assignment will always return true as guaranteed to be neither the empty string nor any representation of zero.

Answered By: Stéphane Chazelas
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.