Change only a particular section in a file and ski

2019-09-06 00:46发布

问题:

I want to change only a certain section of a file that looks like this:

{
   "Section_A": {
    "ws/abc-Location01": 24, 
    "ws/abc-Location02": 67,
    "ws/abc-Location03:  101,
   },
   "Section_B": {
    "ws/abc-Location01": 33, 
    "ws/abc-Location02": 59,
    "ws/abc-Location03:  92,
   }
}

I want to change ONLY the numeric values in field 2 in Section B, based on match on the location name with an array, from a shell script.

For example, if ws/abc-Location01 is present in my array in my script, its value should not be changed, else its value should be changed to the value of the 2nd field in file1.txt, where the 1st field contains location name (like ws/abc-Location01)

I'm not sure how to go about this, since a given location name occurs in each section. So how do I tell my script to ignore Section A, Section C and so on, and just change the value in Section B?

回答1:

Using jq:

# list all the keys in Section B into a bash array
keys_str=$(jq --raw-output '.["Section_B"] | keys | @sh' <test.json)
eval "keys_arr=( $keys_str )"

# determine which keys are not present in your other array
# assumes that the other array is associative for sanity
keys_to_change=( )
for key in "${keys_arr[@]}"; do
  [[ ${other_array[$key]} ]] || keys_to_change+=( "$key" )
done

for key in "${keys_to_change[@]}"; do
  # this is the inefficient way to do this, but it's much less code / easier
  # to show; in this case, we're changing everything listed to 0, since you
  # don't specify how you want to change them more precisely.
  jq --arg key "$key" \
    '.["Section_B"][$key] = 0' \
    <test.json >test.json.new \
    && mv test.json.new test.json
done

In general, syntax-aware tools are your best bet here -- just as folks asking similar questions about XML are told to use XMLStarlet or another XPath engine, using a proper JSON parser and generator is the Right Thing for making edits. This can require installing extra dependencies -- but it ensures that you're actually generating syntactically valid content.



回答2:

Assuming your file is proper JSON (it does not seems to be the case in your sample file -- but you confirmed it in a comment), a few lines of your favorite script language should be sufficient.

As of myself, I used Python:

import sys
import json

data = json.load(sys.stdin)
data["Section_B"]["ws/abc-Location02"] = 9999
json.dump(data, sys.stdout,indent=True)

Assuming that file stored as "change.py"

# Original data (proper JSON)
$ cat data.json 
{
   "Section_A": {
    "ws/abc-Location01": 24, 
    "ws/abc-Location02": 67,
    "ws/abc-Location03": 101
   },
   "Section_B": {
    "ws/abc-Location01": 33, 
    "ws/abc-Location02": 59,
    "ws/abc-Location03": 92
   }
}

# Pipe original data to our filter
$ cat data.json | python change.py 
{
 "Section_A": {
  "ws/abc-Location01": 24, 
  "ws/abc-Location02": 67, 
  "ws/abc-Location03": 101
 }, 
 "Section_B": {
  "ws/abc-Location01": 33, 
  "ws/abc-Location02": 9999, 
  "ws/abc-Location03": 92
 }
}

If you want a shell only solution, sed might be sufficient:

cat data.json | sed '/Section_B/,/}/{/Location02/s/:[^,]*/ 9999/}'

This is obviously shorter; but much more fragile too... On the bright side, this will process JSON-like files as well a proper JSON (assuming formated as in your example).



标签: json file shell