1

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
5
  • 1
    The problem is, this probably can be considered a "json file", more a like a JavaScript file or so that consists of objects? I mean, otherwise, maybe you can / should use something like jq instead? Commented May 1, 2024 at 20:49
  • yeah, this would be far easier with jq Commented May 1, 2024 at 21:14
  • Actually, this is just a configuration file that defines stuff "like" json objects, updated the question. Commented May 1, 2024 at 21:16
  • 1
    How would jq be helpful here? Commented May 1, 2024 at 22:04
  • Is this a Python dictionary? Commented May 2, 2024 at 6:49

3 Answers 3

2

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",
}
.
.
1

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.

0

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)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.