2

I wrote some script that takes all user data of aws ec2 instance, and echo to local.json. All this happens when I install my node.js modules. I don't know how to delete last comma in the json file. Here is the bash script:

#!/bin/bash
export DATA_DIR=/data
export PATH=$PATH:/usr/local/bin

#install package from git repository
sudo -- sh -c "export PATH=$PATH:/usr/local/bin; export DATA_DIR=/data; npm install git+https://reader:secret@bitbucket.org/somebranch/$1.git#$2"

#update config files from instance user-data
InstanceConfig=`cat /instance-config`
echo '{' >> node_modules/$1/config/local.json
while read line
do
   if [ ! -z "$line" -a "$line" != " " ]; then
      Key=`echo $line | cut -f1 -d=`
      Value=`echo $line | cut -f2 -d=`
      if [ "$Key" = "Env" ]; then
         Env="$Value"
      fi
      printf '"%s" : "%s",\n' "$Key" "$Value" >> node_modules/*/config/local.json
   fi
done <<< "$InstanceConfig"
sed -i '$ s/.$//' node_modules/$1/config/local.json
echo '}' >> node_modules/$1/config/local.json

To run him im doing that way: ./script I get json(OUTPUT), but with comma in all lines. Here is local.json that I get:

{
    "Env" : "dev",
    "EngineUrl" : "engine.url.net",
}

All I trying to do, is delete in last line of the json file - comma(","). I try many ways, that I found in internet. I know that it should be after last "fi"(end of the loop). And I know that it should be something like this line:

sed -i "s?${Key} : ${Value},?${Key} : ${Value}?g" node_modules/$1/config/local.json

Or this:

sed '$ s/,$//' node_modules/$1/config/local.json

But they not work for me. Can someone help me with that? Who knows Bash scripting well? Thanks!

muzafarow
  • 926
  • 3
  • 12
  • 32
  • 4
    What generated the file? It's not valid JSON; you should fix that and regenerate the data. – chepner Apr 24 '16 at 13:29
  • 1
    Please provide some sample input. – Thor Apr 24 '16 at 14:28
  • @chepner It not valid json because of comma. And this is my question. How to delete comma? This shell script install packages in instance and takes varibales from local.json. – muzafarow Apr 24 '16 at 15:07
  • @EdMortonhow it looks now? Thanks! – muzafarow Apr 24 '16 at 17:06
  • 2
    To be clear - we will be throwing away your current shell script completely as it's the wrong approach. The input file you need to post a sample of is the input to your currently posted shell script. – Ed Morton Apr 24 '16 at 17:28
  • 2
    I agree with @EdMorton. Building a JSON by hand is absolutely the wrong way to start. Many languages out there, Perl, Ruby, Python etc, have well written and tested JSON modules, that can build your JSON in couple of lines. Providing the input file may land you with a solution that is robust and scalable. – jaypal singh Apr 24 '16 at 17:30
  • @jaypalsingh agree with you guys, but I'm working with docker and aws instances. So for me simplest way to write Bash script, and use it inside linux machines without installing more other packages and dependencies. Thanks anyway. I found the answer. – muzafarow Apr 24 '16 at 17:35

5 Answers5

6

If you know that it is the last comma that needs to be replaced, a reasonably robust way is to use GNU sed in "slurp" mode like this:

sed -zr 's/,([^,]*$)/\1/' local.json

Output:

{
  "Env" : "dev",
  "EngineUrl" : "engine.url.net"
}
Thor
  • 45,082
  • 11
  • 119
  • 130
  • 1
    @muzafarow: You need to use the GNU version of sed – Thor Apr 24 '16 at 18:53
  • 1
    awesome. i wasted so much time looking at multi-line sed commands trying to do this. – Moika Turns Feb 16 '19 at 00:12
  • This only replaces the very last komma in the file. to replace every last komma before a `}` use `sed -i.bak ':begin;$!N;s/,\n}/\n}/g;tbegin;P;D' FILE` see https://unix.stackexchange.com/a/485010/20661 – rubo77 Jan 15 '21 at 07:58
3

If you'd just post some sample input/output it'd remove the guess-work but IF this is your input file:

$ cat file
Env=dev
EngineUrl=engine.url.net

Then IF you're trying to do what I think you are then all you need is:

$ cat tst.awk
BEGIN { FS="="; sep="{\n" }
{
    printf "%s    \"%s\" : \"%s\"", sep, $1, $2
    sep = ",\n"
}
END { print "\n}" }

which you'd execute as:

$ awk -f tst.awk file
{
    "Env" : "dev",
    "EngineUrl" : "engine.url.net"
}

Or you can execute the awk script inline within a shell script if you prefer:

awk '
BEGIN { FS="="; sep="{\n" }
{
    printf "%s    \"%s\" : \"%s\"", sep, $1, $2
    sep = ",\n"
}
END { print "\n}" }
' file
{
    "Env" : "dev",
    "EngineUrl" : "engine.url.net"
}

The above is far more robust, portable, efficient and better in every other way than the shell script you posted because it's using the right tool for the job. A UNIX shell is an environment from which to call tools with a language to sequence those calls. It is NOT a language to process text which is why it's so difficult to get it right. The UNIX tool for general text processing is awk so when you need to process text in UNIX, you just have shell call awk, that's all.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • 2
    What you asked was an [XY Problem](http://mywiki.wooledge.org/XyProblem) like "whats the best type of hammer to use to paint my house?". In a few years (or sooner if/when it starts failing cryptically depending on the contents of your input file, environment, etc.) you will look at your script and wonder what on earth you were thinking. You asked for help from someone `Who knows Bash scripting well?` and you got an answer from someone who does. You might want to read an intro to shell tutorial or similar, e.g. the book Shell Scripting Recipes by Chris Johnson. Meantime - good luck! – Ed Morton Apr 24 '16 at 19:38
2

Here a jq version if it's available:

jq --raw-input 'split("=") | {(.[0]):.[1]}' /instance-config | jq --slurp 'add'

There might be a way to do it with one jqpass, but I couldn't see it.

Cole Tierney
  • 9,571
  • 1
  • 27
  • 35
0

You an remove all trailing commas from invalid json with:

sed -i.bak ':begin;$!N;s/,\n}/\n}/g;tbegin;P;D' FILE
  • sed -i.bak = creates a backup of the original file, then applies changes to the file

  • ':begin;$!N;s/,\n}/\n}/g;tbegin;P;D' = anything ending with , followed by
    "new line and }". Remove the , on the previous line

  • FILE = the file you want to make the change to

rubo77
  • 19,527
  • 31
  • 134
  • 226
0

If you're willing to use it, is rather forgiving for trailing commas:

xidel -s local.json -e '$json'
{
  "Env": "dev",
  "EngineUrl": "engine.url.net"
}
xidel - -se '$json' <<< '{"Env":"dev","EngineUrl":"engine.url.net",}'
#or
xidel - -se 'parse-json($raw,{"liberal":true()})' <<< '{"Env":"dev","EngineUrl":"engine.url.net",}'
{
  "Env": "dev",
  "EngineUrl": "engine.url.net"
}
Reino
  • 3,203
  • 1
  • 13
  • 21