flybonzai
flybonzai

Reputation: 3941

How to insert a line after a match using sed (or awk) in a formatted JSON file?

{
  "id": "a1234567-89ab-cdef-0123-456789abcdef",
  "properties": {
    ...
    "my_id": "c1234567-89ab-cdef-0123-456789abcdef",
    ...
}

Given the above in a file, I want to be able to perform a match (including the 4 leading spaces) on my_id and then append a new line "my_value": "abcd",. The desired output would look like this:

{
  "id": "a1234567-89ab-cdef-0123-456789abcdef",
  "properties": {
    ...
    "my_id": "c1234567-89ab-cdef-0123-456789abcdef",
    "my_value": "abcd",
    ...
}

Using examples online, I'm unable to get the command to work. Here is an example of something I have tried: sed '/.*"my_id".*/a "my_value": "abcd",' test.json, for which I receive the following error: command a expects \ followed by text.

What is the correct way to structure this command?

Upvotes: 2

Views: 771

Answers (5)

potong
potong

Reputation: 58478

This might work for you (GNU sed):

sed '/"my-id".*/p;s//"my-value": "abcd"/' file

Match on "my-id" and print that line, then substitute the additional line.

Upvotes: 1

Ed Morton
Ed Morton

Reputation: 204124

Using any awk:

$ awk -v new='"my_value": "abcd",' '{print} sub(/"my_id":.*/,""){print $0 new}' file
{
  "id": "a1234567-89ab-cdef-0123-456789abcdef",
  "properties": {
    ...
    "my_id": "c1234567-89ab-cdef-0123-456789abcdef",
    "my_value": "abcd",
    ...
}

The above will print the new line using whatever indent the existing "my_id" line has, it doesn't assume/hard-code any indent, e.g. 4 blanks.

I'm using this:

sub(/"my_id":.*/,""){print $0 new}

instead of the briefer:

sub(/"my_id":.*/,new)

so it won't break if the new string contains any backreference chars such as &.

Upvotes: 5

RavinderSingh13
RavinderSingh13

Reputation: 133650

With your shown samples and attempts please try following GNU awk code. Where newVal is an awk variable having new value in it. Using match function in GNU awk where I have used regex (.*)("my_id": "[^"]*",)(.*) which creates 3 capturing groups and saves values into an array named arr. Then printing values as per requirement.

awk -v newVal='"my_value": "abcd",' -v RS=   '
match($0,/(.*)("my_id": "[^"]*",)(.*)/,arr){
  print arr[1] arr[2] newVal arr[3]
}
' Input_file

Upvotes: 2

Dave Pritlove
Dave Pritlove

Reputation: 2687

awk procedure with passed argument for insert value

The following awk procedure allows for 'abcd' to be passed as an argument for insertion (allowing it to be set in a bash script if required).

awk -v insertVal="abcd" '/"my_id":/{$0=$0"\n    \"my_value\": \""insertVal"\","} {print}' dat.txt

explanation

The required insertion string ('abcd' in this case) is passed as an argument using the -v variable switch followed by a variable name and value: insertVal="abcd".

The first awk action block has a pattern condition to only act on lines containing the target-line string (in this case "my_id":). When a line with that pattern is found, the line is extended with a new line mark \n, the required four spaces to start the next line, the specified key named "my_value", and the value associated with the key, passed by argument as the variable named insertVal ("abcd"), and the final , character. Note the need to escape the " quotes to render them.

The final awk block, prints the current line (whether or not it was modified).

test

The procedure was tested on Mac Terminal using GNU Awk 5.2.0.

The output generated (from the input data saved to a file named dat.txt) is:

{
  "id": "a1234567-89ab-cdef-0123-456789abcdef",
  "properties": {
    ...
    "my_id": "c1234567-89ab-cdef-0123-456789abcdef",
    "my_value": "abcd",
    ...
}

Upvotes: 2

sseLtaH
sseLtaH

Reputation: 11237

Using sed

$ sed -e '/my_id/{p;s/id.*"/value": "abcd"/' -e '}' input_file
{
  "id": "a1234567-89ab-cdef-0123-456789abcdef",
  "properties": {
    ...
    "my_id": "c1234567-89ab-cdef-0123-456789abcdef",
    "my_value": "abcd",
    ...
}

Upvotes: 3

Related Questions